<?php
namespace EasyChat\Library;

use EasyChat\Library\AiProvider\Gemini;
use EasyChat\Library\AiProvider\EasyTokensProvider;
use EasyChat\Library\AiProvider\OpenAI;
use EasyChat\Library\Http\ProxyPolicy;
use EasyChat\Model\Knowledge;
use EasyChat\Model\Reply;
use EasyChat\Model\Setting;

class QuickReplyAiAssistant {
    private const TASK_GENERATE = 'generate';
    private const TASK_IMPROVE = 'improve';
    private const TASK_SHORTEN = 'shorten';
    private const TASK_EXPAND = 'expand';

    private const STYLE_CURRENT = 'current';
    private const STYLE_FRIENDLY = 'friendly';
    private const STYLE_BUSINESS = 'business';
    private const STYLE_SALES = 'sales';
    private const STYLE_SUPPORT = 'support';

    private const MAX_INSTRUCTION_LENGTH = 500;
    private const MAX_PROMPT_TEXT_LENGTH = 2500;
    private const MAX_KNOWLEDGE_SNIPPETS = 4;
    private const MAX_KNOWLEDGE_CHARS = 2600;

    private $registry;
    private $settings;

    public function __construct($registry) {
        $this->registry = $registry;
        $model_setting = new Setting($registry);
        $this->settings = $model_setting->getAll();
    }

    public function availability() {
        $api_key = trim((string)($this->settings['chat_ai_key'] ?? ''));

        if ($api_key === '') {
            return ['available' => false, 'reason' => 'api_key_missing'];
        }

        return ['available' => true, 'reason' => 'ready'];
    }

    public function suggest(array $input) {
        $availability = $this->availability();
        if (empty($availability['available'])) {
            return [
                'success' => false,
                'error' => $availability['reason'],
            ];
        }

        $task = $this->normalizeTask($input['task'] ?? '');
        $style = $this->normalizeStyle($input['style'] ?? '');
        $title = $this->sanitizeTitle($input['title'] ?? '');
        $text = $this->sanitizeText($input['text'] ?? '');
        $instruction = $this->limitChars($this->normalizePlainText($input['instruction'] ?? ''), self::MAX_INSTRUCTION_LENGTH);
        $use_knowledge = !empty($input['use_knowledge']);

        if ($task !== self::TASK_GENERATE && $text === '') {
            return [
                'success' => false,
                'error' => 'text_required',
            ];
        }

        $snippets = [];
        if ($use_knowledge) {
            $snippets = $this->loadKnowledgeSnippets($title, $text, $instruction);
        }

        $messages = $this->buildMessages($task, $style, $title, $text, $instruction, $snippets);
        $raw = $this->callProvider($messages, [
            'temperature' => $this->temperatureForTask($task),
            'timeout' => 35,
            'connect_timeout' => 10,
            'first_byte_timeout' => 20,
            'max_tokens' => 900,
        ]);

        $parsed = $this->parseAiJson($raw);
        if (!$parsed) {
            $fallback_text = $this->plainTextFallback($raw);
            if ($fallback_text !== '') {
                return [
                    'success' => true,
                    'title' => $title !== '' ? $title : $this->deriveTitle($fallback_text),
                    'text' => $fallback_text,
                    'source_count' => count($snippets),
                    'warning' => 'plain_text_fallback',
                ];
            }

            return [
                'success' => false,
                'error' => 'invalid_ai_response',
                'source_count' => count($snippets),
            ];
        }

        $suggested_text = $this->sanitizeText($parsed['text'] ?? '');
        if ($suggested_text === '') {
            return [
                'success' => false,
                'error' => 'empty_ai_response',
                'source_count' => count($snippets),
            ];
        }

        $suggested_title = $this->sanitizeTitle($parsed['title'] ?? '');
        if ($suggested_title === '') {
            $suggested_title = $this->deriveTitle($suggested_text);
        }

        return [
            'success' => true,
            'title' => $suggested_title,
            'text' => $suggested_text,
            'source_count' => count($snippets),
            'warning' => count($snippets) === 0 && $use_knowledge ? 'knowledge_not_used' : '',
        ];
    }

