<?php
namespace EasyChat\Library\Ai;

use D1medrol\MysqlVector\VectorTable;
use EasyChat\KnowledgeSection;









class StepRagSearch implements StepInterface {

     
    private const TOP_K      = 10;

     
    private const THRESHOLD  = 0.70;

     
    private const MAX_INJECT = 5;

    public function execute(array &$context, array $settings) {
        $responder    = $context['ai_responder'];
        $last_message = $context['last_message'];

        $policy = is_array($context['channel_policy'] ?? null) ? $context['channel_policy'] : [];
        if (!($policy['rag_allowed'] ?? true)) {
            $responder->log('[Шаг RAG] Пропуск по policy внешнего канала: ' . (string)($policy['rag_reason'] ?? 'not_allowed'));
            return null;
        }

        if (empty($last_message)) {
            $responder->log('[Шаг RAG] Пропуск: последнее сообщение пусто.');
            return null;
        }

        if (!defined('VECTOR_STORAGE')) {
            $responder->log('[Шаг RAG] Пропуск: константа VECTOR_STORAGE не определена.');
            return null;
        }

        $responder->log('[Шаг RAG] Получение эмбеддинга для запроса...');

         
        $provider_name = $settings['chat_ai_provider'] ?? 'gemini';
        $embedding_config = EmbeddingConfigResolver::resolve($provider_name);
        try {
            $embedding = $responder->getProvider()->getEmbedding(
                $last_message,
                $embedding_config['model'],
                EmbeddingConfigResolver::requestDimension($embedding_config),
                [
                     
                     
                     
                     
                    'connect_timeout' => 2,
                    'timeout' => 4,
                ]
            );
        } catch (\Throwable $e) {
             
             
             
             
            $responder->log('[Шаг RAG] Ошибка получения эмбеддинга: ' . $e->getMessage() . ' — пропуск шага.');
            return null;
        }

        if (empty($embedding) || !is_array($embedding)) {
            $responder->log('[Шаг RAG] Ошибка: эмбеддинг не получен — пропуск шага.');
            return null;
        }

        $dims = count($embedding);
        $responder->log("[Шаг RAG] Эмбеддинг получен ({$dims} dims). Поиск в векторной БД...");

         
        try {
            $db          = $context['registry']->get('db');
            $storage_status = VectorStorageGuard::status($context['registry'], VECTOR_STORAGE, $dims);
            if (empty($storage_status['compatible'])) {
                $responder->log('[Шаг RAG] Пропуск: vector storage не готов или имеет другую размерность (' . $storage_status['reason'] . ').');
                return null;
            }

            $vectorTable = new VectorTable(
                $db->getRawConnection(),
                VECTOR_STORAGE,
                $dims    
            );

            $candidates = $vectorTable->searchCosim($embedding, self::TOP_K);

        } catch (\Throwable $e) {
            $responder->log('[Шаг RAG] Ошибка векторного поиска: ' . $e->getMessage());
            return null;
        }

        if (empty($candidates)) {
            $responder->log('[Шаг RAG] Векторная БД не вернула кандидатов.');
            return null;
        }

        $top_score = round((float)($candidates[0]['score'] ?? 0), 4);
        $responder->log('[Шаг RAG] Кандидатов получено: ' . count($candidates) . '. Лучший score: ' . $top_score);

         
        $above = array_values(array_filter($candidates, function ($c) {
            return (float)$c['score'] >= self::THRESHOLD;
        }));

        if (empty($above)) {
            $responder->log('[Шаг RAG] Нет результатов выше порога ' . self::THRESHOLD . ' — пропуск.');
            return null;
        }

         
        $above = array_slice($above, 0, self::MAX_INJECT);

        $responder->log('[Шаг RAG] Прошли порог ' . self::THRESHOLD . ': ' . count($above) . ' результатов.');

         
        $vector_ids = implode(',', array_map('intval', array_column($above, 'vector_id')));
        $p          = easychat_db_prefix();

        $result  = $db->query(
            "SELECT id, title, content, vector_id, section_id, item_id
             FROM `{$p}knowledge`
             WHERE vector_id IN ({$vector_ids}) AND status = 'active'
             LIMIT " . self::MAX_INJECT
        );
        $records = $result->rows ?? [];

         
         
         
         
        $search_mode = (int)($settings['chat_productsearch'] ?? 0);
        if ($search_mode !== 3) {
            $records = array_values(array_filter($records, function (array $record): bool {
                return (int)($record['section_id'] ?? 0) !== KnowledgeSection::PRODUCT;
            }));
        }
        if (empty($records)) {
            $responder->log('[Шаг RAG] После проверки режима не осталось безопасных записей для вставки.');
            return null;
        }

        if (empty($records)) {
            $responder->log('[Шаг RAG] Активные записи не найдены для vector_ids: ' . $vector_ids);
            return null;
        }

         
        $score_map = [];
        foreach ($above as $c) {
            $score_map[(int)$c['vector_id']] = round((float)$c['score'], 4);
        }

         
        usort($records, function ($a, $b) use ($score_map) {
            return ($score_map[(int)$b['vector_id']] ?? 0) <=> ($score_map[(int)$a['vector_id']] ?? 0);
        });

        $responder->log('[Шаг RAG] Загружено записей: ' . count($records) . '. Scores: ' . implode(', ', array_map(
            function ($r) use ($score_map) {
                return '"' . mb_substr($r['title'], 0, 30) . '" → ' . ($score_map[(int)$r['vector_id']] ?? '?');
            },
            $records
        )));

         
        $lines = ['[КОНТЕКСТ ИЗ БАЗЫ ЗНАНИЙ]', ''];
        foreach ($records as $idx => $rec) {
            $score_str = isset($score_map[(int)$rec['vector_id']])
                ? ' (релевантность: ' . $score_map[(int)$rec['vector_id']] . ')'
                : '';
            $lines[] = '### ' . ($idx + 1) . '. ' . trim($rec['title']) . $score_str;
            if ((int)($rec['section_id'] ?? 0) === KnowledgeSection::PRODUCT) {
                $lines[] = '[КАНДИДАТ ТОВАРА: цена, наличие и возможность заказа здесь не подтверждены. Перед ответом обязательно вызови search_products по названию/модели.]';
            }
            $lines[] = trim($rec['content']);
            $lines[] = '';
        }
        $lines[] = '[КОНЕЦ КОНТЕКСТА]';
        $lines[] = '';

        $rag_block = implode("\n", $lines);
        $existing_context = trim((string)($context['rag_context'] ?? ''));
        $context['rag_context'] = $existing_context === ''
            ? $rag_block
            : $existing_context . "\n\n" . $rag_block;

        $responder->log('[Шаг RAG] Контекст из ' . count($records) . ' документов добавлен в rag_context.');

        return null;
    }
}
