<?php
namespace EasyChat\Controller;

use EasyChat\Model\Setting;
use EasyChat\Model\Knowledge;
use EasyChat\KnowledgeSection;
use EasyChat\Library\RequestContext;
use EasyChat\Library\Queue\QueueManager;
use EasyChat\View\HtmxFragmentRenderer;
use EasyChat\View\KnowledgeStorageFragments;

class KnowledgeStorage extends Base {

    protected $layouts = 'layout';

    public function __construct($registry) {
        parent::__construct($registry);

        if (!isset(\EasyChat\Library\RequestContext::session()['manager_id'])) {
            $this->denyKnowledgeAccess('auth');
        }
        if (\EasyChat\Library\RequestContext::session()['manager_role'] !== 'admin') {
            $this->denyKnowledgeAccess('forbidden');
        }
        $model_setting = new Setting($registry);
        if (trim((string)$model_setting->get('chat_ai_key', '')) === '') {
            $this->denyKnowledgeAccess('ai_key_required');
        }
    }

    private function denyKnowledgeAccess(string $reason): void {
        if ($this->isHtmxRequest()) {
             
             
             
            http_response_code($reason === 'auth' ? 401 : 403);
            header('Content-Type: text/plain; charset=utf-8');
            header('Cache-Control: no-store');
            echo $reason === 'auth'
                ? 'Сессия завершена. Обновите страницу и войдите снова.'
                : ($reason === 'ai_key_required'
                    ? 'Сначала настройте ключ ИИ в настройках EasyChat.'
                    : 'Недостаточно прав для работы с базой знаний.');
            exit;
        }

        header('Location: index.php?route=' . ($reason === 'auth' ? 'auth' : 'index'));
        exit;
    }

     
     
     

    public function index() {
        $this->vars['title'] = 'База знаний ИИ | EasyChat';

        $model_setting   = new Setting($this->registry);
        $state = $this->buildListState(RequestContext::get());

        $fragments = new KnowledgeStorageFragments();

        $this->vars['ai_provider'] = $model_setting->get('chat_ai_provider', 'gemini');
        $this->vars['filters']     = $state['filters'];
        $this->vars['total']       = $state['total'];
        $this->vars['page']        = $state['page'];
        $this->vars['filters_html'] = $fragments->filters($state['filters'], $state['total']);
        $this->vars['list_html']    = $fragments->listPanel(
            $state['items'],
            $state['total'],
            $state['page'],
            $state['limit'],
            $state['filters'],
            $state['selected_ids']
        );
        $this->vars['editor_html'] = $fragments->editorShell();

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

    public function list() {
        if (!$this->isHtmxRequest()) {
            $this->index();
            return;
        }

        $state = $this->buildListState(RequestContext::get());
        $fragments = new KnowledgeStorageFragments();
        (new HtmxFragmentRenderer())->sendFragment(
            $fragments->listPanel(
                $state['items'],
                $state['total'],
                $state['page'],
                $state['limit'],
                $state['filters'],
                $state['selected_ids']
            )
        );
    }

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

        $post = RequestContext::post();
        $action = (string)($post['action'] ?? '');
        $selected_ids = $this->selectedIdsFromRequest($post);
        $message = 'Выберите записи для массового действия.';
        $type = 'warning';

        if ($selected_ids) {
            $model_knowledge = new Knowledge($this->registry);

            if ($action === 'enable') {
                $count = $model_knowledge->setStatusByIds($selected_ids, 'active', ['disabled']);
                $message = $count > 0 ? 'Включено записей: ' . $count . '.' : 'Нет отключенных записей среди выбранных.';
                $type = $count > 0 ? 'success' : 'warning';
            } elseif ($action === 'disable') {
                $count = $model_knowledge->setStatusByIds($selected_ids, 'disabled', ['active']);
                $message = $count > 0 ? 'Отключено записей: ' . $count . '.' : 'Нет активных записей среди выбранных.';
                $type = $count > 0 ? 'success' : 'warning';
            } elseif ($action === 'retry') {
                $rows = $model_knowledge->resetFailedToPendingByIds($selected_ids);
                foreach ($rows as $row) {
                    $this->queueDocumentEmbedding(
                        (int)($row['id'] ?? 0),
                        (string)($row['title'] ?? ''),
                        (string)($row['content'] ?? ''),
                        'active',
                        (int)($row['section_id'] ?? KnowledgeSection::DOCUMENT)
                    );
                }
                $count = count($rows);
                $message = $count > 0 ? 'Переиндексация запущена для записей: ' . $count . '.' : 'Среди выбранных нет записей с ошибкой.';
                $type = $count > 0 ? 'success' : 'warning';
            } elseif ($action === 'delete') {
                $count = $model_knowledge->deleteByIds($selected_ids);
                $message = $count > 0 ? 'Удалено записей: ' . $count . '.' : 'Выбранные записи уже не найдены.';
                $type = $count > 0 ? 'success' : 'warning';
                $selected_ids = [];
            } else {
                $message = 'Неизвестное массовое действие.';
                $type = 'danger';
            }
        }

        $state = $this->buildListState($post, $selected_ids);
        $fragments = new KnowledgeStorageFragments();
        (new HtmxFragmentRenderer())->sendFragment(
            $fragments->listPanel(
                $state['items'],
                $state['total'],
                $state['page'],
                $state['limit'],
                $state['filters'],
                $state['selected_ids']
            ),
            [
                'easychat:knowledge-bulk-done' => [
                    'type' => $type,
                    'message' => $message,
                ],
            ]
        );
    }

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

