<?php

namespace ZenoChatbot\Services;

use ZenoChatbot\Helper\Settings;
use ZenoChatbot\Model\Conversation;
use ZenoChatbot\Model\Message;
use ZenoChatbot\Model\Meta;
use ZenoChatbot\Services\LLMService\LLMService;
use ZenoChatbot\Support\Hooks;

if (!defined('ABSPATH')) {
    exit;
}

class ConversationLifecycleService
{
    public const SUMMARY_STATUS_IDLE = 'idle';
    public const SUMMARY_STATUS_PENDING = 'pending';
    public const SUMMARY_STATUS_READY = 'ready';
    public const SUMMARY_STATUS_FAILED = 'failed';

    public const SUMMARY_SOURCE = 'conversation_summary';
    public const SUMMARY_SENT_VIA = 'summary';

    public const META_SUMMARY_STATUS = 'conversation_summary_status';
    public const META_SUMMARY_GENERATED_AT = 'conversation_summary_generated_at';
    public const META_SUMMARY_MESSAGE_ID = 'conversation_summary_message_id';
    public const META_SUMMARY_TEXT = 'conversation_summary_text';

    public static function dispatchSummaryRequestAfterResponse(int $conversationId, string $trigger, string $requestedBy): void
    {
        if ($conversationId <= 0) {
            return;
        }

        add_action('shutdown', static function () use ($conversationId, $trigger, $requestedBy) {
            static::requestSummaryGeneration($conversationId, $trigger, $requestedBy);
        }, 1);
    }

    public static function requestSummaryGeneration(int $conversationId, string $trigger, string $requestedBy): void
    {
        if ($conversationId <= 0) {
            return;
        }

        Hooks::doAction(Hooks::CONVERSATION_SUMMARY_REQUESTED, [
            'conversation_id' => $conversationId,
            'trigger' => sanitize_key($trigger),
            'requested_by' => sanitize_key($requestedBy),
            'requested_at' => gmdate('c'),
        ]);
    }

    public static function generateOnCloseSync(int $conversationId, string $trigger): array
    {
        if ($conversationId <= 0) {
            return [
                'success' => false,
                'status' => static::SUMMARY_STATUS_FAILED,
                'error_code' => 'invalid_conversation',
            ];
        }

        $trigger = sanitize_key($trigger);
        if (!in_array($trigger, ['admin_close', 'user_close'], true)) {
            $trigger = 'admin_close';
        }

        $settings = Settings::getChatSettings();
        if ((string) ($settings['auto_summary_enabled'] ?? 'yes') !== 'yes') {
            return [
                'success' => true,
                'status' => static::getSummaryState($conversationId)['status'],
                'skipped' => true,
                'reason' => 'auto_summary_disabled',
            ];
        }

        return static::processSummaryGeneration($conversationId, $trigger, false);
    }

    public static function generateOnDemandSync(int $conversationId, string $mode): array
    {
        if ($conversationId <= 0) {
            return [
                'success' => false,
                'status' => static::SUMMARY_STATUS_FAILED,
                'error_code' => 'invalid_conversation',
            ];
        }

        $mode = sanitize_key($mode);
        if (!in_array($mode, ['retry', 'regenerate'], true)) {
            $mode = 'regenerate';
        }

        return static::processSummaryGeneration($conversationId, $mode, true);
    }

    public static function markSummaryPending(int $conversationId): void
    {
        static::upsertConversationMeta($conversationId, static::META_SUMMARY_STATUS, static::SUMMARY_STATUS_PENDING);
    }

    public static function getSummaryState(int $conversationId): array
    {
        $status = (string) static::getConversationMeta($conversationId, static::META_SUMMARY_STATUS, static::SUMMARY_STATUS_IDLE);
        if (!in_array($status, [static::SUMMARY_STATUS_IDLE, static::SUMMARY_STATUS_PENDING, static::SUMMARY_STATUS_READY, static::SUMMARY_STATUS_FAILED], true)) {
            $status = static::SUMMARY_STATUS_IDLE;
        }

        return [
            'status' => $status,
            'generated_at' => static::getConversationMeta($conversationId, static::META_SUMMARY_GENERATED_AT, null),
            'message_id' => (int) static::getConversationMeta($conversationId, static::META_SUMMARY_MESSAGE_ID, 0),
            'text' => (string) static::getConversationMeta($conversationId, static::META_SUMMARY_TEXT, ''),
        ];
    }

