<?php
// Protect against hack attempts
if (!defined('NGCMS')) die('HAL');

use function Plugins\{logger, sanitize, benchmark, cache_get, cache_put, validate_url};

// Simple HTTP client using cURL
if (!function_exists('ai_rewriter_http_post_json')) {
    function ai_rewriter_http_post_json($url, $headers, $payload, $timeout = 20)
    {
        if (!function_exists('curl_init')) {
            logger('HTTP error: cURL not available', 'error', 'ai_rewriter.log');
            return [false, 'PHP cURL extension is not available', 0, null];
        }

        // Validate URL
        if (!validate_url($url)) {
            logger('HTTP error: invalid URL=' . $url, 'error', 'ai_rewriter.log');
            return [false, 'Invalid URL provided', 0, null];
        }

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(['Content-Type: application/json'], $headers));
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
        $t = max(5, intval($timeout));
        curl_setopt($ch, CURLOPT_TIMEOUT, $t);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, min($t, 15));
        // Follow redirects off by default for APIs

        $startTime = microtime(true);
        $resp = curl_exec($ch);
        $duration = round((microtime(true) - $startTime) * 1000, 2);

        $errno = curl_errno($ch);
        $errmsg = curl_error($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($errno) {
            logger('HTTP error: ' . $errmsg . ', code=' . $code . ', time=' . $duration . 'ms', 'error', 'ai_rewriter.log');
            return [false, 'cURL error: ' . $errmsg, $code, null];
        }

        logger('HTTP success: code=' . $code . ', time=' . $duration . 'ms, size=' . strlen($resp) . ' bytes', 'info', 'ai_rewriter.log');
        return [true, null, $code, $resp];
    }
}

// Provider: OpenAI-compatible chat API
if (!function_exists('ai_rewriter_provider_openai')) {
    function ai_rewriter_provider_openai($model, $apiKey, $apiBase, $systemPrompt, $userPrompt, $temperature = 0.7, $timeout = 20)
    {
        $base = rtrim($apiBase ?: 'https://api.openai.com/v1', '/');
        $url = $base . '/chat/completions';

        logger('OpenAI request: model=' . $model . ', temp=' . $temperature . ', timeout=' . $timeout, 'info', 'ai_rewriter.log');
        $payload = [
            'model' => $model ?: 'gpt-4o-mini',
            'messages' => [
                ['role' => 'system', 'content' => $systemPrompt],
                ['role' => 'user', 'content' => $userPrompt],
            ],
            'temperature' => $temperature,
        ];
        $headers = [
            'Authorization: Bearer ' . $apiKey,
        ];
        list($ok, $err, $code, $resp) = ai_rewriter_http_post_json($url, $headers, $payload, $timeout);
        if (!$ok) {
            logger('OpenAI error: ' . $err, 'error', 'ai_rewriter.log');
            return [false, $err];
        }
        $data = json_decode($resp, true);
        if (!is_array($data)) {
            logger('OpenAI error: invalid JSON', 'error', 'ai_rewriter.log');
            return [false, 'Invalid JSON from provider'];
        }
        if (isset($data['error'])) {
            $errorMsg = is_array($data['error']) ? ($data['error']['message'] ?? 'unknown') : $data['error'];
            logger('OpenAI API error: ' . $errorMsg, 'error', 'ai_rewriter.log');
            return [false, 'API error: ' . $errorMsg];
        }
        $text = $data['choices'][0]['message']['content'] ?? '';
        if (!$text) {
            logger('OpenAI error: empty response', 'error', 'ai_rewriter.log');
            return [false, 'Empty response from provider'];
        }
        // Decode HTML entities that AI might have encoded
        $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        logger('OpenAI success: length=' . mb_strlen($text) . ' chars', 'info', 'ai_rewriter.log');
        return [true, $text];
    }
}

