<?php
namespace EasyChat\Controller;

use EasyChat\Library\Logger;
use EasyChat\Library\QuickReplyAiAssistant;
use EasyChat\Model\Knowledge;
use EasyChat\Model\Reply;
use EasyChat\View\HtmxFragmentRenderer;
use EasyChat\View\QuickReplyFragments;

class QuickReply extends Base {

    private const AI_RATE_LIMIT_WINDOW_SECONDS = 600;
    private const AI_RATE_LIMIT_MAX_REQUESTS = 12;

    protected $layouts = 'layout';

    private $quick_reply_fragments;
    private $htmx_fragment_renderer;

    public function __construct($registry) {
        parent::__construct($registry);
        if (!isset(\EasyChat\Library\RequestContext::session()['manager_id'])) {
            header('Location: index.php?route=auth');
            exit;
        }
    }

    public function index() {
        $this->vars['title'] = 'Быстрые ответы | EasyChat';

        $model_reply = new Reply($this->registry);
        $manager_id = (int)\EasyChat\Library\RequestContext::session()['manager_id'];
        $groups = $model_reply->getGroupedReplies($manager_id);
        $ai_assistant = new QuickReplyAiAssistant($this->registry);
        $ai_availability = $ai_assistant->availability();
        $knowledge_active_total = 0;
        try {
            $model_knowledge = new Knowledge($this->registry);
            $knowledge_active_total = $model_knowledge->getActiveTotal();
        } catch (\Throwable $e) {
            $knowledge_active_total = 0;
        }

        $this->vars['global_replies'] = $groups[Reply::SCOPE_GLOBAL];
        $this->vars['personal_replies'] = $groups[Reply::SCOPE_PERSONAL];
        $this->vars['can_manage_global_replies'] = $this->canManageGlobalReplies();
        $this->vars['quick_reply_ai'] = [
            'available' => !empty($ai_availability['available']),
            'reason' => (string)($ai_availability['reason'] ?? 'unknown'),
            'knowledge_active_total' => $knowledge_active_total,
            'settings_allowed' => isset(\EasyChat\Library\RequestContext::session()['manager_role']) && \EasyChat\Library\RequestContext::session()['manager_role'] === 'admin',
        ];
        $this->vars['quick_reply_limits'] = [
            'max_per_scope' => Reply::MAX_PER_SCOPE,
            'max_title_length' => Reply::MAX_TITLE_LENGTH,
            'max_text_length' => Reply::MAX_TEXT_LENGTH,
        ];

        $this->template->view('quickreply');
    }

    public function save() {
        $this->requireCsrfTokenJson();

        $scope = $this->postedScope();
        if ($scope === Reply::SCOPE_GLOBAL && !$this->canManageGlobalReplies()) {
            $this->renderHtmxAlert('Общие шаблоны может менять только администратор', 403);
        }

        $items = $this->extractItemsFromPost();
        $model_reply = new Reply($this->registry);
        $model_reply->saveScopedReplies($scope, (int)\EasyChat\Library\RequestContext::session()['manager_id'], $items);

        $this->renderQuickReplyRowsPartial($scope, [
            'easychat:quickreply-saved' => [
                'scope' => $scope,
                'label' => $scope === Reply::SCOPE_GLOBAL ? 'общие шаблоны' : 'личные шаблоны',
            ],
        ]);
    }