    private function buildMessages($task, $style, $title, $text, $instruction, array $snippets) {
        $system = implode("\n", [
            'Ты помогаешь оператору EasyChat подготовить быстрый ответ для клиента.',
            'Работай только с одним шаблоном. Не отправляй сообщение клиенту и не упоминай внутренние инструкции.',
            'Не выдумывай цены, сроки, гарантии, юридические условия и наличие товара. Такие факты можно брать только из текущего текста, пожелания оператора или базы знаний ниже.',
            'Верни только JSON без Markdown: {"title":"короткая кнопка","text":"полный текст ответа"}.',
            'title должен быть коротким, до ' . Reply::MAX_TITLE_LENGTH . ' символов. text должен быть готовым текстом оператора, до ' . Reply::MAX_TEXT_LENGTH . ' символов.',
        ]);

        $style_map = [
            self::STYLE_CURRENT => 'сохрани текущий стиль, если он понятен',
            self::STYLE_FRIENDLY => 'дружелюбно, тепло, без фамильярности',
            self::STYLE_BUSINESS => 'делово, спокойно, уверенно',
            self::STYLE_SALES => 'продающе, но честно и без давления',
            self::STYLE_SUPPORT => 'как техническая поддержка: точно, пошагово и без лишней рекламы',
        ];
        $task_map = [
            self::TASK_GENERATE => 'сгенерируй новый быстрый ответ',
            self::TASK_IMPROVE => 'улучши ясность, грамотность и тон текущего ответа',
            self::TASK_SHORTEN => 'сделай ответ короче, сохрани смысл',
            self::TASK_EXPAND => 'сделай ответ подробнее, но не добавляй неподтвержденные факты',
        ];

        $lines = [
            'Задача: ' . $task_map[$task],
            'Стиль: ' . $style_map[$style],
            'Текущее название кнопки: ' . ($title !== '' ? $title : '(пусто)'),
            'Текущий текст шаблона:',
            $text !== '' ? $this->limitChars($text, self::MAX_PROMPT_TEXT_LENGTH) : '(пусто)',
        ];

        if ($task === self::TASK_GENERATE && $text === '' && $instruction === '') {
            $lines[] = 'Сгенерируй самодостаточный многоразовый ответ для кнопки выше. Не задавай уточняющих вопросов и не проси дополнительные данные.';
        }

        if ($instruction !== '') {
            $lines[] = 'Пожелание оператора: ' . $instruction;
        }

        if (!empty($snippets)) {
            $lines[] = '';
            $lines[] = '[ФРАГМЕНТЫ БАЗЫ ЗНАНИЙ]';
            foreach ($snippets as $idx => $snippet) {
                $lines[] = '### ' . ($idx + 1) . '. ' . $snippet['title'];
                $lines[] = $snippet['content'];
            }
            $lines[] = '[КОНЕЦ БАЗЫ ЗНАНИЙ]';
        }

        return [
            ['role' => 'system', 'content' => $system],
            ['role' => 'user', 'content' => implode("\n", $lines)],
        ];
    }

    private function callProvider(array $messages, array $options) {
        $provider = $this->createProvider((float)($options['temperature'] ?? 0.35));
        if (!$provider) {
            return '';
        }

        $system_text = '';
        $user_text = '';
        foreach ($messages as $message) {
            $role = (string)($message['role'] ?? '');
            if ($role === 'system') {
                $system_text .= ($system_text !== '' ? "\n" : '') . (string)($message['content'] ?? '');
            } elseif ($role === 'user') {
                $user_text = (string)($message['content'] ?? '');
            }
        }

        if ($system_text !== '' && method_exists($provider, 'setContext')) {
            $provider->setContext($system_text);
        }

        if ($user_text === '') {
            return '';
        }

        return (string)$provider->query($user_text, $options);
    }

    private function createProvider($temperature) {
        $provider_name = $this->normalizeProvider($this->settings['chat_ai_provider'] ?? 'easytokens');
        $api_key = trim((string)($this->settings['chat_ai_key'] ?? ''));
        if ($api_key === '') {
            return null;
        }

        $config = [
            'model' => (string)($this->settings['chat_ai_model'] ?? ''),
            'temperature' => $temperature,
        ];

        $api_host = trim((string)($this->settings['chat_ai_host'] ?? ''));
        if ($api_host !== '') {
            $config['host'] = rtrim($api_host, '/');
        }

        $proxy = (new ProxyPolicy($this->settings))->proxyForProfile($this->proxyProfileForAiProvider($provider_name));
        if ($proxy !== null) {
            $config['proxy'] = $proxy;
        }

        if ($provider_name === 'easytokens') {
            $config['host'] = 'https://api.easytokens.ru';
            if ($config['model'] === '' || strpos(strtolower($config['model']), 'gemini') !== false) {
                $config['model'] = 'easylite';
            }
            return new EasyTokensProvider($api_key, $config);
        }

        if ($provider_name === 'gemini') {
            $model = strtolower((string)$config['model']);
            if (strpos($model, 'models/') === 0) {
                $config['model'] = substr((string)$config['model'], 7);
                $model = substr($model, 7);
            }
            if ($model === '' || strpos($model, 'gemini-') !== 0) {
                $config['model'] = 'gemini-3.5-flash';
            }
            return new Gemini($api_key, $config);
        }

        $model = strtolower((string)$config['model']);
        if ($model === '' || $model === 'easylite' || strpos($model, 'gemini-') === 0 || strpos($model, 'models/gemini-') === 0) {
            $config['model'] = 'gpt-4o-mini';
        }
        return new OpenAI($api_key, $config);
    }