    public static function processSummaryGeneration(int $conversationId, string $trigger, bool $force = false): array
    {
        $conversation = Conversation::query()->find($conversationId);
        if (!$conversation) {
            return [
                'success' => false,
                'status' => static::SUMMARY_STATUS_FAILED,
                'error_code' => 'conversation_not_found',
            ];
        }

        $state = static::getSummaryState($conversationId);
        if (!$force && $state['status'] === static::SUMMARY_STATUS_READY) {
            return [
                'success' => true,
                'status' => static::SUMMARY_STATUS_READY,
                'skipped' => true,
                'summary_message_id' => (int) $state['message_id'],
            ];
        }

        static::markSummaryPending($conversationId);

        $turns = static::buildTranscriptTurns($conversationId);
        if (empty($turns)) {
            static::markSummaryFailed($conversationId);

            return [
                'success' => false,
                'status' => static::SUMMARY_STATUS_FAILED,
                'error_code' => 'empty_transcript',
            ];
        }

        try {
            $summaryResult = LLMService::create()->summarizeConversation($turns);
            $summaryText = sanitize_textarea_field((string) ($summaryResult['text'] ?? ''));

            if ($summaryText === '') {
                static::markSummaryFailed($conversationId);

                return [
                    'success' => false,
                    'status' => static::SUMMARY_STATUS_FAILED,
                    'error_code' => 'empty_summary',
                ];
            }

            $summaryMessageId = static::replaceSummaryMessage((int) $conversation->id, $summaryText);
            static::markSummaryReady((int) $conversation->id, $summaryMessageId, $summaryText);

            Hooks::doAction(Hooks::CONVERSATION_SUMMARY_GENERATED, [
                'conversation_id' => (int) $conversation->id,
                'summary_message_id' => $summaryMessageId,
                'status' => static::SUMMARY_STATUS_READY,
                'generated_at' => gmdate('c'),
                'trigger' => sanitize_key($trigger),
            ]);

            return [
                'success' => true,
                'status' => static::SUMMARY_STATUS_READY,
                'summary_message_id' => $summaryMessageId,
            ];
        } catch (\Throwable $exception) {
            static::markSummaryFailed((int) $conversation->id);

            Hooks::doAction(Hooks::CONVERSATION_SUMMARY_FAILED, [
                'conversation_id' => (int) $conversation->id,
                'status' => static::SUMMARY_STATUS_FAILED,
                'error_code' => static::categorizeSummaryError($exception),
                'trigger' => sanitize_key($trigger),
                'failed_at' => gmdate('c'),
            ]);

            return [
                'success' => false,
                'status' => static::SUMMARY_STATUS_FAILED,
                'error_code' => static::categorizeSummaryError($exception),
            ];
        }
    }

    public static function processAutoCloseBatch(int $limit = 20): array
    {
        $settings = Settings::getChatSettings();
        if ((string) ($settings['auto_close_enabled'] ?? 'no') !== 'yes') {
            return [
                'closed' => 0,
                'scanned' => 0,
            ];
        }

        $hours = (int) ($settings['auto_close_after_hours'] ?? 12);
        if ($hours < 1) {
            $hours = 1;
        }
        if ($hours > 720) {
            $hours = 720;
        }

        $cutoffTimestamp = time() - ($hours * HOUR_IN_SECONDS);

        $openConversations = Conversation::query()
            ->whereNull('ended_at')
            ->where('status', 'open')
            ->orderBy('started_at', 'asc')
            ->limit($limit * 4)
            ->get();

        $closed = 0;
        $scanned = 0;

        foreach ($openConversations as $conversation) {
            if ($closed >= $limit) {
                break;
            }

            $scanned++;
            $lastActivityTs = static::lastActivityTimestamp((int) $conversation->id, (string) $conversation->started_at);
            if ($lastActivityTs > $cutoffTimestamp) {
                continue;
            }

            $conversation->update([
                'ended_at' => current_time('mysql'),
                'status' => 'closed',
            ]);

            $closed++;
            static::requestSummaryGeneration((int) $conversation->id, 'auto_close', 'system');
        }

        return [
            'closed' => $closed,
            'scanned' => $scanned,
        ];
    }

    protected static function buildTranscriptTurns(int $conversationId): array
    {
        $messages = Message::query()
            ->where('conversation_id', $conversationId)
            ->orderBy('created_at', 'asc')
            ->get();

        $turns = [];
        foreach ($messages as $message) {
            if (static::isSummaryMessage($message)) {
                continue;
            }

            $text = trim((string) $message->message);
            if ($text === '') {
                continue;
            }

            $senderType = strtolower((string) ($message->sender_type ?? 'customer'));
            $role = ($senderType === 'ai' || $senderType === 'agent') ? 'assistant' : 'user';
            $turns[] = [
                'role' => $role,
                'message' => $text,
            ];
        }

        return $turns;
    }

