<?php

declare(strict_types=1);

require_once __DIR__ . '/conversation.php';
require_once __DIR__ . '/chat_store.php';

function chatHistoryKey(array $context): string
{
    $dialogId = trim((string) ($context['dialog_id'] ?? ''));
    if ($dialogId !== '') {
        return 'dialog:' . $dialogId;
    }

    $chatId = trim((string) ($context['chat_id'] ?? ''));
    if ($chatId !== '') {
        return 'chat:' . $chatId;
    }

    return 'default';
}

function getChatHistory(array $context): array
{
    $key = chatHistoryKey($context);
    $chat = chatStoreRead($key);
    $messages = is_array($chat) ? ($chat['messages'] ?? []) : [];

    return is_array($messages) ? $messages : [];
}

function getChatStoredContext(array $context): array
{
    $key = chatHistoryKey($context);
    $chat = chatStoreRead($key);
    $storedContext = is_array($chat) ? ($chat['context'] ?? []) : [];

    return is_array($storedContext) ? $storedContext : [];
}

function appendChatHistory(array $context, string $role, string $content): void
{
    appendChatHistoryMessages($context, [['role' => $role, 'content' => $content]]);
}

function appendChatHistoryMessages(array $context, array $messages): void
{
    $normalized = [];
    foreach ($messages as $message) {
        $content = trim((string) ($message['content'] ?? ''));
        if ($content !== '') $normalized[] = [
            'role' => ($message['role'] ?? '') === 'assistant' ? 'assistant' : 'user',
            'content' => $content,
            'created_at' => date('c'),
        ];
    }
    if ($normalized === []) return;

    $key = chatHistoryKey($context);
    $history = chatStoreUpdate($key, static function (array $history) use (
        $key,
        $context,
        $normalized
    ): array {
        if (!isset($history['chats'][$key]) || !is_array($history['chats'][$key])) {
            $history['chats'][$key] = readChatFallbackState($key) ?: [
                'context' => $context,
                'messages' => [],
            ];
        }

        $history['chats'][$key]['context'] = $context;
        $history['chats'][$key]['updated_at'] = date('c');

        foreach ($normalized as $message) {
            if ($message['role'] === 'user') {
                unset($history['chats'][$key]['passive_no_response_started_at']);
                unset($history['chats'][$key]['followup_sent_at']);
                unset($history['chats'][$key]['followup_for_message_at']);
                unset($history['chats'][$key]['followup_started_at']);
                unset($history['chats'][$key]['followups']);
                unset($history['chats'][$key]['no_response_marked_at']);
                unset($history['chats'][$key]['followups_stopped_at']);
                unset($history['chats'][$key]['followups_stopped_reason']);
            }

            $history['chats'][$key]['messages'][] = $message;
            if ($message['role'] === 'assistant') {
                $history['chats'][$key]['assistant_has_replied'] = true;
                $history['chats'][$key]['last_assistant_message'] = $message['content'];
            }
        }

        $maxMessages = max(2, (int) config('CHAT_HISTORY_MAX_MESSAGES', 20));
        $history['chats'][$key]['messages'] = array_slice(
            (array) $history['chats'][$key]['messages'],
            -$maxMessages
        );

        return $history;
    });
    $chat = $history['chats'][$key];
    unset($history);
    saveChatFollowupOperationalState($key, $chat);
}

function markChatFollowupsStopped(string $chatKey, string $reason): void
{
    $chat = [];
    chatStoreUpdate($chatKey, static function (array $history) use ($chatKey, $reason, &$chat): array {
        $chat = $history['chats'][$chatKey] ?? readChatFallbackState($chatKey);
        if (!is_array($chat) || $chat === []) {
            $chat = [];
            return $history;
        }

        $chat['followups_stopped_at'] = date('c');
        $chat['followups_stopped_reason'] = $reason;
        if (isset($history['chats'][$chatKey])) {
            $history['chats'][$chatKey] = $chat;
        }

        return $history;
    });
    if ($chat === []) {
        return;
    }
    saveChatFollowupOperationalState($chatKey, $chat);
}