// Provider: Anthropic Messages API
if (!function_exists('ai_rewriter_provider_anthropic')) {
    function ai_rewriter_provider_anthropic($model, $apiKey, $systemPrompt, $userPrompt, $temperature = 0.7, $timeout = 20)
    {
        $url = 'https://api.anthropic.com/v1/messages';

        logger('Anthropic request: model=' . $model . ', temp=' . $temperature . ', timeout=' . $timeout, 'info', 'ai_rewriter.log');
        $payload = [
            'model' => $model ?: 'claude-3-haiku-20240307',
            'max_tokens' => 4096,
            'system' => $systemPrompt,
            'messages' => [
                [
                    'role' => 'user',
                    'content' => [
                        ['type' => 'text', 'text' => $userPrompt],
                    ],
                ],
            ],
            'temperature' => $temperature,
        ];
        $headers = [
            'x-api-key: ' . $apiKey,
            'anthropic-version: 2023-06-01',
        ];
        list($ok, $err, $code, $resp) = ai_rewriter_http_post_json($url, $headers, $payload, $timeout);
        if (!$ok) {
            logger('Anthropic error: ' . $err, 'error', 'ai_rewriter.log');
            return [false, $err];
        }
        $data = json_decode($resp, true);
        if (!is_array($data)) {
            logger('Anthropic error: invalid JSON', 'error', 'ai_rewriter.log');
            return [false, 'Invalid JSON from provider'];
        }
        if (isset($data['error'])) {
            $errorMsg = is_array($data['error']) ? ($data['error']['message'] ?? 'unknown') : $data['error'];
            logger('Anthropic API error: ' . $errorMsg, 'error', 'ai_rewriter.log');
            return [false, 'API error: ' . $errorMsg];
        }
        $parts = $data['content'][0]['text'] ?? '';
        if (!$parts) {
            logger('Anthropic error: empty response', 'error', 'ai_rewriter.log');
            return [false, 'Empty response from provider'];
        }
        // Decode HTML entities that AI might have encoded
        $parts = html_entity_decode($parts, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        logger('Anthropic success: length=' . mb_strlen($parts) . ' chars', 'info', 'ai_rewriter.log');
        return [true, $parts];
    }
}

// Detect language of text
if (!function_exists('ai_rewriter_detect_language')) {
    function ai_rewriter_detect_language($text)
    {
        // Remove HTML/BBCode tags for analysis
        $cleanText = strip_tags($text);
        $cleanText = preg_replace('/\[.*?\]/s', '', $cleanText);
        $cleanText = trim($cleanText);

        // Get first 500 characters for language detection
        $sample = mb_substr($cleanText, 0, 500);

        // Language patterns with specific characters
        $patterns = [
            'ukrainian' => '/[ієїґІЄЇҐ]/u',  // Ukrainian-specific letters (check first!)
            'russian' => '/[а-яёА-ЯЁ]/u',
            'english' => '/[a-zA-Z]/',
            'german' => '/[äöüßÄÖÜẞ]/u',
            'french' => '/[àâæçéèêëïîôùûüÿœÀÂÆÇÉÈÊËÏÎÔÙÛÜŸŒ]/u',
            'spanish' => '/[áéíóúüñ¿¡ÁÉÍÓÚÜÑ]/u',
            'italian' => '/[àèéìíòóùúÀÈÉÌÍÒÓÙÚ]/u',
            'portuguese' => '/[ãáàâçéêíóôõúÃÁÀÂÇÉÊÍÓÔÕÚ]/u',
            'polish' => '/[ąćęłńóśźżĄĆĘŁŃÓŚŹŻ]/u',
        ];

        $scores = [];
        foreach ($patterns as $lang => $pattern) {
            preg_match_all($pattern, $sample, $matches);
            $scores[$lang] = count($matches[0]);
        }

        // Special handling: Ukrainian has unique letters, check first
        if ($scores['ukrainian'] > 0) {
            logger('Language detected: ukrainian (unique letters found)', 'info', 'ai_rewriter.log');
            return 'ukrainian';
        }

        // Get language with highest score
        arsort($scores);
        $detectedLang = key($scores);

        // If no specific characters found, check for basic Latin
        if ($scores[$detectedLang] == 0) {
            if (preg_match('/[a-zA-Z]/', $sample)) {
                logger('Language detected: english (default Latin)', 'info', 'ai_rewriter.log');
                return 'english';
            }
            logger('Language detected: unknown', 'warning', 'ai_rewriter.log');
            return 'unknown';
        }

        logger('Language detected: ' . $detectedLang . ' (score: ' . $scores[$detectedLang] . ')', 'info', 'ai_rewriter.log');
        return $detectedLang;
    }
}

// Core rewrite function
if (!function_exists('ai_rewriter_rewrite')) {
    function ai_rewriter_rewrite($text)
    {
        // Decode HTML entities before sending to AI
        // This converts &nbsp; &quot; etc. to normal characters
        $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');

        // Load config
        pluginsLoadConfig();
        $provider = pluginGetVariable('ai_rewriter', 'provider');
        $apiKey = trim((string)pluginGetVariable('ai_rewriter', 'api_key'));
        $apiBase = trim((string)pluginGetVariable('ai_rewriter', 'api_base'));
        $model = trim((string)pluginGetVariable('ai_rewriter', 'model'));
        $orig = intval(pluginGetVariable('ai_rewriter', 'originality')); // 0..100
        $tone = trim((string)pluginGetVariable('ai_rewriter', 'tone'));
        $temperature = floatval(pluginGetVariable('ai_rewriter', 'temperature'));
        if (!$temperature) {
            $temperature = 0.7;
        }
        $timeout = intval(pluginGetVariable('ai_rewriter', 'timeout'));
        if ($timeout <= 0) {
            $timeout = 20;
        }

        if (!$provider) {
            logger('Rewrite skipped: provider not configured', 'info', 'ai_rewriter.log');
            return [true, $text]; // disabled
        }
        if (!$apiKey && in_array($provider, ['openai', 'openai_compat', 'anthropic'])) {
            logger('Rewrite error: API key missing for provider=' . $provider, 'error', 'ai_rewriter.log');
            return [false, 'AI Rewriter: API ключ не задан'];
        }

        logger('Rewrite started: provider=' . $provider . ', length=' . mb_strlen($text) . ' chars', 'info', 'ai_rewriter.log');

        // Basic model/provider sanity checks for clearer errors
        $m = mb_strtolower($model);
        if ($provider === 'anthropic') {
            if ((strpos($m, 'gpt') !== false) || (strpos($m, 'o1') !== false) || (strpos($m, 'o3') !== false)) {
                $error = 'AI Rewriter: выбран провайдер Anthropic, но указана модель OpenAI (' . $model . '). Задайте модель Claude, например: claude-3-5-sonnet-20240620 или claude-3-haiku-20240307.';
                logger('Config error: ' . $error, 'error', 'ai_rewriter.log');
                return [false, $error];
            }
        }
        if (in_array($provider, ['openai', 'openai_compat'])) {
            if (strpos($m, 'claude') !== false) {
                $error = 'AI Rewriter: выбран провайдер OpenAI, но указана модель Anthropic (' . $model . '). Укажите GPT-модель, например: gpt-4o-mini.';
                logger('Config error: ' . $error, 'error', 'ai_rewriter.log');
                return [false, $error];
            }
        }

        // Get configured default language
        $defaultLang = pluginGetVariable('ai_rewriter', 'default_language') ?: 'auto';

        if ($defaultLang !== 'auto') {
            // If specific language is set in config, always use it (ignore auto-detection)
            $detectedLang = $defaultLang;
            logger('Using configured language: ' . $defaultLang, 'info', 'ai_rewriter.log');
        } else {
            // Auto-detect language only when "auto" is selected
            $detectedLang = ai_rewriter_detect_language($text);
            logger('Auto-detected language: ' . $detectedLang, 'info', 'ai_rewriter.log');
        }

        // Language names for prompts
        $langNames = [
            'russian' => 'русском',
            'ukrainian' => 'украинском',
            'english' => 'английском',
            'german' => 'немецком',
            'french' => 'французском',
            'spanish' => 'испанском',
            'italian' => 'итальянском',
            'portuguese' => 'португальском',
            'polish' => 'польском',
            'unknown' => 'исходном'
        ];

        $langName = $langNames[$detectedLang] ?? 'исходном';

        // Get system prompt from config (with default)
        $defaultPrompt = 'Ты профессиональный редактор и копирайтер. ' .
            'Переписывай текст сохраняя смысл, факты, структуру и разметку. ' .
            'Строго сохраняй HTML-теги, BBCode, ссылки, URL, кавычки, номера и знаки препинания. ' .
            'НЕ используй HTML-сущности типа &nbsp; &quot; &amp; - используй обычные символы. ' .
            'Не добавляй фактов, не удаляй важный смысл. ' .
            'ВАЖНО: Переписанный текст должен быть на {язык} языке, сохраняя язык оригинала.';

        $systemPromptTemplate = pluginGetVariable('ai_rewriter', 'system_prompt') ?: $defaultPrompt;

        // Replace {язык} placeholder with detected language
        $sys = str_replace('{язык}', $langName . ' языке', $systemPromptTemplate);

        // Build user prompt
        $req = 'Перепиши следующий текст с целевой уникальностью ~' . max(0, min(100, $orig ?: 60)) . '%. ' .
            ($tone ? ('Тон: ' . $tone . '. ') : '') .
            'Сохрани разметку (HTML/BBCode), заголовки, абзацы и ссылки без изменений. ' .
            'Текст должен остаться на ' . $langName . ' языке. ' .
            'Ничего не комментируй, верни только итоговый текст.

=== ТЕКСТ ДЛЯ РЕРАЙТА ===
' . $text;

        switch ($provider) {
            case 'openai':
            case 'openai_compat':
                return ai_rewriter_provider_openai($model, $apiKey, $apiBase, $sys, $req, $temperature, $timeout);
            case 'anthropic':
                return ai_rewriter_provider_anthropic($model, $apiKey, $sys, $req, $temperature, $timeout);
            default:
                return [false, 'AI Rewriter: неизвестный провайдер'];
        }
    }
}

class AIRewriterNewsFilter extends NewsFilter
{
    // For add form UI extension (optional)
    public function addNewsForm(&$tvars)
    {
        return 1;
    }

    // For edit form UI extension (optional)
    public function editNewsForm($newsID, $SQLnews, &$tvars)
    {
        return 1;
    }

    // Hook called BEFORE adding news (can modify $SQL)
    public function addNews(&$tvars, &$SQL)
    {
        pluginsLoadConfig();
        $enabled = intval(pluginGetVariable('ai_rewriter', 'enable_on_add'));
        $force = isset($_REQUEST['ai_rewrite_now']) && ($_REQUEST['ai_rewrite_now'] == '1');
        if (!$enabled && !$force) {
            return 1;
        }

        $content = $SQL['content'] ?? '';
        if (!mb_strlen(trim($content))) {
            return 1;
        }

        // If split editor used, keep delimiter but allow full-text rewrite
        $parts = preg_split('#(<!--more(?:=.*?)?-->)#si', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
        $joined = $content;
        // Strategy: rewrite entire content in one go to preserve style
        list($ok, $res) = ai_rewriter_rewrite($joined);
        if ($ok) {
            $SQL['content'] = $res;
            logger('News add: rewritten successfully', 'info', 'ai_rewriter.log');
        } else {
            logger('News add: rewrite failed - ' . $res, 'error', 'ai_rewriter.log');
            // Non-blocking: keep original, show admin message
            if (function_exists('msg')) {
                msg(['type' => 'error', 'text' => $res]);
            }
        }
        return 1;
    }

    // Hook called BEFORE editing news (can modify $SQL)
    public function editNews($newsID, $SQLnews, &$SQLnew, &$tvars)
    {
        pluginsLoadConfig();
        $enabled = intval(pluginGetVariable('ai_rewriter', 'enable_on_edit'));
        $force = isset($_REQUEST['ai_rewrite_now']) && ($_REQUEST['ai_rewrite_now'] == '1');
        if (!$enabled && !$force) {
            return 1;
        }

        $content = $SQLnew['content'] ?? '';
        if (!mb_strlen(trim($content))) {
            return 1;
        }

        list($ok, $res) = ai_rewriter_rewrite($content);
        if ($ok) {
            $SQLnew['content'] = $res;
            logger('News edit: newsID=' . $newsID . ', rewritten successfully', 'info', 'ai_rewriter.log');
        } else {
            logger('News edit: newsID=' . $newsID . ', rewrite failed - ' . $res, 'error', 'ai_rewriter.log');
            if (function_exists('msg')) {
                msg(['type' => 'error', 'text' => $res]);
            }
        }
        return 1;
    }
}

// Register filter
register_filter('news', 'ai_rewriter', new AIRewriterNewsFilter);

// RPC: rewrite preview (no save)
function ai_rewriter_rpc_rewrite($params = null)
{
    // Security: rpcRegisterFunction(..., true) требует авторизации администратора
    // Дополнительная проверка не требуется - встроенная защита NGCMS

    // Text can come as POST variable or as `$params['text']`
    $text = '';
    if (is_array($params) && isset($params['text'])) {
        $text = sanitize((string)$params['text'], 'html');
    } elseif (isset($_POST['text'])) {
        $text = sanitize((string)$_POST['text'], 'html');
    }
    if (!mb_strlen(trim($text))) {
        logger('RPC error: empty text', 'warning', 'ai_rewriter.log');
        return ['status' => 0, 'errorCode' => 100, 'errorText' => 'Пустой текст'];
    }
    logger('RPC rewrite request: length=' . mb_strlen($text) . ' chars', 'info', 'ai_rewriter.log');
    list($ok, $res) = ai_rewriter_rewrite($text);
    if ($ok) {
        logger('RPC success: result length=' . mb_strlen($res) . ' chars', 'info', 'ai_rewriter.log');
        return ['status' => 1, 'errorCode' => 0, 'text' => $res];
    }
    logger('RPC error: ' . $res, 'error', 'ai_rewriter.log');
    return ['status' => 0, 'errorCode' => 101, 'errorText' => $res];
}

rpcRegisterFunction('ai_rewriter.rewrite', 'ai_rewriter_rpc_rewrite', true);