        $post = RequestContext::post();
        $id = (int)($post['id'] ?? 0);
        $article = [];
        $mode = $id > 0 ? 'edit' : 'create';
        $error = '';

        if ($id > 0) {
            $model_knowledge = new Knowledge($this->registry);
            $row = $model_knowledge->getById($id);
            if (empty($row)) {
                $mode = 'create';
                $error = 'Статья не найдена. Обновите список и попробуйте снова.';
            } elseif ((int)($row['section_id'] ?? 0) !== KnowledgeSection::DOCUMENT) {
                $mode = 'create';
                $error = 'Эта запись синхронизируется из CMS. Измените её в CMS и переиндексируйте базу знаний.';
            } else {
                $article = $this->serializeArticle($row);
            }
        }

        $fragments = new KnowledgeStorageFragments();
        (new HtmxFragmentRenderer())->sendFragment(
            $fragments->editorShell($article, $mode, $error),
            ['easychat:knowledge-editor-ready' => ['mode' => $mode, 'error' => $error !== '']]
        );
    }

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

        $post = RequestContext::post();
        $id = (int)($post['id'] ?? 0);
        $title = trim((string)($post['title'] ?? ''));
        $content = trim((string)($post['content'] ?? ''));
        $mode = $id > 0 ? 'edit' : 'create';

        if ($title === '' || $content === '') {
            $this->sendEditorValidation($mode, [
                'id' => $id,
                'title' => $title,
                'content' => $content,
                'section_id' => KnowledgeSection::DOCUMENT,
                'status' => $id > 0 ? 'pending' : '',
            ], 'Заполните заголовок и текст статьи.');
        }

        $model_knowledge = new Knowledge($this->registry);
        if ($id > 0) {
            $previous = $model_knowledge->getById($id);
            $row = $model_knowledge->updateManualDocument($id, $title, $content);
            if (empty($row)) {
                $this->sendEditorValidation($mode, [
                    'id' => $id,
                    'title' => $title,
                    'content' => $content,
                    'section_id' => KnowledgeSection::DOCUMENT,
                    'status' => 'pending',
                ], 'Статья не найдена или синхронизируется из CMS.');
            }

            $final_status = ((string)($previous['status'] ?? '') === 'disabled') ? 'disabled' : 'active';
            $this->queueDocumentEmbedding($id, $title, $content, $final_status);
            $message = 'Статья обновлена и поставлена в очередь переиндексации.';
        } else {
            $id = $model_knowledge->add($title, $content);
            $this->queueDocumentEmbedding($id, $title, $content);
            $message = 'Статья добавлена и поставлена в очередь индексирования.';
        }

        $state = $this->buildListState($post);
        $fragments = new KnowledgeStorageFragments();
        (new HtmxFragmentRenderer())->sendFragment(
            $fragments->listPanel(
                $state['items'],
                $state['total'],
                $state['page'],
                $state['limit'],
                $state['filters'],
                $state['selected_ids']
            ),
            [
                'easychat:knowledge-document-saved' => [
                    'type' => 'success',
                    'mode' => $mode,
                    'id' => $id,
                    'message' => $message,
                ],
            ]
        );
    }

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

        $post = RequestContext::post();
        $id = (int)($post['id'] ?? 0);
        $action = (string)($post['action'] ?? '');
        $message = 'Не удалось выполнить действие.';
        $type = 'danger';
        $selected_ids = $this->selectedIdsFromRequest($post);

        if ($id <= 0) {
            $message = 'Запись не найдена. Обновите список и попробуйте снова.';
        } else {
            $model_knowledge = new Knowledge($this->registry);

            if ($action === 'toggle') {
                $status = $model_knowledge->toggleDisabled($id);
                if ($status !== '') {
                    $type = 'success';
                    $message = $status === 'active' ? 'Запись включена.' : 'Запись отключена.';
                } else {
                    $message = 'Статус этой записи сейчас нельзя переключить.';
                }
            } elseif ($action === 'retry') {
                $row = $model_knowledge->resetToPending($id);
                if (!empty($row)) {
                    $this->queueDocumentEmbedding($id, $row['title'], $row['content'], 'active', (int)$row['section_id']);
                    $type = 'success';
                    $message = 'Переиндексация запущена.';
                } else {
                    $message = 'Запись не найдена или уже не находится в статусе «Ошибка».';
                }
            } elseif ($action === 'delete') {
                $row = $model_knowledge->getById($id);
                if (!empty($row)) {
                    $model_knowledge->delete($id);
                    $selected_ids = array_values(array_filter($selected_ids, function($selected_id) use ($id) {
                        return (int)$selected_id !== $id;
                    }));
                    $type = 'success';
                    $message = 'Запись удалена.';
                } else {
                    $type = 'warning';
                    $message = 'Запись уже не найдена. Список обновлен.';
                }
            } else {
                $message = 'Неизвестное действие.';
            }
        }

        $state = $this->buildListState($post, $selected_ids);
        $fragments = new KnowledgeStorageFragments();
        (new HtmxFragmentRenderer())->sendFragment(
            $fragments->listPanel(
                $state['items'],
                $state['total'],
                $state['page'],
                $state['limit'],
                $state['filters'],
                $state['selected_ids']
            ),
            [
                'easychat:knowledge-row-action-done' => [
                    'type' => $type,
                    'action' => $action,
                    'id' => $id,
                    'message' => $message,
                ],
            ]
        );
    }

     
     
     

    public function addDocument() {
        header('Content-Type: application/json; charset=utf-8');
        $this->requireCsrfTokenJson();

        $title   = trim(RequestContext::post()['title']   ?? '');
        $content = trim(RequestContext::post()['content'] ?? '');

        if ($title === '' || $content === '') {
            echo json_encode(['success' => false, 'error' => 'Заполните все поля']);
            exit;
        }

        $model_knowledge = new Knowledge($this->registry);
        $knowledge_id    = $model_knowledge->add($title, $content);

        $this->queueDocumentEmbedding($knowledge_id, $title, $content);

        echo json_encode(['success' => true, 'id' => $knowledge_id]);
        exit;
    }

     
     
     

    public function getDocument() {
        header('Content-Type: application/json; charset=utf-8');
        $this->requireCsrfTokenJson();

        $id = (int)(RequestContext::post()['id'] ?? 0);
        if ($id <= 0) {
            echo json_encode(['success' => false, 'error' => 'Неверный ID']);
            exit;
        }

        $model_knowledge = new Knowledge($this->registry);
        $row = $model_knowledge->getById($id);

        if (empty($row)) {
            echo json_encode(['success' => false, 'error' => 'Статья не найдена']);
            exit;
        }

        if ((int)($row['section_id'] ?? 0) !== KnowledgeSection::DOCUMENT) {
            echo json_encode(['success' => false, 'error' => 'Эта запись синхронизируется из CMS. Измените её в CMS и переиндексируйте базу знаний.']);
            exit;
        }

        echo json_encode([
            'success' => true,
            'article' => $this->serializeArticle($row),
        ]);
        exit;
    }

     
     
     

    public function updateDocument() {
        header('Content-Type: application/json; charset=utf-8');
        $this->requireCsrfTokenJson();

        $id      = (int)(RequestContext::post()['id'] ?? 0);
        $title   = trim(RequestContext::post()['title']   ?? '');
        $content = trim(RequestContext::post()['content'] ?? '');

        if ($id <= 0) {
            echo json_encode(['success' => false, 'error' => 'Неверный ID']);
            exit;
        }

        if ($title === '' || $content === '') {
            echo json_encode(['success' => false, 'error' => 'Заполните все поля']);
            exit;
        }

        $model_knowledge = new Knowledge($this->registry);
        $previous = $model_knowledge->getById($id);
        $row = $model_knowledge->updateManualDocument($id, $title, $content);

        if (empty($row)) {
            echo json_encode(['success' => false, 'error' => 'Статья не найдена или синхронизируется из CMS']);
            exit;
        }

        $final_status = ((string)($previous['status'] ?? '') === 'disabled') ? 'disabled' : 'active';
        $this->queueDocumentEmbedding($id, $title, $content, $final_status);

        echo json_encode([
            'success' => true,
            'article' => $this->serializeArticle($row),
        ]);
        exit;
    }

     
     
     

    public function toggleStatus() {
        header('Content-Type: application/json; charset=utf-8');
        $this->requireCsrfTokenJson();

        $id = (int)(RequestContext::post()['id'] ?? 0);
        if ($id <= 0) {
            echo json_encode(['success' => false, 'error' => 'Неверный ID']);
            exit;
        }

        $model_knowledge = new Knowledge($this->registry);
        $new_status      = $model_knowledge->toggleDisabled($id);

        if ($new_status === '') {
            echo json_encode(['success' => false, 'error' => 'Запись не найдена или статус не допускает переключение']);
            exit;
        }

        echo json_encode(['success' => true, 'status' => $new_status]);
        exit;
    }

     
     
     

    public function retry() {
        header('Content-Type: application/json; charset=utf-8');
        $this->requireCsrfTokenJson();

        $id = (int)(RequestContext::post()['id'] ?? 0);
        if ($id <= 0) {
            echo json_encode(['success' => false, 'error' => 'Неверный ID']);
            exit;
        }

        $model_knowledge = new Knowledge($this->registry);
        $row             = $model_knowledge->resetToPending($id);

        if (empty($row)) {
            echo json_encode(['success' => false, 'error' => 'Запись не найдена или не находится в статусе «Ошибка»']);
            exit;
        }

        $this->queueDocumentEmbedding($id, $row['title'], $row['content'], 'active', (int)$row['section_id']);

        echo json_encode(['success' => true]);
        exit;
    }

     
     
     

    public function delete() {
        header('Content-Type: application/json; charset=utf-8');
        $this->requireCsrfTokenJson();

        $id = (int)(RequestContext::post()['id'] ?? 0);
        if ($id <= 0) {
            echo json_encode(['success' => false, 'error' => 'Неверный ID']);
            exit;
        }

        $model_knowledge = new Knowledge($this->registry);
        $model_knowledge->delete($id);

        echo json_encode(['success' => true]);
        exit;
    }

    private function queueDocumentEmbedding($knowledge_id, $title, $content, $final_status = 'active', $section_id = KnowledgeSection::DOCUMENT) {
        if ((int)$knowledge_id <= 0 || trim((string)$title) === '' || trim((string)$content) === '') {
            return;
        }

        $queue = new QueueManager($this->registry);
        $payload = [
            'knowledge_id' => (int)$knowledge_id,
            'text'         => (string)$title . "\n\n" . (string)$content,
            'item_id'      => (int)$knowledge_id,
            'section_id'   => (int)$section_id,
            'meta'         => ['title' => (string)$title, 'source' => 'document'],
        ];

        if ($final_status === 'disabled') {
            $payload['final_status'] = 'disabled';
        }

        $queue->push('embedding', $payload);
    }

    private function buildListState(array $source, array $selected_ids_override = null): array {
        $filters = $this->normalizeFilters($source);
        $page = max(1, (int)($source['page'] ?? 1));
        $limit = 20;
        $model_knowledge = new Knowledge($this->registry);
        $total = $model_knowledge->getFilteredTotal($filters);
        $total_pages = max(1, (int)ceil($total / $limit));
        if ($page > $total_pages) {
            $page = $total_pages;
        }

        return [
            'filters' => $filters,
            'page' => $page,
            'limit' => $limit,
            'total' => $total,
            'items' => $model_knowledge->getFilteredList($filters, $page, $limit),
            'selected_ids' => $selected_ids_override !== null ? $selected_ids_override : $this->selectedIdsFromRequest($source),
        ];
    }

    private function normalizeFilters(array $source): array {
        $q = trim((string)($source['q'] ?? ''));
        if ($q !== '') {
            $q = mb_substr(strip_tags($q), 0, 120, 'UTF-8');
        }

        $status = (string)($source['status'] ?? '');
        if (!in_array($status, ['pending', 'indexing', 'active', 'disabled', 'failed'], true)) {
            $status = '';
        }

        $section_id = (int)($source['section_id'] ?? 0);
        if (!in_array($section_id, [KnowledgeSection::DOCUMENT, KnowledgeSection::PRODUCT, KnowledgeSection::PAGE], true)) {
            $section_id = 0;
        }

        return [
            'q' => $q,
            'status' => $status,
            'section_id' => $section_id,
        ];
    }

    private function selectedIdsFromRequest(array $source): array {
        $raw = $source['selected_ids'] ?? [];
        if (!is_array($raw)) {
            $raw = [$raw];
        }

        $ids = [];
        foreach ($raw as $id) {
            $id = (int)$id;
            if ($id > 0) {
                $ids[$id] = $id;
            }
            if (count($ids) >= 100) {
                break;
            }
        }
        return array_values($ids);
    }

    private function sendEditorValidation(string $mode, array $article, string $error): void {
        $fragments = new KnowledgeStorageFragments();
        (new HtmxFragmentRenderer())->sendHtml(
            $fragments->editorShell($article, $mode, $error),
            422,
            [
                'HX-Retarget' => '#knowledge-document-editor-shell',
                'HX-Reswap' => 'outerHTML',
                'HX-Trigger' => json_encode([
                    'easychat:knowledge-editor-ready' => ['mode' => $mode, 'error' => true],
                ], JSON_UNESCAPED_SLASHES),
            ]
        );
    }

    private function isHtmxRequest(): bool {
        $server = RequestContext::server();
        return (string)($server['HTTP_X_EASYCHAT_HTMX'] ?? '') === '1'
            || strtolower((string)($server['HTTP_HX_REQUEST'] ?? '')) === 'true';
    }

    private function serializeArticle(array $row): array {
        return [
            'id' => (int)($row['id'] ?? 0),
            'title' => (string)($row['title'] ?? ''),
            'content' => (string)($row['content'] ?? ''),
            'section_id' => (int)($row['section_id'] ?? 0),
            'status' => (string)($row['status'] ?? ''),
            'date_modified' => (string)($row['date_modified'] ?? ''),
        ];
    }
}