function registerPassiveBotChat(array $context): void
{
    $key = chatHistoryKey($context);
    chatStoreUpdate($key, static function (array $history) use ($key, $context): array {
        $chat = $history['chats'][$key] ?? ['context' => $context, 'messages' => []];
        // Imported/service events never reset an existing conversation or reopen stopped timers.
        if (!empty($chat['messages']) || !empty($chat['followups_stopped_at'])) return $history;
        $chat['context'] = array_replace((array) ($chat['context'] ?? []), array_filter($context, static fn($v) => $v !== '' && $v !== null));
        $chat['passive_no_response_started_at'] ??= date('c');
        $chat['updated_at'] = date('c');
        $history['chats'][$key] = $chat;
        return $history;
    });
}

function mergeChatContactData(array $context, array $contact): array
{
    $key = chatHistoryKey($context);
    $stored = [];
    $history = chatStoreUpdate($key, static function (array $history) use (
        $key,
        $context,
        $contact,
        &$stored
    ): array {
        if (!isset($history['chats'][$key]) || !is_array($history['chats'][$key])) {
            $history['chats'][$key] = readChatFallbackState($key) ?: [
                'context' => $context,
                'messages' => [],
            ];
        }

        $stored = (array) ($history['chats'][$key]['contact'] ?? []);
        $stored['phone'] = normalizeCollectedPhone((string) ($stored['phone'] ?? ''));
        $receivedAt = (array) ($history['chats'][$key]['contact_received_at'] ?? []);
        foreach (['phone', 'city', 'full_name', 'interest_group', 'branch'] as $field) {
            $value = trim((string) ($contact[$field] ?? ''));
            if ($field === 'phone') $value = normalizeCollectedPhone($value);
            if ($value !== '') {
                if (trim((string) ($stored[$field] ?? '')) !== $value
                    || trim((string) ($receivedAt[$field] ?? '')) === ''
                ) {
                    $receivedAt[$field] = date('c');
                }
                $stored[$field] = $value;
            }
        }

        $stored += [
            'phone' => '',
            'city' => '',
            'full_name' => '',
            'interest_group' => '',
            'branch' => '',
        ];

        $history['chats'][$key]['contact'] = $stored;
        $history['chats'][$key]['contact_received_at'] = $receivedAt;
        $history['chats'][$key]['context'] = array_filter(
            $context,
            static fn ($value): bool => $value !== null && $value !== ''
        );
        $history['chats'][$key]['updated_at'] = date('c');

        return $history;
    });
    $chat = $history['chats'][$key];
    unset($history);
    saveChatFollowupOperationalState($key, $chat);

    return $stored;
}

function markPhoneOnlyEndOfDayProcessed(string $chatKey, string $dealId, string $phone, array $crmResult): void
{
    chatStoreUpdate($chatKey, static function (array $history) use (
        $chatKey,
        $dealId,
        $phone,
        $crmResult
    ): array {
        if (!isset($history['chats'][$chatKey]) || !is_array($history['chats'][$chatKey])) {
            return $history;
        }

        $history['chats'][$chatKey]['phone_only_end_of_day'] = [
            'processed_at' => date('c'),
            'deal_id' => $dealId,
            'phone' => $phone,
            'result' => $crmResult,
        ];

        return $history;
    });
}

function getChatContactData(array $context): array
{
    $key = chatHistoryKey($context);
    $chat = chatStoreRead($key);
    $contact = is_array($chat) ? (array) ($chat['contact'] ?? []) : [];

    return [
        'phone' => normalizeCollectedPhone((string) ($contact['phone'] ?? '')),
        'city' => trim((string) ($contact['city'] ?? '')),
        'full_name' => trim((string) ($contact['full_name'] ?? '')),
        'interest_group' => normalizeInterestGroup($contact['interest_group'] ?? ''),
        'branch' => normalizeBranchName($contact['branch'] ?? ''),
    ];
}

function getChatConversationState(array $context): array
{
    $key = chatHistoryKey($context);
    $chat = chatStoreRead($key);
    $chat = is_array($chat) ? $chat : [];
    $messages = (array) ($chat['messages'] ?? []);

    $assistantHasReplied = ($chat['assistant_has_replied'] ?? false) === true;
    $lastAssistantMessage = trim((string) ($chat['last_assistant_message'] ?? ''));
    foreach ($messages as $historyMessage) {
        if ((string) ($historyMessage['role'] ?? '') !== 'assistant') {
            continue;
        }

        $assistantHasReplied = true;
        $candidate = trim((string) ($historyMessage['content'] ?? ''));
        if ($candidate !== '') {
            $lastAssistantMessage = $candidate;
        }
    }

    return [
        'assistant_has_replied' => $assistantHasReplied,
        'last_assistant_message' => $lastAssistantMessage,
    ];
}