    protected static function replaceSummaryMessage(int $conversationId, string $summaryText): int
    {
        $conversation = Conversation::query()->findOrFail($conversationId);

        $oldSummaryMessages = static::getSummaryMessages($conversationId);

        $summaryMessage = $conversation->messages()->create([
            'message' => $summaryText,
            'sent_via' => static::SUMMARY_SENT_VIA,
            'sender_type' => 'ai',
            'sender_id' => null,
            'prompt_tokens' => 0,
            'completion_tokens' => 0,
            'token_usage' => 0,
        ]);

        $summaryMessage->setMeta([
            'timestamp' => current_time('mysql'),
            'source' => static::SUMMARY_SOURCE,
        ]);

        foreach ($oldSummaryMessages as $message) {
            if ((int) $message->id === (int) $summaryMessage->id) {
                continue;
            }

            $message->delete();
        }

        return (int) $summaryMessage->id;
    }

    protected static function getSummaryMessages(int $conversationId): array
    {
        $messages = Message::query()
            ->where('conversation_id', $conversationId)
            ->orderBy('created_at', 'asc')
            ->get();

        $summaryMessages = [];
        foreach ($messages as $message) {
            if (static::isSummaryMessage($message)) {
                $summaryMessages[] = $message;
            }
        }

        return $summaryMessages;
    }

    protected static function isSummaryMessage($message): bool
    {
        if ((string) ($message->sent_via ?? '') === static::SUMMARY_SENT_VIA) {
            return true;
        }

        $meta = $message->getMeta();
        return is_array($meta) && (string) ($meta['source'] ?? '') === static::SUMMARY_SOURCE;
    }

    protected static function lastActivityTimestamp(int $conversationId, string $fallbackStartedAt): int
    {
        $latestMessage = Message::query()
            ->where('conversation_id', $conversationId)
            ->orderBy('created_at', 'desc')
            ->first();

        if ($latestMessage && !empty($latestMessage->created_at)) {
            $timestamp = strtotime((string) $latestMessage->created_at);
            if ($timestamp !== false) {
                return (int) $timestamp;
            }
        }

        $fallbackTimestamp = strtotime($fallbackStartedAt);
        return $fallbackTimestamp === false ? time() : (int) $fallbackTimestamp;
    }

    protected static function markSummaryReady(int $conversationId, int $summaryMessageId, string $summaryText): void
    {
        static::upsertConversationMeta($conversationId, static::META_SUMMARY_STATUS, static::SUMMARY_STATUS_READY);
        static::upsertConversationMeta($conversationId, static::META_SUMMARY_GENERATED_AT, current_time('mysql'));
        static::upsertConversationMeta($conversationId, static::META_SUMMARY_MESSAGE_ID, $summaryMessageId);
        static::upsertConversationMeta($conversationId, static::META_SUMMARY_TEXT, $summaryText);
    }

    protected static function markSummaryFailed(int $conversationId): void
    {
        static::upsertConversationMeta($conversationId, static::META_SUMMARY_STATUS, static::SUMMARY_STATUS_FAILED);
    }

    protected static function getConversationMeta(int $conversationId, string $metaKey, $default = null)
    {
        $meta = Meta::query()
            ->where('entity_type', Conversation::class)
            ->where('entity_id', $conversationId)
            ->where('meta_key', $metaKey)
            ->first();

        if (!$meta) {
            return $default;
        }

        return maybe_unserialize($meta->meta_value);
    }

    protected static function upsertConversationMeta(int $conversationId, string $metaKey, $metaValue): void
    {
        $serialized = maybe_serialize($metaValue);

        $meta = Meta::query()
            ->where('entity_type', Conversation::class)
            ->where('entity_id', $conversationId)
            ->where('meta_key', $metaKey)
            ->first();

        if ($meta) {
            $meta->update([
                'meta_value' => $serialized, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
            ]);

            return;
        }

        Meta::create([
            'entity_type' => Conversation::class,
            'entity_id' => $conversationId,
            'meta_key' => $metaKey, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
            'meta_value' => $serialized, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
        ]);
    }

    protected static function categorizeSummaryError(\Throwable $exception): string
    {
        $message = strtolower($exception->getMessage());

        if (str_contains($message, 'api key') || str_contains($message, 'auth')) {
            return 'auth_error';
        }

        if (str_contains($message, 'timeout')) {
            return 'timeout';
        }

        if (str_contains($message, 'rate')) {
            return 'rate_limit';
        }

        return 'summary_generation_failed';
    }
}
