<?php

declare(strict_types=1);

function handleBitrixBotEvent(array $payload): array
{
    $message = normalizeBitrixBotPayload($payload);

    if ($message['event'] !== '' && !in_array($message['event'], ['ONIMBOTMESSAGEADD', 'ONIMBOTJOINCHAT', 'ONIMBOTDELETE'], true)) {
        return ['ok' => true, 'skipped' => 'Unsupported bot event: ' . $message['event'], 'normalized' => $message];
    }

    if ($message['event'] === 'ONIMBOTDELETE') {
        return ['ok' => true, 'skipped' => 'Bot delete event.', 'normalized' => $message];
    }

    if ($message['message'] === '') {
        return ['ok' => true, 'skipped' => 'Message is empty.', 'normalized' => $message];
    }

    if ($message['author_id'] !== '' && in_array($message['author_id'], configuredBitrixBotIds(), true)) {
        return ['ok' => true, 'skipped' => 'Own bot message.', 'normalized' => $message];
    }

    if (isOwnFollowupText($message['message'])) {
        return ['ok' => true, 'skipped' => 'Own followup message.', 'normalized' => $message];
    }

    if (shouldSkipOpenLineInternalSender($message)) {
        logMessage('info', 'Open line internal sender ignored', [
            'dialog_id' => $message['dialog_id'],
            'chat_id' => $message['chat_id'],
            'open_line_id' => $message['open_line_id'],
            'message_id' => $message['message_id'],
            'author_id' => $message['author_id'],
            'is_connector' => $message['is_connector'],
            'is_bot' => $message['is_bot'],
        ]);

        return ['ok' => true, 'skipped' => 'Open line message is not from an external client.', 'normalized' => $message];
    }

    if ($message['dialog_id'] === '') {
        return ['ok' => false, 'error' => 'DIALOG_ID not found.', 'normalized' => $message];
    }

    $eventId = 'bot:' . ($message['message_id'] ?: sha1(json_encode($payload)));
    if (isEventProcessed($eventId)) {
        return ['ok' => true, 'skipped' => 'Event already processed.', 'event_id' => $eventId];
    }

    markEventProcessed($eventId);
    $dialogLock = null;

    try {
        $dialogLock = acquireBotDialogLock($message['dialog_id']);
        $chatContext = [
            'dialog_id' => $message['dialog_id'],
            'chat_id' => $message['chat_id'],
            'chat_title' => $message['chat_title'],
            'open_line_id' => $message['open_line_id'],
            'deal_id' => $message['deal_id'],
            'bot_id' => $message['bot_id'],
        ];
        $chatContext = enrichAiContextWithDealAdvertisement($chatContext);

        $aiResult = generateAiAnswerResult($message['message'], $chatContext);
        $answer = (string) ($aiResult['answer'] ?? '');
        $contactState = mergeChatContactData($chatContext, (array) ($aiResult['contact'] ?? []));
        appendChatHistory($chatContext, 'user', $message['message']);
        appendChatHistory($chatContext, 'assistant', $answer);

        $replyBotId = (int) ($message['bot_id'] ?: config('BITRIX_AI_BOT_ID', 0));
        $reply = sendBitrixBotMessage($message['dialog_id'], $answer, $replyBotId);
        $crmSync = syncDealFieldsFromContactData($contactState, $chatContext);

        if (isContactDataComplete($contactState)) {
            clearChatAwaitingClientReply($chatContext);
        } else {
            markChatAwaitingClientReply($chatContext);
        }

        logMessage('info', 'Bitrix bot event processed', [
            'dialog_id' => $message['dialog_id'],
            'chat_id' => $message['chat_id'],
            'open_line_id' => $message['open_line_id'],
            'deal_id' => $message['deal_id'],
            'bot_id' => $replyBotId,
            'message_id' => $message['message_id'],
            'reply' => $reply['result'] ?? null,
            'contact_state' => $contactState,
            'crm_sync' => $crmSync,
        ]);

        return [
            'ok' => true,
            'normalized' => $message,
            'message_to_client' => $answer,
            'ai_contact' => $aiResult['contact'] ?? [],
            'contact_state' => $contactState,
            'bitrix_reply' => $reply,
            'crm_sync' => $crmSync,
        ];
    } catch (Throwable $exception) {
        unmarkEventProcessed($eventId);
        throw $exception;
    } finally {
        releaseBotDialogLock($dialogLock);
    }
}

function shouldSkipOpenLineInternalSender(array $message): bool
{
    if (!(bool) config('OPEN_LINE_EXTERNAL_CLIENTS_ONLY', true)) {
        return false;
    }

    if (trim((string) ($message['open_line_id'] ?? '')) === '') {
        return false;
    }

    $isConnector = strtoupper(trim((string) ($message['is_connector'] ?? '')));
    $isBot = strtoupper(trim((string) ($message['is_bot'] ?? '')));

    return $isConnector === 'N' || $isBot === 'Y';
}

function acquireBotDialogLock(string $dialogId)
{
    $lockDir = dirname((string) config('STATE_FILE')) . '/locks';
    if (!is_dir($lockDir) && !mkdir($lockDir, 0775, true) && !is_dir($lockDir)) {
        throw new RuntimeException('Unable to create bot dialog lock directory.');
    }

    $lockPath = $lockDir . '/bot_dialog_' . sha1($dialogId) . '.lock';
    $handle = fopen($lockPath, 'c');
    if ($handle === false) {
        throw new RuntimeException('Unable to open bot dialog lock.');
    }

    if (!flock($handle, LOCK_EX)) {
        fclose($handle);
        throw new RuntimeException('Unable to acquire bot dialog lock.');
    }

    return $handle;
}

function releaseBotDialogLock($handle): void
{
    if (!is_resource($handle)) {
        return;
    }

    flock($handle, LOCK_UN);
    fclose($handle);
}

function isOwnFollowupText(string $message): bool
{
    $message = trim($message);
    if ($message === '') {
        return false;
    }

    $texts = [];
    foreach ([
        'FOLLOWUP_MESSAGE',
        'FOLLOWUP_MESSAGES',
        'FOLLOWUP_MESSAGES_NEED_CITY',
        'FOLLOWUP_MESSAGES_NEED_PHONE',
    ] as $key) {
        $value = config($key, []);
        if (is_array($value)) {
            foreach ($value as $item) {
                $text = trim((string) $item);
                if ($text !== '') {
                    $texts[] = $text;
                }
            }
        } else {
            $text = trim((string) $value);
            if ($text !== '') {
                $texts[] = $text;
            }
        }
    }

    return in_array($message, array_unique($texts), true);
}