function normalizeInterestGroup($value): string
{
    $value = trim((string) $value);
    if ($value === '') {
        return '';
    }

    $value = function_exists('mb_strtolower')
        ? mb_strtolower($value, 'UTF-8')
        : strtolower($value);

    return in_array($value, ['техника', 'запчасти', 'сервис', 'другое'], true) ? $value : '';
}

function isContactDataComplete(array $contact): bool
{
    return normalizeCollectedPhone((string) ($contact['phone'] ?? '')) !== ''
        && trim((string) ($contact['city'] ?? '')) !== '';
}

function markChatAwaitingClientReply(array $context): void
{
    $key = chatHistoryKey($context);
    $history = chatStoreUpdate($key, static function (array $history) use ($key, $context): array {
        if (!isset($history['chats'][$key]) || !is_array($history['chats'][$key])) {
            $history['chats'][$key] = readChatFallbackState($key) ?: [
                'context' => $context,
                'messages' => [],
            ];
        }

        $history['chats'][$key]['context'] = array_filter(
            $context,
            static fn ($value): bool => $value !== null && $value !== ''
        );
        if (trim((string) ($history['chats'][$key]['followup_started_at'] ?? '')) === '') {
            $history['chats'][$key]['followup_started_at'] = date('c');
        }

        if (!isset($history['chats'][$key]['followups']) || !is_array($history['chats'][$key]['followups'])) {
            $history['chats'][$key]['followups'] = ['sent' => []];
        }

        return $history;
    });
    $chat = $history['chats'][$key];
    unset($history);
    saveChatFollowupOperationalState($key, $chat);
}

function clearChatAwaitingClientReply(array $context): void
{
    $key = chatHistoryKey($context);
    $chat = [];
    chatStoreUpdate($key, static function (array $history) use ($key, &$chat): array {
        $chat = $history['chats'][$key] ?? readChatFallbackState($key);
        if (!is_array($chat) || $chat === []) {
            $chat = [];
            return $history;
        }

        unset($chat['followup_started_at']);
        unset($chat['followups']);
        unset($chat['followup_sent_at']);
        unset($chat['followup_for_message_at']);
        unset($chat['no_response_marked_at']);

        if (isset($history['chats'][$key])) {
            $history['chats'][$key] = $chat;
        }

        return $history;
    });
    if ($chat === []) {
        return;
    }
    saveChatFollowupOperationalState($key, $chat);
}

function clearChatHistory(?string $dialogId = null, ?string $chatId = null): array
{
    $key = $dialogId === null && $chatId === null ? null
        : chatHistoryKey(['dialog_id' => $dialogId, 'chat_id' => $chatId]);
    chatStoreClear($key);
    return ['cleared' => $key ?? 'all'];
}

function listChatHistories(): array
{
    return ['chats' => iterator_to_array(chatStoreIterate())];
}

function listChatHistoriesForFollowups(): array
{
    return ['chats' => iterator_to_array(chatStoreIterate(true))];
}

function markChatFollowupSent(string $chatKey, int $stageIndex, string $message): void
{
    $chat = [];
    chatStoreUpdate($chatKey, static function (array $history) use (
        $chatKey,
        $stageIndex,
        $message,
        &$chat
    ): array {
        $chat = $history['chats'][$chatKey] ?? readChatFallbackState($chatKey);
        if (!is_array($chat) || $chat === []) {
            $chat = [];
            return $history;
        }

        if (!isset($chat['followups']) || !is_array($chat['followups'])) {
            $chat['followups'] = ['sent' => []];
        }

        $chat['followup_sent_at'] = date('c');
        $chat['followups']['sent'][(string) $stageIndex] = [
            'sent_at' => date('c'),
            'message' => $message,
        ];

        if (isset($history['chats'][$chatKey])) {
            $history['chats'][$chatKey] = $chat;
        }

        return $history;
    });
    if ($chat === []) {
        return;
    }
    saveChatFollowupOperationalState($chatKey, $chat);
}

function markChatNoResponseMarked(string $chatKey, array $crmResult): void
{
    $chat = [];
    chatStoreUpdate($chatKey, static function (array $history) use (
        $chatKey,
        $crmResult,
        &$chat
    ): array {
        $chat = $history['chats'][$chatKey] ?? readChatFallbackState($chatKey);
        if (!is_array($chat) || $chat === []) {
            $chat = [];
            return $history;
        }

        $chat['no_response_marked_at'] = date('c');
        $chat['no_response_crm_result'] = $crmResult;

        if (isset($history['chats'][$chatKey])) {
            $history['chats'][$chatKey] = $chat;
        }

        return $history;
    });
    if ($chat === []) {
        return;
    }
    saveChatFollowupOperationalState($chatKey, $chat);
}

