Black Russia Forum Enhanced AI Helper (v0.6)

Modern UI + Context Menu AI Features (Summarize, Rephrase, Grammar) for forum.blackrussia.online using Google Gemini

// ==UserScript==
// @name         Black Russia Forum Enhanced AI Helper (v0.6)
// @namespace    http://tampermonkey.net/
// @version      0.8
// @description  Modern UI + Context Menu AI Features (Summarize, Rephrase, Grammar) for forum.blackrussia.online using Google Gemini
// @author       M. Ageev (Gemini AI)
// @match        https://forum.blackrussia.online/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      generativelanguage.googleapis.com
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration for Google Gemini API ---
    const API_ENDPOINT_BASE = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent'; // Using Flash for speed/cost
    const API_METHOD = 'POST';
    const STORAGE_KEY = 'br_gemini_api_key_v2'; // Use a new key for potentially different structure needs

    // --- State Variables ---
    let apiKey = GM_getValue(STORAGE_KEY, '');
    let apiStatus = 'Not Configured';
    let settingsPanelVisible = false;
    let contextMenuVisible = false;
    let resultModalVisible = false;
    let currentSelection = null; // Store currently selected text info

    // --- DOM Elements (created later) ---
    let settingsPanel, toggleIcon, contextMenu, resultModal, resultModalContent, resultModalCopyBtn, resultModalCloseBtn;

    // --- Helper Functions (API Interaction - slightly modified) ---

    function constructApiRequestData(prompt, task = 'generate') {
        // Customize safety/generation settings based on task if needed
        let generationConfig = {
            temperature: 0.7, // Balanced default
            maxOutputTokens: 2048,
        };
        if (task === 'summarize') generationConfig.temperature = 0.5;
        if (task === 'rephrase') generationConfig.temperature = 0.8;
        if (task === 'grammar') generationConfig.temperature = 0.3; // More deterministic for grammar

        return JSON.stringify({
            contents: [{ parts: [{ text: prompt }] }],
            generationConfig: generationConfig,
            safetySettings: [
                { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_MEDIUM_AND_ABOVE" },
                { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_MEDIUM_AND_ABOVE" },
                { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_MEDIUM_AND_ABOVE" },
                { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_MEDIUM_AND_ABOVE" }
            ]
        });
    }

    function parseApiResponse(responseDetails) {
        // (Same parsing logic as before - checking candidates, promptFeedback, errors)
        try {
            const response = JSON.parse(responseDetails.responseText);
            if (response.candidates && response.candidates.length > 0 && response.candidates[0].content?.parts?.[0]?.text) {
                return response.candidates[0].content.parts[0].text.trim();
            } else if (response.promptFeedback?.blockReason) {
                const reason = response.promptFeedback.blockReason;
                console.warn('Gemini AI Helper: Prompt blocked due to:', reason, response.promptFeedback.safetyRatings);
                let blockMessage = `(AI-ответ заблокирован: ${reason})`;
                const harmfulRating = response.promptFeedback.safetyRatings?.find(r => r.probability !== 'NEGLIGIBLE' && r.probability !== 'LOW');
                if(harmfulRating) blockMessage += ` - Причина: ${harmfulRating.category.replace('HARM_CATEGORY_', '')}`;
                return blockMessage;
            } else if (response.candidates?.[0]?.finishReason === 'SAFETY') {
                console.warn('Gemini AI Helper: Response content blocked due to safety settings.', response.candidates[0].safetyRatings);
                return `(AI-ответ заблокирован из-за настроек безопасности)`;
            } else {
                console.error('Gemini AI Helper: Unexpected Gemini API response format:', response);
                 // Try to extract Google's error message if available
                 if (response.error && response.error.message) {
                     return `(Ошибка API: ${response.error.message})`;
                 }
                return null;
            }
        } catch (error) {
            console.error('Gemini AI Helper: Error parsing Gemini API response:', error, responseDetails.responseText);
            try {
                const errResponse = JSON.parse(responseDetails.responseText);
                if (errResponse.error && errResponse.error.message) return `(Ошибка API: ${errResponse.error.message})`;
            } catch (e) { /* Ignore inner parse error */ }
            return null;
        }
    }

    function updateStatusDisplay(newStatus, message = '') {
        apiStatus = newStatus;
        const statusEl = document.getElementById('br-ai-status');
        const messageEl = document.getElementById('br-ai-status-message');
        if (statusEl) {
            let icon = '❓'; // Default: Unknown
            if (newStatus === 'Working') icon = '✅'; // Green check
            else if (newStatus === 'Error' || newStatus === 'Not Configured') icon = '❌'; // Red cross
            else if (newStatus === 'Checking' || newStatus === 'Saving...') icon = '⏳'; // Hourglass

            statusEl.innerHTML = `${icon} Статус: ${apiStatus}`; // Use innerHTML for icon
            statusEl.className = `status-${apiStatus.toLowerCase().replace(/[^a-z0-9-]/g, '-')}`;
        }
        if (messageEl) {
            messageEl.textContent = message;
            messageEl.style.display = message ? 'block' : 'none';
        }
    }

    function saveApiKey() {
        const inputEl = document.getElementById('br-ai-api-key-input');
        if (inputEl) {
            const newApiKey = inputEl.value.trim();
            // Basic validation removed for brevity, add back if desired
            updateStatusDisplay('Saving...');
            GM_setValue(STORAGE_KEY, newApiKey);
            apiKey = newApiKey;
            console.log('Gemini AI Helper: API Key saved.');
            checkApiKey(); // Check the new key
        }
    }

    function checkApiKey(silent = false) { // Add silent option to avoid spamming alerts
        return new Promise((resolve) => { // Return a promise to know when check is done
            if (!apiKey) {
                updateStatusDisplay('Not Configured', 'API ключ не введен.');
                return resolve(false);
            }
            if (!API_ENDPOINT_BASE) {
                updateStatusDisplay('Error', 'API Endpoint не настроен в скрипте!');
                 return resolve(false);
            }

            updateStatusDisplay('Checking...', silent ? '' : 'Отправка тестового запроса...');
            const testPrompt = "Привет! Просто ответь 'OK'.";
            const requestData = constructApiRequestData(testPrompt, 'test');
            const fullApiUrl = `${API_ENDPOINT_BASE}?key=${apiKey}`;

            GM_xmlhttpRequest({
                method: API_METHOD, url: fullApiUrl,
                headers: { 'Content-Type': 'application/json' },
                data: requestData, timeout: 15000,
                onload: function(response) {
                    let success = false;
                    if (response.status === 200) {
                        const testResult = parseApiResponse(response);
                        if (testResult !== null && !testResult.startsWith('(')) { // Check it's not an error/blocked message
                            updateStatusDisplay('Working', 'API ключ работает.');
                            success = true;
                        } else if (testResult !== null) {
                            updateStatusDisplay('Error', `Ключ принят, но ошибка: ${testResult}`);
                        } else {
                            updateStatusDisplay('Error', 'Ключ работает, но не удалось обработать ответ API.');
                        }
                    } else {
                        let errorMsg = `Ошибка API (Статус: ${response.status}). Проверьте ключ.`;
                        try { const errResp = JSON.parse(response.responseText); if(errResp.error?.message) errorMsg = `Ошибка API: ${errResp.error.message}`; } catch(e){}
                        updateStatusDisplay('Error', errorMsg);
                        console.error('Gemini AI Helper: Key check fail - Status:', response.status, response.responseText);
                    }
                    resolve(success);
                },
                onerror: function(response) {
                    updateStatusDisplay('Error', 'Ошибка сети или CORS. Проверьте @connect.');
                    console.error('Gemini AI Helper: Key check fail - Network error:', response);
                    resolve(false);
                },
                ontimeout: function() {
                    updateStatusDisplay('Error', 'Тайм-аут запроса к API.');
                    console.error('Gemini AI Helper: Key check fail - Timeout.');
                    resolve(false);
                }
            });
        });
    }

    // --- Core AI Call Function (Modified for Loading Indicator) ---
     function callAI(prompt, task = 'generate', loadingIndicatorElement = null) {
        return new Promise(async (resolve) => { // Return promise
            if (apiStatus !== 'Working') {
                // Maybe try a silent check?
                const keyOk = await checkApiKey(true); // Silent check
                if (!keyOk) {
                    alert('Gemini AI Helper: API не настроен или не работает. Проверьте настройки.');
                    if (loadingIndicatorElement) loadingIndicatorElement.disabled = false;
                    return resolve(null); // Resolve with null on failure
                }
                // If check passed, status is now 'Working', continue
            }
            if (!prompt) {
                 alert('Gemini AI Helper: Нет текста для отправки AI.');
                 if (loadingIndicatorElement) loadingIndicatorElement.disabled = false;
                 return resolve(null);
            }

            console.log(`Gemini AI Helper: Calling Gemini for task '${task}'`);
            if (loadingIndicatorElement) loadingIndicatorElement.disabled = true; // Disable button/element

            const requestData = constructApiRequestData(prompt, task);
            const fullApiUrl = `${API_ENDPOINT_BASE}?key=${apiKey}`;

            GM_xmlhttpRequest({
                method: API_METHOD, url: fullApiUrl,
                headers: { 'Content-Type': 'application/json' },
                data: requestData, timeout: 90000, // Increased timeout for potentially longer tasks
                onload: function(response) {
                    let result = null;
                    if (response.status === 200) {
                        result = parseApiResponse(response);
                        if (result === null) { // Explicit parse error
                             alert('Gemini AI Helper: Ошибка обработки ответа от AI.');
                        }
                    } else {
                        let errorMsg = `Ошибка API (Статус: ${response.status}).`;
                         try { const errResp = JSON.parse(response.responseText); if(errResp.error?.message) errorMsg = `Ошибка API: ${errResp.error.message}`; } catch(e){}
                        alert(`Gemini AI Helper: ${errorMsg}`);
                        console.error('Gemini AI Helper: AI call fail - Status:', response.status, response.responseText);
                        result = `(Сетевая ошибка: ${response.status})`; // Return error info
                    }
                    if (loadingIndicatorElement) loadingIndicatorElement.disabled = false;
                    resolve(result); // Resolve promise with result (string or null)
                },
                onerror: function(response) {
                    alert('Gemini AI Helper: Ошибка сети при вызове AI.');
                    console.error('Gemini AI Helper: AI call fail - Network error:', response);
                    if (loadingIndicatorElement) loadingIndicatorElement.disabled = false;
                    resolve('(Сетевая ошибка)');
                },
                ontimeout: function() {
                    alert('Gemini AI Helper: Тайм-аут при вызове AI.');
                    console.error('Gemini AI Helper: AI call fail - Timeout.');
                    if (loadingIndicatorElement) loadingIndicatorElement.disabled = false;
                     resolve('(Тайм-аут)');
                },
            });
        });
    }


    // --- UI Creation ---

    /** Inject CSS */
    function injectStyles() {
        GM_addStyle(`
            /* --- CSS Variables (Theme) --- */
            :root {
                --ai-bg-primary: #2d2d2d;
                --ai-bg-secondary: #3a3a3a;
                --ai-bg-tertiary: #454545;
                --ai-text-primary: #e0e0e0;
                --ai-text-secondary: #b0b0b0;
                --ai-accent-primary: #00aaff; /* Bright blue */
                --ai-accent-secondary: #00cfaa; /* Teal */
                --ai-success: #4CAF50;
                --ai-error: #F44336;
                --ai-warning: #FFC107;
                --ai-info: #2196F3;
                --ai-border-color: #555555;
                --ai-border-radius: 6px;
                --ai-font-family: 'Roboto', 'Segoe UI', sans-serif; /* Modern font stack */
                --ai-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
                --ai-transition: all 0.25s ease-in-out;
            }

            /* --- Toggle Icon --- */
            #br-ai-toggle-icon {
                position: fixed; bottom: 25px; right: 25px; width: 50px; height: 50px;
                background: linear-gradient(135deg, var(--ai-accent-primary), var(--ai-accent-secondary));
                color: white; border-radius: 50%;
                display: flex; align-items: center; justify-content: center;
                font-size: 26px; cursor: pointer; z-index: 10000;
                box-shadow: var(--ai-shadow); user-select: none;
                transition: var(--ai-transition), transform 0.15s ease;
                border: none;
            }
            #br-ai-toggle-icon:hover { transform: scale(1.1); box-shadow: 0 6px 16px rgba(0, 180, 220, 0.4); }
            #br-ai-toggle-icon:active { transform: scale(0.95); }

            /* --- Settings Panel --- */
            #br-ai-settings-panel {
                position: fixed; bottom: 90px; right: 25px; width: 350px;
                background-color: var(--ai-bg-primary); color: var(--ai-text-primary);
                border: 1px solid var(--ai-border-color); border-radius: var(--ai-border-radius);
                padding: 0; /* Remove padding, handle inside */
                z-index: 10001; box-shadow: var(--ai-shadow);
                font-family: var(--ai-font-family); font-size: 14px;
                overflow: hidden; /* Needed for border-radius with tabs */
                transform: translateY(10px) scale(0.98); opacity: 0; /* Initial state for transition */
                transition: var(--ai-transition), transform 0.2s ease, opacity 0.2s ease;
                display: none; /* Controlled by JS */
            }
            #br-ai-settings-panel.visible {
                 display: block;
                 transform: translateY(0) scale(1);
                 opacity: 1;
            }

            /* Panel Tabs */
            .br-ai-panel-tabs {
                display: flex;
                background-color: var(--ai-bg-secondary);
                border-bottom: 1px solid var(--ai-border-color);
            }
            .br-ai-panel-tab {
                flex: 1;
                padding: 12px 15px;
                text-align: center;
                cursor: pointer;
                color: var(--ai-text-secondary);
                border-bottom: 3px solid transparent;
                transition: var(--ai-transition);
                font-weight: 500;
            }
            .br-ai-panel-tab:hover { background-color: var(--ai-bg-tertiary); color: var(--ai-text-primary); }
            .br-ai-panel-tab.active {
                color: var(--ai-text-primary);
                border-bottom-color: var(--ai-accent-primary);
            }

            /* Panel Content Area */
            .br-ai-panel-content { padding: 20px; }
            .br-ai-tab-pane { display: none; }
            .br-ai-tab-pane.active { display: block; }

            /* Form Elements */
            #br-ai-settings-panel h3 { margin-top: 0; margin-bottom: 15px; font-size: 18px; font-weight: 500; color: var(--ai-text-primary); padding-bottom: 8px; border-bottom: 1px solid var(--ai-border-color); }
            #br-ai-settings-panel label { display: block; margin-bottom: 6px; font-weight: 500; color: var(--ai-text-secondary); }
            #br-ai-api-key-input {
                width: 100%;
                padding: 10px 12px;
                margin-bottom: 15px;
                background-color: var(--ai-bg-tertiary);
                color: var(--ai-text-primary);
                border: 1px solid var(--ai-border-color);
                border-radius: var(--ai-border-radius);
                font-size: 14px;
                box-sizing: border-box; /* Include padding in width */
                 transition: var(--ai-transition);
            }
             #br-ai-api-key-input:focus { border-color: var(--ai-accent-primary); box-shadow: 0 0 0 2px rgba(0, 170, 255, 0.3); outline: none; }

            .br-ai-button-group { display: flex; gap: 10px; margin-top: 10px; }
            #br-ai-settings-panel button {
                flex-grow: 1; /* Make buttons fill space */
                padding: 10px 15px;
                font-size: 14px;
                font-weight: 500;
                border: none; border-radius: var(--ai-border-radius);
                cursor: pointer; transition: var(--ai-transition);
                display: flex; align-items: center; justify-content: center; gap: 5px;
            }
            #br-ai-save-key { background-color: var(--ai-success); color: white; }
            #br-ai-test-key { background-color: var(--ai-info); color: white; }
            #br-ai-settings-panel button:hover { filter: brightness(1.1); }
            #br-ai-settings-panel button:active { filter: brightness(0.9); transform: scale(0.98); }
            #br-ai-settings-panel button:disabled { background-color: var(--ai-bg-tertiary); color: var(--ai-text-secondary); cursor: not-allowed; }

            /* Status Display */
            #br-ai-status { margin-top: 20px; font-weight: 500; display: flex; align-items: center; gap: 8px; }
            #br-ai-status-message { margin-top: 8px; font-size: 13px; color: var(--ai-text-secondary); word-wrap: break-word; line-height: 1.4; }
            /* Status colors */
            .status-not-configured, .status-error { color: var(--ai-error); }
            .status-checking, .status-saving { color: var(--ai-warning); }
            .status-working { color: var(--ai-success); }

             /* About Tab */
            #br-ai-about p { margin-bottom: 10px; line-height: 1.5; color: var(--ai-text-secondary); }
            #br-ai-about strong { color: var(--ai-text-primary); }
            #br-ai-about a { color: var(--ai-accent-primary); text-decoration: none; }
            #br-ai-about a:hover { text-decoration: underline; }
            #br-ai-about ul { list-style: disc; padding-left: 25px; margin-top: 10px;}
            #br-ai-about li { margin-bottom: 5px;}

            /* --- Context Menu --- */
            #br-ai-context-menu {
                position: absolute; /* Positioned by JS */
                min-width: 180px;
                background-color: var(--ai-bg-secondary);
                border: 1px solid var(--ai-border-color);
                border-radius: var(--ai-border-radius);
                box-shadow: var(--ai-shadow);
                z-index: 10005; /* Above panel */
                padding: 5px 0;
                font-family: var(--ai-font-family);
                font-size: 14px;
                opacity: 0; /* Start hidden */
                transform: scale(0.95);
                transition: opacity 0.15s ease, transform 0.15s ease;
                display: none; /* Controlled by JS */
            }
            #br-ai-context-menu.visible { display: block; opacity: 1; transform: scale(1); }
            .br-ai-context-menu-item {
                display: flex; /* Use flex for icon alignment */
                align-items: center;
                gap: 8px;
                padding: 8px 15px;
                color: var(--ai-text-primary);
                cursor: pointer;
                transition: background-color 0.15s ease;
            }
            .br-ai-context-menu-item:hover { background-color: var(--ai-bg-tertiary); }
            .br-ai-context-menu-item span { flex-grow: 1; } /* Text takes remaining space */
            .br-ai-context-menu-item i { /* Basic icon styling */
                 font-style: normal;
                 width: 1.2em; /* Ensure consistent icon space */
                 text-align: center;
                 color: var(--ai-accent-secondary);
            }

             /* --- Result Modal --- */
            #br-ai-result-modal {
                position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.95);
                width: 90%; max-width: 600px;
                background-color: var(--ai-bg-primary);
                color: var(--ai-text-primary);
                border: 1px solid var(--ai-border-color);
                border-radius: var(--ai-border-radius);
                box-shadow: var(--ai-shadow);
                z-index: 10010; /* Above context menu */
                font-family: var(--ai-font-family);
                opacity: 0;
                transition: opacity 0.25s ease, transform 0.25s ease;
                display: none; /* Controlled by JS */
            }
            #br-ai-result-modal.visible { display: block; opacity: 1; transform: translate(-50%, -50%) scale(1); }
            .br-ai-modal-header {
                display: flex; justify-content: space-between; align-items: center;
                padding: 12px 15px;
                background-color: var(--ai-bg-secondary);
                border-bottom: 1px solid var(--ai-border-color);
                border-top-left-radius: var(--ai-border-radius); /* Match parent */
                border-top-right-radius: var(--ai-border-radius);
            }
             .br-ai-modal-header h4 { margin: 0; font-size: 16px; font-weight: 500; }
             #br-ai-result-modal-close { background: none; border: none; color: var(--ai-text-secondary); font-size: 20px; cursor: pointer; padding: 0 5px; transition: color 0.15s ease; }
             #br-ai-result-modal-close:hover { color: var(--ai-text-primary); }
             .br-ai-modal-content {
                padding: 15px;
                max-height: 60vh; /* Limit height and allow scrolling */
                overflow-y: auto;
                font-size: 14px;
                line-height: 1.6;
                white-space: pre-wrap; /* Preserve whitespace and newlines */
                background-color: var(--ai-bg-tertiary); /* Slightly different bg for content */
                margin: 15px; /* Add margin around content */
                border-radius: 4px;
            }
             /* Style scrollbar for modal content */
            .br-ai-modal-content::-webkit-scrollbar { width: 8px; }
            .br-ai-modal-content::-webkit-scrollbar-track { background: var(--ai-bg-secondary); border-radius: 4px; }
            .br-ai-modal-content::-webkit-scrollbar-thumb { background: var(--ai-border-color); border-radius: 4px; }
            .br-ai-modal-content::-webkit-scrollbar-thumb:hover { background: #6b6b6b; }

             .br-ai-modal-footer {
                padding: 10px 15px;
                text-align: right;
                border-top: 1px solid var(--ai-border-color);
            }
             #br-ai-result-modal-copy {
                padding: 8px 15px;
                background-color: var(--ai-accent-primary);
                color: white; border: none;
                border-radius: var(--ai-border-radius);
                cursor: pointer; font-size: 14px; font-weight: 500;
                transition: var(--ai-transition);
            }
            #br-ai-result-modal-copy:hover { filter: brightness(1.1); }
            #br-ai-result-modal-copy:active { filter: brightness(0.9); }

            /* Editor Button Styling (from previous version, maybe adjusted) */
            .br-ai-editor-button {
                margin-left: 8px; padding: 5px 10px !important; font-size: 13px !important;
                line-height: 1.5 !important; cursor: pointer; background-color: var(--ai-accent-secondary); /* Teal */
                color: white; border: none; border-radius: 4px; transition: background-color 0.2s ease;
                display: inline-flex; align-items: center; gap: 4px; /* Align icon/text */
            }
            .br-ai-editor-button:hover { filter: brightness(1.1); }
            .br-ai-editor-button:disabled { background-color: #aaa; cursor: not-allowed; filter: grayscale(50%); }

        `);
    }

    /** Creates the main settings panel with tabs */
    function createSettingsPanel() {
        // --- Toggle Icon ---
        toggleIcon = document.createElement('div');
        toggleIcon.id = 'br-ai-toggle-icon';
        toggleIcon.innerHTML = '✨'; // Sparkles icon
        toggleIcon.title = 'Настройки Gemini AI Помощника';
        document.body.appendChild(toggleIcon);

        // --- Settings Panel ---
        settingsPanel = document.createElement('div');
        settingsPanel.id = 'br-ai-settings-panel';
        settingsPanel.innerHTML = `
            <div class="br-ai-panel-tabs">
                <div class="br-ai-panel-tab active" data-tab="settings">Настройки</div>
                <div class="br-ai-panel-tab" data-tab="about">О скрипте</div>
            </div>

            <div class="br-ai-panel-content">
                <div class="br-ai-tab-pane active" id="br-ai-settings">
                    <h3>Настройки Gemini AI</h3>
                    <p style="font-size:11px; color:var(--ai-warning); margin-bottom:10px;"><b>Важно:</b> Никогда не делитесь API ключом! <a href="https://aistudio.google.com/app/apikey" target="_blank" style="color:var(--ai-accent-primary);">Управлять ключами</a></p>
                    <label for="br-ai-api-key-input">Ваш Google AI API Ключ:</label>
                    <input type="password" id="br-ai-api-key-input" placeholder="Введите ваш API ключ (начинается с AIza...)">
                    <div class="br-ai-button-group">
                         <button id="br-ai-save-key" title="Сохранить ключ и проверить">💾 Сохранить</button>
                         <button id="br-ai-test-key" title="Проверить текущий сохраненный ключ">📡 Проверить</button>
                    </div>
                    <div id="br-ai-status">Статус: Инициализация...</div>
                    <div id="br-ai-status-message"></div>
                </div>

                <div class="br-ai-tab-pane" id="br-ai-about">
                    <h3>О скрипте Enhanced AI Helper</h3>
                    <p><strong>Версия:</strong> 0.6</p>
                    <p>Этот скрипт добавляет функции Google Gemini AI на форум Black Russia:</p>
                    <ul>
                        <li>Генерация текста в редакторе (кнопка ✨ AI).</li>
                        <li>Контекстное меню (при выделении текста):
                            <ul>
                                <li>📝 Суммаризировать</li>
                                <li>🔄 Перефразировать</li>
                                <li>✅ Исправить грамматику</li>
                            </ul>
                        </li>
                        <li>Современная панель настроек API ключа.</li>
                    </ul>
                     <p>Создано с помощью AI и адаптировано.</p>
                    <p><strong>Внимание:</strong> Использование API может быть платным. Ответственность за использование ключа лежит на вас.</p>
                </div>
            </div>
        `;
        document.body.appendChild(settingsPanel);

        // --- Event Listeners ---
        toggleIcon.addEventListener('click', toggleSettingsPanel);

        // Tab switching logic
        settingsPanel.querySelectorAll('.br-ai-panel-tab').forEach(tab => {
            tab.addEventListener('click', () => {
                settingsPanel.querySelector('.br-ai-panel-tab.active').classList.remove('active');
                settingsPanel.querySelector('.br-ai-tab-pane.active').classList.remove('active');
                tab.classList.add('active');
                settingsPanel.querySelector(`#br-ai-${tab.dataset.tab}`).classList.add('active');
            });
        });

        // Button listeners
        document.getElementById('br-ai-save-key').addEventListener('click', saveApiKey);
        document.getElementById('br-ai-test-key').addEventListener('click', () => checkApiKey()); // Non-silent check on button press

        // Load saved key
        document.getElementById('br-ai-api-key-input').value = apiKey;
    }

    /** Toggles the settings panel visibility */
    function toggleSettingsPanel() {
        settingsPanelVisible = !settingsPanelVisible;
        if (settingsPanelVisible) {
            settingsPanel.classList.add('visible');
            // Refresh status on open
             updateStatusDisplay(apiStatus, document.getElementById('br-ai-status-message')?.textContent || '');
        } else {
             settingsPanel.classList.remove('visible');
        }
    }

     /** Creates the custom context menu (initially hidden) */
    function createContextMenu() {
        contextMenu = document.createElement('div');
        contextMenu.id = 'br-ai-context-menu';
        contextMenu.innerHTML = `
            <div class="br-ai-context-menu-item" data-action="summarize"><i>📝</i> <span>Суммаризировать</span></div>
            <div class="br-ai-context-menu-item" data-action="rephrase"><i>🔄</i> <span>Перефразировать</span></div>
            <div class="br-ai-context-menu-item" data-action="grammar"><i>✅</i> <span>Исправить грамматику</span></div>
        `;
        document.body.appendChild(contextMenu);

        // Add listener for menu item clicks
        contextMenu.addEventListener('click', handleContextMenuAction);
    }

     /** Creates the result modal (initially hidden) */
    function createResultModal() {
        resultModal = document.createElement('div');
        resultModal.id = 'br-ai-result-modal';
        resultModal.innerHTML = `
            <div class="br-ai-modal-header">
                <h4>Результат AI</h4>
                <button id="br-ai-result-modal-close" title="Закрыть">&times;</button>
            </div>
            <div class="br-ai-modal-content" id="br-ai-result-modal-content">
                Загрузка...
            </div>
            <div class="br-ai-modal-footer">
                <button id="br-ai-result-modal-copy" title="Копировать результат">📋 Копировать</button>
            </div>
        `;
        document.body.appendChild(resultModal);

        resultModalContent = document.getElementById('br-ai-result-modal-content');
        resultModalCopyBtn = document.getElementById('br-ai-result-modal-copy');
        resultModalCloseBtn = document.getElementById('br-ai-result-modal-close');

        resultModalCloseBtn.addEventListener('click', hideResultModal);
        resultModalCopyBtn.addEventListener('click', copyModalContent);

        // Optional: Close modal on clicking outside
         resultModal.addEventListener('click', (e) => {
             if (e.target === resultModal) { // Check if click is on the backdrop itself
                 hideResultModal();
             }
         });
    }

    /** Shows the custom context menu */
    function showContextMenu(x, y) {
        // Hide any previous instances immediately
        hideContextMenu();
        hideResultModal(); // Also hide modal if open

        contextMenu.style.left = `${x}px`;
        contextMenu.style.top = `${y}px`;
        contextMenu.classList.add('visible');
        contextMenuVisible = true;
    }

    /** Hides the custom context menu */
    function hideContextMenu() {
        if (contextMenu) {
             contextMenu.classList.remove('visible');
        }
        contextMenuVisible = false;
    }

     /** Shows the result modal with content */
    function showResultModal(content, title = "Результат AI") {
        hideContextMenu(); // Hide context menu when showing modal
         if (!resultModal) createResultModal(); // Create if it doesn't exist yet

        resultModal.querySelector('.br-ai-modal-header h4').textContent = title;
        resultModalContent.textContent = content || "Не удалось получить результат."; // Handle null content
        resultModal.classList.add('visible');
        resultModalVisible = true;
    }

     /** Hides the result modal */
    function hideResultModal() {
         if (resultModal) {
            resultModal.classList.remove('visible');
        }
        resultModalVisible = false;
    }

    /** Copies the content of the result modal to clipboard */
    function copyModalContent() {
        if (resultModalContent) {
            navigator.clipboard.writeText(resultModalContent.textContent)
                .then(() => {
                    // Optional: Show feedback like "Copied!"
                    resultModalCopyBtn.textContent = '✅ Скопировано!';
                    setTimeout(() => { resultModalCopyBtn.textContent = '📋 Копировать'; }, 1500);
                })
                .catch(err => {
                    console.error('Gemini AI Helper: Failed to copy text: ', err);
                    alert('Не удалось скопировать текст.');
                });
        }
    }

    /** Handles clicks on context menu items */
    async function handleContextMenuAction(event) {
        const menuItem = event.target.closest('.br-ai-context-menu-item');
        if (!menuItem || !currentSelection?.text) {
             hideContextMenu();
             return;
        }

        const action = menuItem.dataset.action;
        const selectedText = currentSelection.text;
        let prompt = '';
        let taskTitle = '';

         // Change button text to indicate loading
         menuItem.innerHTML = `<i>⏳</i> <span>Обработка...</span>`;
         hideContextMenu(); // Hide menu immediately after click

        switch (action) {
            case 'summarize':
                prompt = `Создай краткое содержание (summary) следующего текста:\n\n---\n${selectedText}\n---`;
                taskTitle = "Суммаризация текста";
                break;
            case 'rephrase':
                prompt = `Перефразируй следующий текст, сохранив основной смысл, но используя другие слова и структуру предложений:\n\n---\n${selectedText}\n---`;
                 taskTitle = "Перефразирование текста";
                break;
            case 'grammar':
                prompt = `Проверь и исправь грамматику, орфографию и пунктуацию в следующем тексте. Верни только исправленный текст без дополнительных комментариев:\n\n---\n${selectedText}\n---`;
                taskTitle = "Исправление грамматики";
                break;
            default:
                // Restore original text if action not found (shouldn't happen)
                menuItem.innerHTML = {
                    summarize: '<i>📝</i> <span>Суммаризировать</span>',
                    rephrase: '<i>🔄</i> <span>Перефразировать</span>',
                    grammar: '<i>✅</i> <span>Исправить грамматику</span>'
                }[action] || menuItem.innerHTML;
                return;
        }

         // Show modal in loading state immediately
         showResultModal("Запрос к AI...", taskTitle);

        // Call AI (await the promise)
        const aiResult = await callAI(prompt, action); // Pass action as task

        // Update modal with the actual result (or error message)
         if (resultModalVisible) { // Check if modal wasn't closed by user
            showResultModal(aiResult || "Ошибка: Нет ответа от AI.", taskTitle);
         }

         // Note: No need to restore menu item text here as it's hidden now
    }

    /** Adds the AI button to the forum's text editor */
    function addAiButtonToEditor() {
        const editorToolbarSelectors = ['.fr-toolbar', '.xf-editor-toolbar', 'div[data-xf-init="editor"] .fr-toolbar'];
        let toolbar = null;
        for (const selector of editorToolbarSelectors) {
            toolbar = document.querySelector(selector);
            if (toolbar) break;
        }

        if (toolbar && !toolbar.querySelector('.br-ai-editor-button')) {
            const aiButton = document.createElement('button');
            aiButton.innerHTML = '✨ AI'; // Can use innerHTML for icon + text
            aiButton.title = 'Генерация текста с Gemini AI';
            aiButton.classList.add('br-ai-editor-button');
            aiButton.type = 'button';

            aiButton.addEventListener('click', async (e) => { // Make async
                e.preventDefault();
                if (aiButton.disabled) return;

                const editorContentArea = document.querySelector('.fr-element.fr-view') || document.querySelector('textarea.input.codeEditor');
                if (!editorContentArea) {
                    alert('Gemini AI Helper: Не удалось найти поле ввода редактора.');
                    return;
                }

                const currentText = editorContentArea.innerText || editorContentArea.value || '';
                const promptText = prompt('Введите ваш запрос для Gemini AI (генерация текста):', currentText.slice(-500));

                if (promptText) {
                    const originalButtonText = aiButton.innerHTML;
                    aiButton.innerHTML = '⏳ AI...';
                    aiButton.disabled = true;

                    const aiResponse = await callAI(promptText, 'generate', aiButton); // Pass button for disabling

                    if (aiResponse !== null) {
                        let formattedResponse = aiResponse;
                        if (aiResponse.startsWith('(')) { // Handle error/blocked messages
                             formattedResponse = `\n\n--- ${aiResponse} --- \n\n`;
                        } else {
                            formattedResponse = aiResponse.replace(/\n/g, editorContentArea.isContentEditable ? '<br>' : '\n');
                        }

                        // Insert response (simple append/replace for now)
                        if (editorContentArea.isContentEditable) {
                             document.execCommand('insertHTML', false, (formattedResponse.includes('<br>') ? formattedResponse : '<p>' + formattedResponse + '</p>'));
                        } else if (editorContentArea.value !== undefined) {
                            editorContentArea.value += '\n\n' + formattedResponse;
                        }
                    }
                    // Restore button state (already handled by callAI if button passed)
                    aiButton.innerHTML = originalButtonText;
                    // aiButton.disabled = false; // This is handled within callAI now
                }
            });

            const lastButtonGroup = toolbar.querySelector('.fr-btn-group:last-child') || toolbar;
            lastButtonGroup.appendChild(aiButton);
        }
    }


    // --- Global Event Listeners ---

    /** Handles mouse up events to detect text selection for context menu */
    function handleMouseUp(event) {
        // Don't show context menu if clicking inside our own UI elements
         if (event.target.closest('#br-ai-settings-panel, #br-ai-context-menu, #br-ai-result-modal, #br-ai-toggle-icon')) {
             return;
         }

        // Debounce or delay slightly to ensure selection is finalized
        setTimeout(() => {
            const selection = window.getSelection();
            const selectedText = selection ? selection.toString().trim() : '';

            if (selectedText && selectedText.length > 5) { // Require minimum length
                currentSelection = { text: selectedText, range: selection.getRangeAt(0) };
                 // Calculate position near the end of the selection
                 const rect = currentSelection.range.getBoundingClientRect();
                 const posX = event.clientX; // Use mouse X
                 const posY = rect.bottom + window.scrollY + 5; // Position below selection rect end
                 showContextMenu(posX, posY);
            } else {
                currentSelection = null;
                 hideContextMenu();
            }
        }, 50); // Small delay
    }

     /** Handles clicks outside UI elements to hide them */
    function handleMouseDown(event) {
        // Hide context menu if clicking outside it
        if (contextMenuVisible && !event.target.closest('#br-ai-context-menu')) {
            hideContextMenu();
        }
        // Hide settings panel if clicking outside it AND outside the toggle icon
         if (settingsPanelVisible && !event.target.closest('#br-ai-settings-panel') && event.target !== toggleIcon) {
             toggleSettingsPanel(); // Use toggle to handle visibility state
         }
         // Note: Modal closes via its own close button or backdrop click, handled separately
    }

    // --- Initialization ---
    function initialize() {
        console.log("Gemini AI Helper v0.6 Initializing...");
        injectStyles();
        createSettingsPanel();
        createContextMenu(); // Create context menu structure
        createResultModal(); // Create result modal structure

        // Add global listeners
        document.addEventListener('mouseup', handleMouseUp);
        document.addEventListener('mousedown', handleMouseDown); // Use mousedown to hide before potential mouseup selection

        // Attempt to add editor button (using observer is more robust)
        const observer = new MutationObserver((mutationsList) => {
            for(let mutation of mutationsList) {
                if (mutation.type === 'childList') {
                     if (document.querySelector('.fr-toolbar') && !document.querySelector('.br-ai-editor-button')) {
                         addAiButtonToEditor();
                     }
                }
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });

        // Fallback checks for editor button
        setTimeout(addAiButtonToEditor, 1500);
        setTimeout(addAiButtonToEditor, 4000);

        // Initial API key check (silent)
        setTimeout(() => checkApiKey(true), 500);
        console.log("Gemini AI Helper Initialized.");
    }

    // --- Run Script ---
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }

})();