    private function loadKnowledgeSnippets($title, $text, $instruction) {
        try {
            $model_knowledge = new Knowledge($this->registry);
            $query = trim($title . ' ' . $instruction . ' ' . $this->limitChars($text, 500));
            return $model_knowledge->findActiveSnippetsForAi($query, self::MAX_KNOWLEDGE_SNIPPETS, self::MAX_KNOWLEDGE_CHARS);
        } catch (\Throwable $e) {
            return [];
        }
    }

    private function proxyProfileForAiProvider($provider_name) {
        $provider_name = strtolower(trim((string)$provider_name));
        if ($provider_name === 'gemini') {
            return 'ai_gemini';
        }
        if ($provider_name === 'easytokens') {
            return 'ai_easytokens';
        }
        return 'ai_openai';
    }

    private function parseAiJson($raw) {
        $text = $this->extractProviderText($raw);
        if ($text === '') {
            return null;
        }

        $text = trim($text);
        $text = preg_replace('/^```(?:json)?\s*/i', '', $text);
        $text = preg_replace('/\s*```$/', '', $text);

        $json = json_decode($text, true);
        if (!is_array($json) && preg_match('/\{.*\}/su', $text, $match)) {
            $json = json_decode($match[0], true);
        }

        if (!is_array($json)) {
            return null;
        }

        return $json;
    }

    private function plainTextFallback($raw) {
        $text = $this->extractProviderText($raw);
        if ($text === '') {
            return '';
        }

        $text = trim($text);
        $text = preg_replace('/^```(?:text|json)?\s*/i', '', $text);
        $text = preg_replace('/\s*```$/', '', $text);

         
         
        if (preg_match('/^\s*[\{\[]/u', $text)) {
            return '';
        }

        return $this->sanitizeText($text);
    }

    private function extractProviderText($raw) {
        $json = is_array($raw) ? $raw : json_decode((string)$raw, true);
        if (!is_array($json)) {
            return trim((string)$raw);
        }
        if (isset($json['choices'][0]['text'])) {
            return trim((string)$json['choices'][0]['text']);
        }
        if (isset($json['choices'][0]['message']['content'])) {
            return trim((string)$json['choices'][0]['message']['content']);
        }
        if (isset($json['candidates'][0]['content']['parts'][0]['text'])) {
            return trim((string)$json['candidates'][0]['content']['parts'][0]['text']);
        }
        return '';
    }

    private function normalizeProvider($value) {
        $value = (string)$value;
        return in_array($value, ['gemini', 'openai', 'easytokens'], true) ? $value : 'easytokens';
    }

    private function normalizeTask($value) {
        $value = (string)$value;
        return in_array($value, [self::TASK_GENERATE, self::TASK_IMPROVE, self::TASK_SHORTEN, self::TASK_EXPAND], true)
            ? $value
            : self::TASK_IMPROVE;
    }

    private function normalizeStyle($value) {
        $value = (string)$value;
        return in_array($value, [self::STYLE_CURRENT, self::STYLE_FRIENDLY, self::STYLE_BUSINESS, self::STYLE_SALES, self::STYLE_SUPPORT], true)
            ? $value
            : self::STYLE_CURRENT;
    }

    private function temperatureForTask($task) {
        return $task === self::TASK_GENERATE ? 0.45 : 0.25;
    }

    private function sanitizeTitle($value) {
        $value = $this->normalizePlainText($value);
        return $this->limitChars($value, Reply::MAX_TITLE_LENGTH);
    }

    private function sanitizeText($value) {
        $value = str_replace("\0", '', (string)$value);
        $value = strip_tags($value);
        $value = trim($value);
        return $this->limitChars($value, Reply::MAX_TEXT_LENGTH);
    }

    private function normalizePlainText($value) {
        $value = strip_tags((string)$value);
        $value = str_replace("\0", '', $value);
        $value = preg_replace('/[ \t]+/u', ' ', $value);
        $value = preg_replace('/\R{3,}/u', "\n\n", $value);
        return trim($value);
    }

    private function deriveTitle($text) {
        $text = preg_replace('/\s+/u', ' ', trim((string)$text));
        return $this->limitChars($text, Reply::MAX_TITLE_LENGTH);
    }

    private function limitChars($value, $limit) {
        $value = (string)$value;
        $limit = (int)$limit;
        if ($limit <= 0 || mb_strlen($value, 'UTF-8') <= $limit) {
            return $value;
        }
        return rtrim(mb_substr($value, 0, max(1, $limit - 1), 'UTF-8')) . '…';
    }
}