function saveChatFollowupOperationalState(string $chatKey, array $chat): void
{
    if (trim($chatKey) === '') return;
    chatStoreGate(LOCK_SH, static function () use ($chatKey, $chat): void {
        // In split mode the canonical record already contains the complete operational state.
        if (chatStoreIsSplit()) return;
        $file = (string) config('CHAT_HISTORY_FILE');
        withJsonFileLock($file, LOCK_SH, static function () use ($file, $chatKey, $chat): void {
            $history = readJsonFileUnlocked($file, ['chats' => []]);
            writeChatFollowupOperationalSnapshot($chatKey, (array) ($history['chats'][$chatKey] ?? $chat));
        });
    });
}

function writeChatFollowupOperationalSnapshot(string $chatKey, array $chat): void
{

    $snapshot = [
        'context' => (array) ($chat['context'] ?? []),
        'contact' => (array) ($chat['contact'] ?? []),
        'contact_received_at' => (array) ($chat['contact_received_at'] ?? []),
        'messages' => array_values((array) ($chat['messages'] ?? [])),
    ];

    foreach ([
        'updated_at',
        'followup_started_at',
        'passive_no_response_started_at',
        'followup_sent_at',
        'followup_for_message_at',
        'followups',
        'followups_stopped_at',
        'followups_stopped_reason',
        'no_response_marked_at',
        'no_response_crm_result',
        'phone_workflow',
        'phone_only_end_of_day',
        'crm_sync_retry',
        'qualification_distribution_workflow',
        'assistant_has_replied',
        'last_assistant_message',
    ] as $field) {
        if (array_key_exists($field, $chat)) {
            $snapshot[$field] = $chat[$field];
        }
    }

    writeJsonFile(followupOperationalStateFile($chatKey), [
        'chat_key' => $chatKey,
        'saved_at' => date('c'),
        'chat' => $snapshot,
    ]);
}

function recordChatCrmSyncResult(array $context, array $result): void
{
    $key = chatHistoryKey($context);
    $history = chatStoreUpdate($key, static function (array $history) use ($key, $result): array {
        $chat = $history['chats'][$key] ?? readChatFallbackState($key);
        if (!is_array($chat) || $chat === []) return $history;
        if (($result['ok'] ?? false) === true) {
            unset($chat['crm_sync_retry']);
        } else {
            $previous = (array) ($chat['crm_sync_retry'] ?? []);
            $attempts = (int) ($previous['attempts'] ?? 0) + 1;
            $chat['crm_sync_retry'] = [
                'attempts' => $attempts,
                'last_attempt_at' => date('c'),
                'next_attempt_at' => date('c', time() + min(21600, 900 * $attempts)),
                'error' => (string) ($result['error'] ?? 'CRM sync failed'),
            ];
        }
        $history['chats'][$key] = $chat;
        return $history;
    });
    $chat = $history['chats'][$key] ?? null;
    unset($history);
    if (is_array($chat)) saveChatFollowupOperationalState($key, $chat);
}

function readChatFollowupOperationalState(string $chatKey): array
{
    return chatStoreRead($chatKey);
}

function readChatFallbackState(string $chatKey): array
{
    if (chatStoreIsSplit()) return [];
    $stored = readJsonFile(followupOperationalStateFile($chatKey), []);
    return (array) ($stored['chat'] ?? []);
}

function followupOperationalStateFile(string $chatKey): string
{
    return followupOperationalStateDirectory() . '/' . sha1($chatKey) . '.json';
}

function followupOperationalStateDirectory(): string
{
    return dirname((string) config('CHAT_HISTORY_FILE')) . '/followup_states';
}

function hasPhoneWorkflowStarted(array $context, string $phone): bool
{
    $phone = trim($phone);
    if ($phone === '') {
        return false;
    }

    $key = chatHistoryKey($context);
    $chat = chatStoreRead($key);
    $workflow = is_array($chat) ? (array) ($chat['phone_workflow'] ?? []) : [];

    return trim((string) ($workflow['phone'] ?? '')) === $phone
        && trim((string) ($workflow['started_at'] ?? '')) !== '';
}