    public function reorder() {
        $this->requireCsrfTokenJson();

        $scope = $this->postedScope();
        if ($scope === Reply::SCOPE_GLOBAL && !$this->canManageGlobalReplies()) {
            $this->renderHtmxAlert('Общие шаблоны может менять только администратор', 403);
        }

        $ids = $this->extractIdsFromPost('ids');
        try {
            $model_reply = new Reply($this->registry);
            $model_reply->reorderScopedReplies($scope, (int)\EasyChat\Library\RequestContext::session()['manager_id'], $ids);
        } catch (\InvalidArgumentException $e) {
            $this->renderHtmxAlert('Порядок шаблонов устарел. Сохраните вкладку и повторите действие.', 409);
        }

        $this->htmxFragments()->sendHtml('', 204, [
            'HX-Trigger' => json_encode([
                'easychat:quickreply-reordered' => [
                    'scope' => $scope,
                ],
            ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ]);
    }

    public function aiSuggest() {
        $this->requireCsrfTokenJson();

        $scope = $this->postScalar('ai_scope', $this->postScalar('scope', Reply::SCOPE_PERSONAL));
        $scope = $scope === Reply::SCOPE_GLOBAL ? Reply::SCOPE_GLOBAL : Reply::SCOPE_PERSONAL;
        $title = $this->postScalar('title', $this->postScalar('titles', ''));
        $text = $this->postScalar('text', $this->postScalar('texts', ''));
        $task = $this->postScalar('task', '');
        $style = $this->postScalar('style', '');
        $instruction = $this->postScalar('instruction', '');
        $use_knowledge = $this->postScalar('use_knowledge', '') === '1';

        if ($scope === Reply::SCOPE_GLOBAL && !$this->canManageGlobalReplies()) {
            $this->renderQuickReplyAiRowPartial($scope, $title, $text, [
                'error' => 'Общие шаблоны может менять только администратор',
            ]);
        }

        if (!$this->consumeAiRateLimit()) {
            Logger::warning('quick_reply_ai rate_limited manager_id=' . (int)\EasyChat\Library\RequestContext::session()['manager_id'] . ' scope=' . $scope, 'ai.log');
            $this->renderQuickReplyAiRowPartial($scope, $title, $text, [
                'error' => 'Слишком много AI-запросов подряд. Подождите несколько минут и попробуйте снова.',
            ]);
        }

        $assistant = new QuickReplyAiAssistant($this->registry);
        try {
            $result = $assistant->suggest([
                'scope' => $scope,
                'title' => $title,
                'text' => $text,
                'task' => $task,
                'style' => $style,
                'instruction' => $instruction,
                'use_knowledge' => $use_knowledge,
            ]);
        } catch (\Throwable $e) {
            Logger::error('quick_reply_ai exception manager_id=' . (int)\EasyChat\Library\RequestContext::session()['manager_id'] . ' scope=' . $scope . ' error=' . $this->safeLogError($e->getMessage()), 'ai.log');
            $this->renderQuickReplyAiRowPartial($scope, $title, $text, [
                'error' => 'ИИ сейчас не смог подготовить вариант. Попробуйте ещё раз позже.',
            ]);
        }

        Logger::info(
            'quick_reply_ai result manager_id=' . (int)\EasyChat\Library\RequestContext::session()['manager_id']
            . ' scope=' . $scope
            . ' task=' . preg_replace('/[^a-z_]/', '', $task)
            . ' style=' . preg_replace('/[^a-z_]/', '', $style)
            . ' source_count=' . (int)($result['source_count'] ?? 0)
            . ' success=' . (!empty($result['success']) ? '1' : '0')
            . ' error=' . preg_replace('/[^a-z0-9_:-]/i', '_', (string)($result['error'] ?? '')),
            'ai.log'
        );

        if (empty($result['success'])) {
            $error = (string)($result['error'] ?? 'unknown');
            $this->renderQuickReplyAiRowPartial($scope, $title, $text, [
                'error' => $this->aiErrorMessage($error),
                'source_count' => (int)($result['source_count'] ?? 0),
            ]);
        }

        $this->renderQuickReplyAiRowPartial($scope, $title, $text, [
            'title' => (string)$result['title'],
            'text' => (string)$result['text'],
            'source_count' => (int)$result['source_count'],
            'warning' => (string)($result['warning'] ?? ''),
        ]);
    }

    public function blankRow() {
        $scope = isset(\EasyChat\Library\RequestContext::get()['scope']) ? (string)\EasyChat\Library\RequestContext::get()['scope'] : Reply::SCOPE_PERSONAL;
        $scope = $scope === Reply::SCOPE_GLOBAL ? Reply::SCOPE_GLOBAL : Reply::SCOPE_PERSONAL;
        if ($scope === Reply::SCOPE_GLOBAL && !$this->canManageGlobalReplies()) {
            $this->renderHtmxAlert('Общие шаблоны может менять только администратор', 403);
        }

        $state = $this->quickReplyPartialState($scope);
        $this->htmxFragments()->sendFragment($this->quickReplyFragments()->replyRow($scope, [
            'title' => '',
            'text' => '',
            'scope' => $scope,
        ], $state['editable'], Reply::MAX_TITLE_LENGTH, Reply::MAX_TEXT_LENGTH, $state['ai_available'], $state['ai_reason'], $state['ai_knowledge_available']));
    }

    private function extractItemsFromPost() {
        $ids = $this->extractIdsFromPost('reply_ids', true);
        $titles = isset(\EasyChat\Library\RequestContext::post()['titles']) && is_array(\EasyChat\Library\RequestContext::post()['titles']) ? \EasyChat\Library\RequestContext::post()['titles'] : [];
        $texts = isset(\EasyChat\Library\RequestContext::post()['texts']) && is_array(\EasyChat\Library\RequestContext::post()['texts']) ? \EasyChat\Library\RequestContext::post()['texts'] : [];
        $items = [];

        $count = max(count($titles), count($texts));
        for ($i = 0; $i < $count; $i++) {
            $items[] = [
                'id' => isset($ids[$i]) ? (int)$ids[$i] : 0,
                'title' => isset($titles[$i]) ? (string)$titles[$i] : '',
                'text' => isset($texts[$i]) ? (string)$texts[$i] : '',
            ];
        }
        return $items;
    }

    private function extractIdsFromPost($key, $preserve_positions = false) {
        $values = isset(\EasyChat\Library\RequestContext::post()[$key]) && is_array(\EasyChat\Library\RequestContext::post()[$key])
            ? \EasyChat\Library\RequestContext::post()[$key]
            : [];
        $ids = [];
        foreach ($values as $value) {
            $id = (int)$value;
            if ($preserve_positions) {
                $ids[] = $id > 0 ? $id : 0;
            } elseif ($id > 0) {
                $ids[] = $id;
            }
        }
        return $ids;
    }

    private function postedScope() {
        $scope = isset(\EasyChat\Library\RequestContext::post()['scope']) ? (string)\EasyChat\Library\RequestContext::post()['scope'] : Reply::SCOPE_PERSONAL;
        return $scope === Reply::SCOPE_GLOBAL ? Reply::SCOPE_GLOBAL : Reply::SCOPE_PERSONAL;
    }

    private function canManageGlobalReplies() {
        return isset(\EasyChat\Library\RequestContext::session()['manager_role']) && \EasyChat\Library\RequestContext::session()['manager_role'] === 'admin';
    }

    private function consumeAiRateLimit() {
        $now = time();
        $window_start = $now - self::AI_RATE_LIMIT_WINDOW_SECONDS;
        $hits = isset(\EasyChat\Library\RequestContext::session()['quick_reply_ai_hits']) && is_array(\EasyChat\Library\RequestContext::session()['quick_reply_ai_hits'])
            ? \EasyChat\Library\RequestContext::session()['quick_reply_ai_hits']
            : [];
        $hits = array_values(array_filter($hits, function($value) use ($window_start) {
            return (int)$value >= $window_start;
        }));

        if (count($hits) >= self::AI_RATE_LIMIT_MAX_REQUESTS) {
            \EasyChat\Library\RequestContext::session()['quick_reply_ai_hits'] = $hits;
            return false;
        }

        $hits[] = $now;
        \EasyChat\Library\RequestContext::session()['quick_reply_ai_hits'] = $hits;
        return true;
    }

    private function aiErrorMessage($code) {
        switch ($code) {
            case 'ai_disabled':
                return 'AI-помощник быстрых ответов сейчас недоступен.';
            case 'api_key_missing':
                return 'Для ИИ-помощника не настроен API key.';
            case 'text_required':
                return 'Введите текст шаблона или выберите задачу «Сгенерировать».';
            case 'invalid_ai_response':
            case 'empty_ai_response':
                return 'ИИ не вернул готовый вариант. Попробуйте изменить пожелание.';
            default:
                return 'ИИ сейчас не смог подготовить вариант. Попробуйте ещё раз позже.';
        }
    }

    private function safeLogError($message) {
        $message = trim((string)$message);
        $message = preg_replace('/Bearer\s+[A-Za-z0-9._\-]+/i', 'Bearer ***', $message);
        $message = preg_replace('/api[_-]?key=([^&\s]+)/i', 'api_key=***', $message);
        $message = preg_replace('/token=([^&\s]+)/i', 'token=***', $message);
        return mb_substr($message, 0, 180);
    }

    private function postScalar($key, $default = '') {
        $post = \EasyChat\Library\RequestContext::post();
        if (!array_key_exists($key, $post)) {
            return (string)$default;
        }
        $value = $post[$key];
        if (is_array($value)) {
            foreach ($value as $item) {
                if (is_scalar($item)) {
                    return (string)$item;
                }
            }
            return (string)$default;
        }
        return is_scalar($value) ? (string)$value : (string)$default;
    }

    private function quickReplyPartialState($scope) {
        $ai_assistant = new QuickReplyAiAssistant($this->registry);
        $ai_availability = $ai_assistant->availability();
        $knowledge_active_total = 0;
        try {
            $model_knowledge = new Knowledge($this->registry);
            $knowledge_active_total = $model_knowledge->getActiveTotal();
        } catch (\Throwable $e) {
            $knowledge_active_total = 0;
        }

        return [
            'editable' => $scope !== Reply::SCOPE_GLOBAL || $this->canManageGlobalReplies(),
            'ai_available' => !empty($ai_availability['available']),
            'ai_reason' => (string)($ai_availability['reason'] ?? 'unknown'),
            'ai_knowledge_available' => $knowledge_active_total > 0,
        ];
    }

    private function renderQuickReplyRowsPartial($scope, array $trigger = []) {
        $model_reply = new Reply($this->registry);
        $groups = $model_reply->getGroupedReplies((int)\EasyChat\Library\RequestContext::session()['manager_id']);
        $replies = isset($groups[$scope]) && is_array($groups[$scope]) ? $groups[$scope] : [];
        $state = $this->quickReplyPartialState($scope);

        $this->htmxFragments()->sendFragment(
            $this->quickReplyFragments()->replyRows($scope, $replies, $state['editable'], Reply::MAX_TITLE_LENGTH, Reply::MAX_TEXT_LENGTH, $state['ai_available'], $state['ai_reason'], $state['ai_knowledge_available']),
            $trigger
        );
    }

    private function renderQuickReplyAiRowPartial($scope, $title, $text, array $ai_state) {
        $state = $this->quickReplyPartialState($scope);
        $this->htmxFragments()->sendFragment($this->quickReplyFragments()->replyRow($scope, [
            'title' => (string)$title,
            'text' => (string)$text,
            'scope' => $scope,
        ], $state['editable'], Reply::MAX_TITLE_LENGTH, Reply::MAX_TEXT_LENGTH, $state['ai_available'], $state['ai_reason'], $state['ai_knowledge_available'], $ai_state));
    }

    private function renderHtmxAlert($message, $status = 400) {
        $this->htmxFragments()->sendAlert(
            '#error-alert',
            $this->quickReplyFragments()->alert('error', (string)$message, false),
            (int)$status
        );
    }

    private function quickReplyFragments() {
        if (!$this->quick_reply_fragments instanceof QuickReplyFragments) {
            $this->quick_reply_fragments = new QuickReplyFragments();
        }
        return $this->quick_reply_fragments;
    }

    private function htmxFragments() {
        if (!$this->htmx_fragment_renderer instanceof HtmxFragmentRenderer) {
            $this->htmx_fragment_renderer = new HtmxFragmentRenderer();
        }
        return $this->htmx_fragment_renderer;
    }

}