function markPhoneWorkflowStarted(array $context, string $phone, array $workflowResult): void
{
    $phone = trim($phone);
    if ($phone === '') {
        return;
    }

    $key = chatHistoryKey($context);
    $history = chatStoreUpdate($key, static function (array $history) use (
        $key,
        $context,
        $phone,
        $workflowResult
    ): array {
        if (!isset($history['chats'][$key]) || !is_array($history['chats'][$key])) {
            $history['chats'][$key] = readChatFallbackState($key) ?: [
                'context' => $context,
                'messages' => [],
            ];
        }

        $history['chats'][$key]['phone_workflow'] = [
            'phone' => $phone,
            'started_at' => date('c'),
            'result' => $workflowResult,
        ];

        return $history;
    });
    $chat = $history['chats'][$key];
    unset($history);
    saveChatFollowupOperationalState($key, $chat);
}

function getQualificationWorkflowState(array $context, string $dealId = ''): array
{
    $dealId = trim($dealId);
    if ($dealId !== '') {
        $dealWorkflow = readJsonFile(qualificationWorkflowStateFile($dealId), []);
        if ($dealWorkflow !== []) {
            return (array) $dealWorkflow;
        }
    }

    $key = chatHistoryKey($context);
    $chat = chatStoreRead($key);

    return is_array($chat) ? (array) ($chat['qualification_distribution_workflow'] ?? []) : [];
}

function markQualificationWorkflowPending(
    array $context,
    string $dealId,
    array $workflowResult
): void {
    $key = chatHistoryKey($context);
    $workflowState = [];
    $history = chatStoreUpdate($key, static function (array $history) use (
        $key,
        $context,
        $dealId,
        $workflowResult,
        &$workflowState
    ): array {
        if (!isset($history['chats'][$key]) || !is_array($history['chats'][$key])) {
            $history['chats'][$key] = readChatFallbackState($key) ?: [
                'context' => $context,
                'messages' => [],
            ];
        }

        $previous = (array) ($history['chats'][$key]['qualification_distribution_workflow'] ?? []);
        $workflowState = [
            'status' => 'pending',
            'deal_id' => $dealId,
            'first_failed_at' => (string) ($previous['first_failed_at'] ?? date('c')),
            'last_attempt_at' => date('c'),
            'attempts' => max(0, (int) ($previous['attempts'] ?? 0)) + 1,
            'result' => $workflowResult,
        ];
        $history['chats'][$key]['qualification_distribution_workflow'] = $workflowState;

        return $history;
    });
    $chat = $history['chats'][$key];
    unset($history);
    saveChatFollowupOperationalState($key, $chat);
    saveDealQualificationWorkflowState($dealId, $workflowState);
}

function markQualificationWorkflowStarted(
    array $context,
    string $dealId,
    array $workflowResult
): void {
    $key = chatHistoryKey($context);
    $workflowState = [];
    $history = chatStoreUpdate($key, static function (array $history) use (
        $key,
        $context,
        $dealId,
        $workflowResult,
        &$workflowState
    ): array {
        if (!isset($history['chats'][$key]) || !is_array($history['chats'][$key])) {
            $history['chats'][$key] = readChatFallbackState($key) ?: [
                'context' => $context,
                'messages' => [],
            ];
        }

        $previous = (array) ($history['chats'][$key]['qualification_distribution_workflow'] ?? []);
        $workflowState = [
            'status' => 'started',
            'deal_id' => $dealId,
            'started_at' => date('c'),
            'attempts' => max(0, (int) ($previous['attempts'] ?? 0)) + 1,
            'result' => $workflowResult,
        ];
        $history['chats'][$key]['qualification_distribution_workflow'] = $workflowState;

        return $history;
    });
    $chat = $history['chats'][$key];
    unset($history);
    saveChatFollowupOperationalState($key, $chat);
    saveDealQualificationWorkflowState($dealId, $workflowState);
}

function saveDealQualificationWorkflowState(string $dealId, array $workflowState): void
{
    $dealId = trim($dealId);
    if ($dealId === '') {
        return;
    }

    writeJsonFile(qualificationWorkflowStateFile($dealId), $workflowState);
}

function qualificationWorkflowStateFile(string $dealId): string
{
    $storageDir = dirname((string) config('CHAT_HISTORY_FILE'));

    return $storageDir . '/qualification_workflows/deal_' . trim($dealId) . '.json';
}
