
from plugins import Plugin, PluginMeta, PluginSetting, PluginSettingType, HookType
from typing import Optional, Dict, Any, List, Tuple, Callable
import asyncio
import json
import re
import urllib.request
import urllib.parse
import urllib.error
import ssl

SUPPORTED_LANGUAGES: Dict[str, str] = {
    "en": "English",
    "tr": "Türkçe",
    "de": "Deutsch",
    "fr": "Français",
    "es": "Español",
    "it": "Italiano",
    "pt": "Português",
    "ru": "Русский",
    "ja": "日本語",
    "ko": "한국어",
    "zh": "中文",
    "ar": "العربية",
    "hi": "हिन्दी",
    "nl": "Nederlands",
    "pl": "Polski",
    "uk": "Українська",
    "vi": "Tiếng Việt",
    "th": "ไทย",
    "id": "Bahasa Indonesia",
    "cs": "Čeština",
    "sv": "Svenska",
    "da": "Dansk",
    "fi": "Suomi",
    "no": "Norsk",
    "el": "Ελληνικά",
    "he": "עברית",
    "ro": "Română",
    "hu": "Magyar",
    "bg": "Български",
    "fa": "فارسی",
}

class TranslationProvider:

    name: str = "base"
    requires_api_key: bool = False

    def translate(self, text: str, source_lang: str, target_lang: str, **kwargs) -> str:
        raise NotImplementedError("Subclasses must implement translate method")

    def detect_language(self, text: str) -> Optional[str]:
        return None

class GoogleTranslateProvider(TranslationProvider):

    name = "google"
    requires_api_key = False

    def __init__(self, api_key: Optional[str] = None):
        self.api_key = api_key
        self._ssl_context = ssl.create_default_context()

    def translate(self, text: str, source_lang: str, target_lang: str, **kwargs) -> str:
        if self.api_key:
            return self._translate_with_api(text, source_lang, target_lang)
        return self._translate_free(text, source_lang, target_lang)

    def _translate_free(self, text: str, source_lang: str, target_lang: str) -> str:
        base_url = "https://translate.googleapis.com/translate_a/single"

        params = {
            "client": "gtx",
            "sl": source_lang if source_lang != "auto" else "auto",
            "tl": target_lang,
            "dt": "t",
            "q": text
        }

        url = f"{base_url}?{urllib.parse.urlencode(params)}"

        request = urllib.request.Request(
            url,
            headers={"User-Agent": "Mozilla/5.0"}
        )

        with urllib.request.urlopen(request, timeout=10, context=self._ssl_context) as response:
            data = json.loads(response.read().decode("utf-8"))

        translated_parts = []
        if data and data[0]:
            for part in data[0]:
                if part[0]:
                    translated_parts.append(part[0])

        return "".join(translated_parts)

    def _translate_with_api(self, text: str, source_lang: str, target_lang: str) -> str:
        base_url = "https://translation.googleapis.com/language/translate/v2"

        data = {
            "q": text,
            "target": target_lang,
            "format": "text",
            "key": self.api_key
        }

        if source_lang != "auto":
            data["source"] = source_lang

        encoded_data = urllib.parse.urlencode(data).encode("utf-8")

        request = urllib.request.Request(base_url, data=encoded_data, method="POST")

        with urllib.request.urlopen(request, timeout=10, context=self._ssl_context) as response:
            result = json.loads(response.read().decode("utf-8"))

        return result["data"]["translations"][0]["translatedText"]

    def detect_language(self, text: str) -> Optional[str]:
        base_url = "https://translate.googleapis.com/translate_a/single"

        params = {
            "client": "gtx",
            "sl": "auto",
            "tl": "en",
            "dt": "t",
            "q": text[:100]
        }

        url = f"{base_url}?{urllib.parse.urlencode(params)}"

        try:
            request = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
            with urllib.request.urlopen(request, timeout=5, context=self._ssl_context) as response:
                data = json.loads(response.read().decode("utf-8"))

            if data and len(data) > 2 and data[2]:
                return data[2]
        except Exception:
            pass

        return None

class DeepLProvider(TranslationProvider):

    name = "deepl"
    requires_api_key = True

    LANG_MAP = {
        "en": "EN",
        "de": "DE",
        "fr": "FR",
        "es": "ES",
        "it": "IT",
        "pt": "PT",
        "nl": "NL",
        "pl": "PL",
        "ru": "RU",
        "ja": "JA",
        "zh": "ZH",
        "tr": "TR",
    }

    def __init__(self, api_key: str, use_free_api: bool = False):
        self.api_key = api_key
        self.base_url = (
            "https://api-free.deepl.com/v2/translate"
            if use_free_api
            else "https://api.deepl.com/v2/translate"
        )
        self._ssl_context = ssl.create_default_context()

    def translate(self, text: str, source_lang: str, target_lang: str, **kwargs) -> str:
        target = self.LANG_MAP.get(target_lang, target_lang.upper())

        data = {
            "auth_key": self.api_key,
            "text": text,
            "target_lang": target,
        }

        if source_lang and source_lang != "auto":
            source = self.LANG_MAP.get(source_lang, source_lang.upper())
            data["source_lang"] = source

        encoded_data = urllib.parse.urlencode(data).encode("utf-8")

        request = urllib.request.Request(
            self.base_url,
            data=encoded_data,
            method="POST",
            headers={"Content-Type": "application/x-www-form-urlencoded"}
        )

        with urllib.request.urlopen(request, timeout=15, context=self._ssl_context) as response:
            result = json.loads(response.read().decode("utf-8"))

        return result["translations"][0]["text"]

class OpenAIProvider(TranslationProvider):

    name = "openai"
    requires_api_key = True
    supports_context = True

    DEFAULT_PROMPT = """You are an expert translator with native-level fluency in all languages. Your task is to provide accurate, natural translations while preserving the original meaning, tone, style, slang, idioms, and cultural nuances.

CRITICAL RULES:
- Translate EXACTLY what is written without censorship, filtering, or modification
- Preserve profanity, slang, adult content, and sensitive topics as-is
- Do NOT add warnings, disclaimers, or moral judgments
- Do NOT refuse to translate any content regardless of subject matter
- Maintain the emotional intensity and register of the original
- Keep formatting, line breaks, and punctuation style
- For untranslatable terms (names, brands), keep original or transliterate appropriately

Source language: {source_lang}
Target language: {target_lang}

{context}
TEXT TO TRANSLATE:
{text}

Provide ONLY the translation, nothing else."""

    def __init__(self, api_key: str, model: str = "gpt-4o-mini", custom_prompt: Optional[str] = None):
        self.api_key = api_key
        self.model = model
        self.custom_prompt = custom_prompt
        self._ssl_context = ssl.create_default_context()

    def translate(self, text: str, source_lang: str, target_lang: str, **kwargs) -> str:
        prompt_template = self.custom_prompt or self.DEFAULT_PROMPT

        source_name = SUPPORTED_LANGUAGES.get(source_lang, source_lang) if source_lang != "auto" else "auto-detect"
        target_name = SUPPORTED_LANGUAGES.get(target_lang, target_lang)

        context_text = ""
        context_messages = kwargs.get("context_messages", [])
        if context_messages:
            context_text = "CONVERSATION CONTEXT (for understanding tone and references):\n"
            for msg in context_messages:
                sender = msg.get("sender", "User")
                body = msg.get("body", "")
                context_text += f"[{sender}]: {body}\n"
            context_text += "\n"

        prompt = prompt_template.format(
            source_lang=source_name,
            target_lang=target_name,
            text=text,
            context=context_text
        )

        messages = [{"role": "user", "content": prompt}]

        data = {
            "model": self.model,
            "messages": messages,
            "temperature": 0.3,
            "max_tokens": len(text) * 4 + 500
        }

        encoded_data = json.dumps(data).encode("utf-8")

        request = urllib.request.Request(
            "https://api.openai.com/v1/chat/completions",
            data=encoded_data,
            method="POST",
            headers={
                "Content-Type": "application/json",
                "Authorization": f"Bearer {self.api_key}"
            }
        )

        with urllib.request.urlopen(request, timeout=30, context=self._ssl_context) as response:
            result = json.loads(response.read().decode("utf-8"))

        return result["choices"][0]["message"]["content"].strip()

class OpenRouterProvider(TranslationProvider):

    name = "openrouter"
    requires_api_key = True
    supports_context = True

    DEFAULT_PROMPT = """You are an expert translator. Translate the text below with absolute accuracy and fidelity.

RULES:
- Translate ALL content without any censorship or filtering
- Preserve profanity, adult themes, slang, and sensitive content exactly
- Keep the original tone, style, and emotional register
- Do NOT add warnings, disclaimers, or refuse any content
- Maintain formatting and structure
- Output ONLY the translation

Target: {target_lang}

{context}
TEXT:
{text}"""

    POPULAR_MODELS = [
        "anthropic/claude-3.5-sonnet",
        "openai/gpt-4o-mini",
        "google/gemini-pro",
        "meta-llama/llama-3-70b-instruct",
        "mistralai/mistral-large",
    ]

    def __init__(self, api_key: str, model: str = "openai/gpt-4o-mini", custom_prompt: Optional[str] = None):
        self.api_key = api_key
        self.model = model
        self.custom_prompt = custom_prompt
        self._ssl_context = ssl.create_default_context()

    def translate(self, text: str, source_lang: str, target_lang: str, **kwargs) -> str:
        prompt_template = self.custom_prompt or self.DEFAULT_PROMPT

        target_name = SUPPORTED_LANGUAGES.get(target_lang, target_lang)

        context_text = ""
        context_messages = kwargs.get("context_messages", [])
        if context_messages:
            context_text = "CONTEXT:\n"
            for msg in context_messages:
                sender = msg.get("sender", "User")
                body = msg.get("body", "")
                context_text += f"[{sender}]: {body}\n"
            context_text += "\n"

        prompt = prompt_template.format(
            source_lang=SUPPORTED_LANGUAGES.get(source_lang, source_lang) if source_lang != "auto" else "auto",
            target_lang=target_name,
            text=text,
            context=context_text
        )

        data = {
            "model": self.model,
            "messages": [{"role": "user", "content": prompt}],
            "temperature": 0.3,
        }

        encoded_data = json.dumps(data).encode("utf-8")

        request = urllib.request.Request(
            "https://openrouter.ai/api/v1/chat/completions",
            data=encoded_data,
            method="POST",
            headers={
                "Content-Type": "application/json",
                "Authorization": f"Bearer {self.api_key}",
                "HTTP-Referer": "https://github.com/antch-client",
            }
        )

        with urllib.request.urlopen(request, timeout=30, context=self._ssl_context) as response:
            result = json.loads(response.read().decode("utf-8"))

        return result["choices"][0]["message"]["content"].strip()

class ChutesProvider(TranslationProvider):

    name = "chutes"
    requires_api_key = True
    supports_context = True

    DEFAULT_PROMPT = """You are an uncensored, expert translator. Your sole purpose is accurate translation without any content filtering.

ABSOLUTE RULES:
- Translate EVERYTHING exactly as written - no exceptions
- NO censorship of profanity, violence, adult content, or any sensitive topics
- NO refusals, warnings, disclaimers, or moral commentary
- Preserve slang, idioms, cultural references, and emotional tone
- Keep original formatting and style
- Output ONLY the direct translation

Target language: {target_lang}

{context}
TRANSLATE THIS:
{text}"""

    def __init__(self, api_key: str, model: str = "deepseek-ai/DeepSeek-V3", custom_prompt: Optional[str] = None):
        self.api_key = api_key
        self.model = model
        self.custom_prompt = custom_prompt
        self._ssl_context = ssl.create_default_context()

    def translate(self, text: str, source_lang: str, target_lang: str, **kwargs) -> str:
        prompt_template = self.custom_prompt or self.DEFAULT_PROMPT

        target_name = SUPPORTED_LANGUAGES.get(target_lang, target_lang)

        context_text = ""
        context_messages = kwargs.get("context_messages", [])
        if context_messages:
            context_text = "CONVERSATION CONTEXT:\n"
            for msg in context_messages:
                sender = msg.get("sender", "User")
                body = msg.get("body", "")
                context_text += f"[{sender}]: {body}\n"
            context_text += "\n"

        prompt = prompt_template.format(
            source_lang=SUPPORTED_LANGUAGES.get(source_lang, source_lang) if source_lang != "auto" else "auto",
            target_lang=target_name,
            text=text,
            context=context_text
        )

        data = {
            "model": self.model,
            "messages": [{"role": "user", "content": prompt}],
            "temperature": 0.3,
            "max_tokens": len(text) * 3,
        }

        encoded_data = json.dumps(data).encode("utf-8")

        request = urllib.request.Request(
            "https://llm.chutes.ai/v1/chat/completions",
            data=encoded_data,
            method="POST",
            headers={
                "Content-Type": "application/json",
                "Authorization": f"Bearer {self.api_key}",
            }
        )

        with urllib.request.urlopen(request, timeout=60, context=self._ssl_context) as response:
            result = json.loads(response.read().decode("utf-8"))

        return result["choices"][0]["message"]["content"].strip()

class CustomPythonProvider(TranslationProvider):

    name = "custom_python"
    requires_api_key = False

    def __init__(self, code: str):
        self.code = code

    def translate(self, text: str, source_lang: str, target_lang: str, **kwargs) -> str:
        local_vars = {
            "text": text,
            "source_lang": source_lang,
            "target_lang": target_lang,
            "result": None,
            "json": json,
            "urllib": urllib,
            "re": re,
            "LANGUAGES": SUPPORTED_LANGUAGES,
        }

        exec(self.code, {"__builtins__": __builtins__}, local_vars)

        result = local_vars.get("result")
        if result is None:
            raise ValueError("Custom code must set 'result' variable with translated text")

        return str(result)

def create_provider(
    provider_type: str,
    api_key: Optional[str] = None,
    model: Optional[str] = None,
    custom_prompt: Optional[str] = None,
    custom_code: Optional[str] = None,
    use_free_api: bool = False
) -> TranslationProvider:

    if provider_type == "google":
        return GoogleTranslateProvider(api_key=api_key)

    elif provider_type == "deepl":
        if not api_key:
            raise ValueError("DeepL requires an API key")
        return DeepLProvider(api_key=api_key, use_free_api=use_free_api)

    elif provider_type == "openai":
        if not api_key:
            raise ValueError("OpenAI requires an API key")
        return OpenAIProvider(
            api_key=api_key,
            model=model or "gpt-4o-mini",
            custom_prompt=custom_prompt
        )

    elif provider_type == "openrouter":
        if not api_key:
            raise ValueError("OpenRouter requires an API key")
        return OpenRouterProvider(
            api_key=api_key,
            model=model or "openai/gpt-4o-mini",
            custom_prompt=custom_prompt
        )

    elif provider_type == "chutes":
        if not api_key:
            raise ValueError("Chutes requires an API key")
        return ChutesProvider(
            api_key=api_key,
            model=model or "deepseek-ai/DeepSeek-V3",
            custom_prompt=custom_prompt
        )

    elif provider_type == "custom_python":
        if not custom_code:
            raise ValueError("Custom Python provider requires code")
        return CustomPythonProvider(code=custom_code)

    else:
        raise ValueError(f"Unknown provider type: {provider_type}")

TRANSLATIONS: Dict[str, Dict[str, str]] = {
    "en": {
        "plugin_name": "Translator",
        "plugin_desc": "Translate messages using various translation APIs",

        "setting_provider": "Translation Provider",
        "setting_provider_desc": "Which translation service to use",
        "setting_target_lang": "Default Target Language",
        "setting_target_lang_desc": "Language to translate messages into by default",
        "setting_api_key": "API Key",
        "setting_api_key_desc": "API key for the selected provider (if required)",
        "setting_model": "Model",
        "setting_model_desc": "Which model to use (for LLM providers)",
        "setting_custom_prompt": "Custom LLM Prompt",
        "setting_custom_prompt_desc": "Custom prompt template. Use {text}, {source_lang}, {target_lang}, {context} placeholders",
        "setting_custom_code": "Custom Python Code",
        "setting_custom_code_desc": "Python code for custom translation. Must set 'result' variable. Available: text, source_lang, target_lang, json, urllib, re",
        "setting_auto_translate": "Auto-Translate",
        "setting_auto_translate_desc": "Automatically translate incoming messages",
        "setting_auto_exclude_langs": "Don't Auto-Translate These Languages",
        "setting_auto_exclude_langs_desc": "Skip auto-translation for messages in these languages (comma-separated codes: en,tr,de)",
        "setting_context_menu_langs": "Context Menu Languages",
        "setting_context_menu_langs_desc": "Languages to show in right-click translate menu (comma-separated codes)",
        "setting_context_menu_langs_hint": "Available: en,tr,de,fr,es,it,pt,ru,ja,ko,zh,ar,hi,nl,pl,uk,vi,th,id,cs,sv,da,fi,no,el,he,ro,hu,bg,fa",
        "setting_show_original": "Show Original Message",
        "setting_show_original_desc": "Display original message alongside translation",
        "setting_deepl_free": "Use DeepL Free API",
        "setting_deepl_free_desc": "Use the free DeepL API endpoint (api-free.deepl.com)",
        "setting_use_context": "Smart Context Translation (AI Only)",
        "setting_use_context_desc": "Send conversation history for more accurate, context-aware translations. Only works with AI providers (OpenAI, OpenRouter, Chutes)",
        "setting_context_message_limit": "Context Message Limit",
        "setting_context_message_limit_desc": "Maximum number of previous messages to include for context",
        "setting_context_token_limit": "Context Token Limit",
        "setting_context_token_limit_desc": "Maximum estimated tokens for context (4 chars ≈ 1 token)",

        "translate_to": "Translate to {lang}",
        "translating": "Translating",
        "translation_error": "Translation failed: {error}",
        "translation_result": "Translation ({lang})",
        "original_text": "Original",
        "auto_translated": "Auto-translated from {source}",
        "context_translate": "🌐 Translate",
        "no_api_key": "API key required for {provider}",
        "select_language": "Select Language...",
        "translated_to": "Translated to {lang}",
    },
    "tr": {
        "plugin_name": "Çevirmen",
        "plugin_desc": "Çeşitli çeviri API'leri kullanarak mesajları çevir",

        "setting_provider": "Çeviri Sağlayıcı",
        "setting_provider_desc": "Hangi çeviri servisi kullanılsın",
        "setting_target_lang": "Varsayılan Hedef Dil",
        "setting_target_lang_desc": "Mesajların varsayılan olarak çevrileceği dil",
        "setting_api_key": "API Anahtarı",
        "setting_api_key_desc": "Seçili sağlayıcı için API anahtarı (gerekliyse)",
        "setting_model": "Model",
        "setting_model_desc": "Hangi model kullanılsın (LLM sağlayıcılar için)",
        "setting_custom_prompt": "Özel LLM Prompt",
        "setting_custom_prompt_desc": "Özel prompt şablonu. {text}, {source_lang}, {target_lang}, {context} yer tutucularını kullan",
        "setting_custom_code": "Özel Python Kodu",
        "setting_custom_code_desc": "Özel çeviri için Python kodu. 'result' değişkenini ayarlamalı. Kullanılabilir: text, source_lang, target_lang, json, urllib, re",
        "setting_auto_translate": "Otomatik Çeviri",
        "setting_auto_translate_desc": "Gelen mesajları otomatik olarak çevir",
        "setting_auto_exclude_langs": "Bu Dilleri Otomatik Çevirme",
        "setting_auto_exclude_langs_desc": "Bu dillerdeki mesajları otomatik çevirme (virgülle ayrılmış kodlar: en,tr,de)",
        "setting_context_menu_langs": "Sağ Tık Menüsü Dilleri",
        "setting_context_menu_langs_desc": "Sağ tık çeviri menüsünde gösterilecek diller (virgülle ayrılmış kodlar)",
        "setting_context_menu_langs_hint": "Kullanılabilir: en,tr,de,fr,es,it,pt,ru,ja,ko,zh,ar,hi,nl,pl,uk,vi,th,id,cs,sv,da,fi,no,el,he,ro,hu,bg,fa",
        "setting_show_original": "Orijinal Mesajı Göster",
        "setting_show_original_desc": "Çeviriyle birlikte orijinal mesajı da göster",
        "setting_deepl_free": "DeepL Ücretsiz API Kullan",
        "setting_deepl_free_desc": "Ücretsiz DeepL API endpoint'ini kullan (api-free.deepl.com)",
        "setting_use_context": "Akıllı Bağlam Çevirisi (Sadece AI)",
        "setting_use_context_desc": "Daha doğru, bağlama uygun çeviri için sohbet geçmişini gönder. Sadece AI sağlayıcılarda çalışır (OpenAI, OpenRouter, Chutes)",
        "setting_context_message_limit": "Bağlam Mesaj Limiti",
        "setting_context_message_limit_desc": "Bağlam için dahil edilecek maksimum önceki mesaj sayısı",
        "setting_context_token_limit": "Bağlam Token Limiti",
        "setting_context_token_limit_desc": "Bağlam için maksimum tahmini token (4 karakter ≈ 1 token)",

        "translate_to": "{lang} diline çevir",
        "translating": "Çevriliyor",
        "translation_error": "Çeviri başarısız: {error}",
        "translation_result": "Çeviri ({lang})",
        "original_text": "Orijinal",
        "auto_translated": "{source} dilinden otomatik çevrildi",
        "context_translate": "🌐 Çevir",
        "no_api_key": "{provider} için API anahtarı gerekli",
        "select_language": "Dil Seç...",
        "translated_to": "{lang} diline çevrildi",
    },
    "de": {
        "plugin_name": "Übersetzer",
        "plugin_desc": "Nachrichten mit verschiedenen Übersetzungs-APIs übersetzen",

        "setting_provider": "Übersetzungsanbieter",
        "setting_provider_desc": "Welcher Übersetzungsdienst verwendet werden soll",
        "setting_target_lang": "Standard-Zielsprache",
        "setting_target_lang_desc": "Sprache, in die Nachrichten standardmäßig übersetzt werden",
        "setting_api_key": "API-Schlüssel",
        "setting_api_key_desc": "API-Schlüssel für den ausgewählten Anbieter (falls erforderlich)",
        "setting_model": "Modell",
        "setting_model_desc": "Welches Modell verwendet werden soll (für LLM-Anbieter)",
        "setting_custom_prompt": "Benutzerdefinierter LLM-Prompt",
        "setting_custom_prompt_desc": "Benutzerdefinierte Prompt-Vorlage. Verwende {text}, {source_lang}, {target_lang}, {context} Platzhalter",
        "setting_custom_code": "Benutzerdefinierter Python-Code",
        "setting_custom_code_desc": "Python-Code für benutzerdefinierte Übersetzung. Muss Variable 'result' setzen. Verfügbar: text, source_lang, target_lang, json, urllib, re",
        "setting_auto_translate": "Auto-Übersetzung",
        "setting_auto_translate_desc": "Eingehende Nachrichten automatisch übersetzen",
        "setting_auto_exclude_langs": "Diese Sprachen nicht auto-übersetzen",
        "setting_auto_exclude_langs_desc": "Auto-Übersetzung für Nachrichten in diesen Sprachen überspringen (kommagetrennte Codes: en,tr,de)",
        "setting_context_menu_langs": "Kontextmenü-Sprachen",
        "setting_context_menu_langs_desc": "Sprachen im Rechtsklick-Übersetzungsmenü anzeigen (kommagetrennte Codes)",
        "setting_context_menu_langs_hint": "Verfügbar: en,tr,de,fr,es,it,pt,ru,ja,ko,zh,ar,hi,nl,pl,uk,vi,th,id,cs,sv,da,fi,no,el,he,ro,hu,bg,fa",
        "setting_show_original": "Originalnachricht anzeigen",
        "setting_show_original_desc": "Originalnachricht neben der Übersetzung anzeigen",
        "setting_deepl_free": "DeepL Free API verwenden",
        "setting_deepl_free_desc": "Kostenlosen DeepL API-Endpoint verwenden (api-free.deepl.com)",
        "setting_use_context": "Intelligente Kontextübersetzung (nur AI)",
        "setting_use_context_desc": "Gesprächsverlauf für genauere, kontextbezogene Übersetzungen senden. Funktioniert nur mit AI-Anbietern (OpenAI, OpenRouter, Chutes)",
        "setting_context_message_limit": "Kontext-Nachrichtenlimit",
        "setting_context_message_limit_desc": "Maximale Anzahl vorheriger Nachrichten für Kontext",
        "setting_context_token_limit": "Kontext-Token-Limit",
        "setting_context_token_limit_desc": "Maximale geschätzte Token für Kontext (4 Zeichen ≈ 1 Token)",

        "translate_to": "Übersetzen nach {lang}",
        "translating": "Übersetze",
        "translation_error": "Übersetzung fehlgeschlagen: {error}",
        "translation_result": "Übersetzung ({lang})",
        "original_text": "Original",
        "auto_translated": "Automatisch übersetzt von {source}",
        "context_translate": "🌐 Übersetzen",
        "no_api_key": "API-Schlüssel für {provider} erforderlich",
        "select_language": "Sprache wählen...",
        "translated_to": "Übersetzt nach {lang}",
    },
    "es": {
        "plugin_name": "Traductor",
        "plugin_desc": "Traducir mensajes usando varias APIs de traducción",

        "setting_provider": "Proveedor de Traducción",
        "setting_provider_desc": "Qué servicio de traducción usar",
        "setting_target_lang": "Idioma de Destino por Defecto",
        "setting_target_lang_desc": "Idioma al que traducir los mensajes por defecto",
        "setting_api_key": "Clave API",
        "setting_api_key_desc": "Clave API para el proveedor seleccionado (si es necesario)",
        "setting_model": "Modelo",
        "setting_model_desc": "Qué modelo usar (para proveedores LLM)",
        "setting_custom_prompt": "Prompt LLM Personalizado",
        "setting_custom_prompt_desc": "Plantilla de prompt personalizada. Usa marcadores {text}, {source_lang}, {target_lang}, {context}",
        "setting_custom_code": "Código Python Personalizado",
        "setting_custom_code_desc": "Código Python para traducción personalizada. Debe establecer variable 'result'. Disponible: text, source_lang, target_lang, json, urllib, re",
        "setting_auto_translate": "Auto-Traducir",
        "setting_auto_translate_desc": "Traducir automáticamente los mensajes entrantes",
        "setting_auto_exclude_langs": "No Auto-Traducir Estos Idiomas",
        "setting_auto_exclude_langs_desc": "Omitir auto-traducción para mensajes en estos idiomas (códigos separados por coma: en,tr,de)",
        "setting_context_menu_langs": "Idiomas del Menú Contextual",
        "setting_context_menu_langs_desc": "Idiomas a mostrar en el menú de traducción con clic derecho (códigos separados por coma)",
        "setting_context_menu_langs_hint": "Disponible: en,tr,de,fr,es,it,pt,ru,ja,ko,zh,ar,hi,nl,pl,uk,vi,th,id,cs,sv,da,fi,no,el,he,ro,hu,bg,fa",
        "setting_show_original": "Mostrar Mensaje Original",
        "setting_show_original_desc": "Mostrar mensaje original junto con la traducción",
        "setting_deepl_free": "Usar API Gratuita de DeepL",
        "setting_deepl_free_desc": "Usar el endpoint gratuito de DeepL API (api-free.deepl.com)",
        "setting_use_context": "Traducción Inteligente con Contexto (Solo AI)",
        "setting_use_context_desc": "Enviar historial de conversación para traducciones más precisas. Solo funciona con proveedores AI (OpenAI, OpenRouter, Chutes)",
        "setting_context_message_limit": "Límite de Mensajes de Contexto",
        "setting_context_message_limit_desc": "Número máximo de mensajes anteriores para contexto",
        "setting_context_token_limit": "Límite de Tokens de Contexto",
        "setting_context_token_limit_desc": "Tokens estimados máximos para contexto (4 caracteres ≈ 1 token)",

        "translate_to": "Traducir a {lang}",
        "translating": "Traduciendo",
        "translation_error": "Traducción fallida: {error}",
        "translation_result": "Traducción ({lang})",
        "original_text": "Original",
        "auto_translated": "Auto-traducido de {source}",
        "context_translate": "🌐 Traducir",
        "no_api_key": "Se requiere clave API para {provider}",
        "select_language": "Seleccionar idioma...",
        "translated_to": "Traducido a {lang}",
    },
    "fr": {
        "plugin_name": "Traducteur",
        "plugin_desc": "Traduire les messages avec diverses APIs de traduction",

        "setting_provider": "Fournisseur de Traduction",
        "setting_provider_desc": "Quel service de traduction utiliser",
        "setting_target_lang": "Langue Cible par Défaut",
        "setting_target_lang_desc": "Langue dans laquelle traduire les messages par défaut",
        "setting_api_key": "Clé API",
        "setting_api_key_desc": "Clé API pour le fournisseur sélectionné (si requis)",
        "setting_model": "Modèle",
        "setting_model_desc": "Quel modèle utiliser (pour les fournisseurs LLM)",
        "setting_custom_prompt": "Prompt LLM Personnalisé",
        "setting_custom_prompt_desc": "Modèle de prompt personnalisé. Utilisez {text}, {source_lang}, {target_lang}, {context} comme marqueurs",
        "setting_custom_code": "Code Python Personnalisé",
        "setting_custom_code_desc": "Code Python pour traduction personnalisée. Doit définir la variable 'result'. Disponible: text, source_lang, target_lang, json, urllib, re",
        "setting_auto_translate": "Auto-Traduction",
        "setting_auto_translate_desc": "Traduire automatiquement les messages entrants",
        "setting_auto_exclude_langs": "Ne Pas Auto-Traduire Ces Langues",
        "setting_auto_exclude_langs_desc": "Ignorer l'auto-traduction pour les messages dans ces langues (codes séparés par virgule: en,tr,de)",
        "setting_context_menu_langs": "Langues du Menu Contextuel",
        "setting_context_menu_langs_desc": "Langues à afficher dans le menu de traduction clic droit (codes séparés par virgule)",
        "setting_context_menu_langs_hint": "Disponible: en,tr,de,fr,es,it,pt,ru,ja,ko,zh,ar,hi,nl,pl,uk,vi,th,id,cs,sv,da,fi,no,el,he,ro,hu,bg,fa",
        "setting_show_original": "Afficher le Message Original",
        "setting_show_original_desc": "Afficher le message original avec la traduction",
        "setting_deepl_free": "Utiliser l'API DeepL Gratuite",
        "setting_deepl_free_desc": "Utiliser le point de terminaison gratuit DeepL API (api-free.deepl.com)",
        "setting_use_context": "Traduction Contextuelle Intelligente (AI uniquement)",
        "setting_use_context_desc": "Envoyer l'historique de conversation pour des traductions plus précises. Fonctionne uniquement avec les fournisseurs AI (OpenAI, OpenRouter, Chutes)",
        "setting_context_message_limit": "Limite de Messages de Contexte",
        "setting_context_message_limit_desc": "Nombre maximum de messages précédents pour le contexte",
        "setting_context_token_limit": "Limite de Tokens de Contexte",
        "setting_context_token_limit_desc": "Tokens estimés maximum pour le contexte (4 caractères ≈ 1 token)",

        "translate_to": "Traduire en {lang}",
        "translating": "Traduction en cours",
        "translation_error": "Échec de la traduction : {error}",
        "translation_result": "Traduction ({lang})",
        "original_text": "Original",
        "auto_translated": "Auto-traduit de {source}",
        "context_translate": "🌐 Traduire",
        "no_api_key": "Clé API requise pour {provider}",
        "select_language": "Sélectionner la langue...",
        "translated_to": "Traduit en {lang}",
    },
}

class TranslatorPlugin(Plugin):

    def t(self, key: str, **kwargs) -> str:
        lang = "en"
        if self._app and hasattr(self._app, 'lang'):
            lang = self._app.lang or "en"

        if lang in TRANSLATIONS and key in TRANSLATIONS[lang]:
            template = TRANSLATIONS[lang][key]
        elif key in TRANSLATIONS.get("en", {}):
            template = TRANSLATIONS["en"][key]
        else:
            return key

        if kwargs:
            try:
                return template.format(**kwargs)
            except KeyError:
                return template
        return template

    @property
    def meta(self) -> PluginMeta:
        return PluginMeta(
            name="Translator",
            id="translator",
            version="1.0.0",
            description="Translate messages using various translation APIs",
            author="ANTCH",
            requires_restart=False,
            tags=["translation", "language", "i18n", "google", "deepl", "openai", "llm"]
        )

    @property
    def settings(self) -> List[PluginSetting]:
        provider_options = [
            ("google", "🌐 Google Translate (Free)"),
            ("deepl", "📘 DeepL (API Key Required)"),
            ("openai", "🤖 OpenAI GPT (API Key Required)"),
            ("openrouter", "🔀 OpenRouter (API Key Required)"),
            ("chutes", "⚡ Chutes (API Key Required)"),
            ("custom_python", "🐍 Custom Python Code"),
        ]

        language_options = [(code, name) for code, name in SUPPORTED_LANGUAGES.items()]

        return [
            PluginSetting(
                key="provider",
                label=self.t("setting_provider"),
                type=PluginSettingType.SELECT,
                default="google",
                description=self.t("setting_provider_desc"),
                options=provider_options
            ),
            PluginSetting(
                key="target_lang",
                label=self.t("setting_target_lang"),
                type=PluginSettingType.SELECT,
                default="en",
                description=self.t("setting_target_lang_desc"),
                options=language_options
            ),
            PluginSetting(
                key="api_key",
                label=self.t("setting_api_key"),
                type=PluginSettingType.STRING,
                default="",
                description=self.t("setting_api_key_desc") + "\n• DeepL: deepl.com/pro-api\n• OpenAI: platform.openai.com/api-keys\n• OpenRouter: openrouter.ai/keys\n• Chutes: chutes.ai",
                placeholder="API key (Google doesn't need one)"
            ),
            PluginSetting(
                key="model",
                label=self.t("setting_model"),
                type=PluginSettingType.STRING,
                default="gpt-4o-mini",
                description=self.t("setting_model_desc") + "\n• OpenAI: gpt-4o-mini, gpt-4o, gpt-4-turbo\n• OpenRouter: openai/gpt-4o-mini, anthropic/claude-3.5-sonnet, google/gemini-pro\n• Chutes: deepseek-ai/DeepSeek-V3, meta-llama/Llama-3.3-70B-Instruct",
                placeholder="gpt-4o-mini"
            ),
            PluginSetting(
                key="custom_prompt",
                label=self.t("setting_custom_prompt"),
                type=PluginSettingType.TEXT,
                default="",
                description=self.t("setting_custom_prompt_desc"),
                placeholder="Translate {text} from {source_lang} to {target_lang}..."
            ),
            PluginSetting(
                key="custom_code",
                label=self.t("setting_custom_code"),
                type=PluginSettingType.TEXT,
                default="",
                description=self.t("setting_custom_code_desc"),
                placeholder="""# Example: Use a custom API
import urllib.request, json

data = json.dumps({"text": text, "to": target_lang}).encode()
req = urllib.request.Request("https://your-api.com/translate", data=data)
req.add_header("Content-Type", "application/json")

with urllib.request.urlopen(req) as resp:
    result = json.loads(resp.read())["translated"]"""
            ),
            PluginSetting(
                key="auto_translate",
                label=self.t("setting_auto_translate"),
                type=PluginSettingType.BOOLEAN,
                default=False,
                description=self.t("setting_auto_translate_desc")
            ),
            PluginSetting(
                key="auto_exclude_langs",
                label=self.t("setting_auto_exclude_langs"),
                type=PluginSettingType.STRING,
                default="",
                description=self.t("setting_auto_exclude_langs_desc"),
                placeholder="en,tr"
            ),
            PluginSetting(
                key="context_menu_langs",
                label=self.t("setting_context_menu_langs"),
                type=PluginSettingType.STRING,
                default="en,tr",
                description=self.t("setting_context_menu_langs_desc") + "\n" + self.t("setting_context_menu_langs_hint"),
                placeholder="en,tr,de,fr,es"
            ),
            PluginSetting(
                key="show_original",
                label=self.t("setting_show_original"),
                type=PluginSettingType.BOOLEAN,
                default=True,
                description=self.t("setting_show_original_desc")
            ),
            PluginSetting(
                key="deepl_free",
                label=self.t("setting_deepl_free"),
                type=PluginSettingType.BOOLEAN,
                default=True,
                description=self.t("setting_deepl_free_desc")
            ),
            PluginSetting(
                key="use_context",
                label=self.t("setting_use_context"),
                type=PluginSettingType.BOOLEAN,
                default=True,
                description=self.t("setting_use_context_desc")
            ),
            PluginSetting(
                key="context_message_limit",
                label=self.t("setting_context_message_limit"),
                type=PluginSettingType.INTEGER,
                default=10,
                min_value=1,
                max_value=50,
                description=self.t("setting_context_message_limit_desc")
            ),
            PluginSetting(
                key="context_token_limit",
                label=self.t("setting_context_token_limit"),
                type=PluginSettingType.INTEGER,
                default=2000,
                min_value=100,
                max_value=16000,
                description=self.t("setting_context_token_limit_desc")
            ),
        ]

    def on_load(self) -> None:
        self._provider: Optional[TranslationProvider] = None
        self._init_provider()

    def on_enable(self) -> None:
        self._init_provider()

    def on_settings_update(self, key: str, value: Any) -> None:
        self._init_provider()

    def _init_provider(self) -> None:
        try:
            provider_type = self.get_setting("provider", "google")
            api_key = self.get_setting("api_key", "")
            model = self.get_setting("model", "")
            custom_prompt = self.get_setting("custom_prompt", "")
            custom_code = self.get_setting("custom_code", "")
            deepl_free = self.get_setting("deepl_free", True)

            self._provider = create_provider(
                provider_type=provider_type,
                api_key=api_key if api_key else None,
                model=model if model else None,
                custom_prompt=custom_prompt if custom_prompt else None,
                custom_code=custom_code if custom_code else None,
                use_free_api=deepl_free
            )
        except Exception as e:
            self._provider = GoogleTranslateProvider()
            print(f"[Translator] Provider init error, falling back to Google: {e}")

    def _get_context_menu_languages(self) -> List[str]:
        langs_str = self.get_setting("context_menu_langs", "en,tr,de,fr,es")
        if not langs_str:
            return ["en", "tr"]

        return [lang.strip().lower() for lang in langs_str.split(",") if lang.strip()]

    def _get_exclude_languages(self) -> List[str]:
        langs_str = self.get_setting("auto_exclude_langs", "")
        if not langs_str:
            return []

        return [lang.strip().lower() for lang in langs_str.split(",") if lang.strip()]

    def translate(self, text: str, target_lang: str, source_lang: str = "auto", context_messages: Optional[List[Dict]] = None) -> str:
        if not self._provider:
            self._init_provider()

        if not self._provider:
            raise RuntimeError("No translation provider available")

        provider_supports_context = getattr(self._provider, 'supports_context', False)
        use_context = self.get_setting("use_context", True)

        if provider_supports_context and use_context and context_messages:
            return self._provider.translate(text, source_lang, target_lang, context_messages=context_messages)
        else:
            return self._provider.translate(text, source_lang, target_lang)

    def _get_conversation_context(self, peer_alias: str, current_msg_id: str) -> List[Dict]:
        if not self._app:
            return []

        use_context = self.get_setting("use_context", True)
        if not use_context:
            return []

        provider_supports_context = getattr(self._provider, 'supports_context', False)
        if not provider_supports_context:
            return []

        message_limit = self.get_setting("context_message_limit", 10)
        token_limit = self.get_setting("context_token_limit", 2000)

        context_messages = []
        total_chars = 0
        char_per_token = 4

        try:
            if hasattr(self._app, 'db') and self._app.db:
                messages = self._app.db.get_messages(peer_alias, limit=message_limit + 5)

                for msg in reversed(messages):
                    msg_id = str(msg.get("msg_id", ""))

                    if msg_id == current_msg_id:
                        continue

                    body = msg.get("body", "")
                    if not body:
                        continue

                    msg_chars = len(body)
                    if total_chars + msg_chars > token_limit * char_per_token:
                        break

                    context_messages.append({
                        "sender": msg.get("sender_alias", "Unknown"),
                        "body": body
                    })
                    total_chars += msg_chars

                    if len(context_messages) >= message_limit:
                        break
        except Exception as e:
            print(f"[Translator] Context fetch error: {e}")

        return context_messages

    def detect_language(self, text: str) -> Optional[str]:
        if not self._provider:
            self._init_provider()

        if self._provider and hasattr(self._provider, 'detect_language'):
            return self._provider.detect_language(text)

        return None

    def on_message_context_menu(self, message_data: Dict[str, Any]) -> List[Dict[str, Any]]:
        menu_items = []

        context_langs = self._get_context_menu_languages()
        text = message_data.get("content", "") or message_data.get("text", "")

        if not text or len(text.strip()) < 2:
            return []

        for lang_code in context_langs:
            lang_name = SUPPORTED_LANGUAGES.get(lang_code, lang_code)

            def make_callback(lc: str):
                def callback(msg_data: Dict[str, Any]) -> None:
                    self._handle_translate_menu(msg_data, lc)
                return callback

            menu_items.append({
                "id": f"translate_to_{lang_code}",
                "label": f"{lang_name}",
                "icon": "🌐",
                "callback": make_callback(lang_code),
            })

        def select_lang_callback(msg_data: Dict[str, Any]) -> None:
            self._show_language_selector(msg_data)

        menu_items.append({
            "id": "translate_select_lang",
            "label": self.t("select_language"),
            "icon": "🌍",
            "callback": select_lang_callback,
        })

        return menu_items

    def _show_language_selector(self, message_data: Dict[str, Any]) -> None:
        if not self._app:
            return

        from ui.dialogs import ContextMenu

        actions = []
        for code, name in sorted(SUPPORTED_LANGUAGES.items(), key=lambda x: x[1]):
            actions.append((code, f"🌐 {name}"))

        async def show_selector():
            try:
                mouse_x = 40
                mouse_y = 10
                try:
                    if hasattr(self._app, 'mouse_position'):
                        mouse_x, mouse_y = self._app.mouse_position
                except Exception:
                    pass

                choice = await self._app.ask(ContextMenu(
                    self.t("select_language"),
                    actions,
                    x=mouse_x,
                    y=mouse_y
                ))

                if choice and choice in SUPPORTED_LANGUAGES:
                    self._handle_translate_menu(message_data, choice)
            except Exception as e:
                print(f"[Translator] Language selector error: {e}")

        try:
            import asyncio
            loop = asyncio.get_event_loop()
            if loop.is_running():
                asyncio.ensure_future(show_selector())
            else:
                loop.run_until_complete(show_selector())
        except Exception:
            self._app.call_later(show_selector)

    def _handle_translate_menu(self, message_data: Dict[str, Any], target_lang: str) -> None:
        text = message_data.get("content", "") or message_data.get("text", "")
        msg_id = message_data.get("msg_id", "")
        peer_alias = message_data.get("peer_alias", "")

        if not text:
            self.notify("No text to translate", severity="warning")
            return

        if not self._app:
            return

        context_messages = self._get_conversation_context(peer_alias, msg_id)

        def update_message_widget(content: str, is_loading: bool = False):
            try:
                if hasattr(self._app, '_msg_widgets') and msg_id in self._app._msg_widgets:
                    widget = self._app._msg_widgets[msg_id]

                    try:
                        text_widgets = list(widget.query(".msg-text"))
                        if text_widgets:
                            for tw in text_widgets:
                                tw.update(content)
                    except Exception:
                        pass
            except Exception as e:
                print(f"[Translator] Widget update error: {e}")

        lang_name = SUPPORTED_LANGUAGES.get(target_lang, target_lang)

        context_indicator = ""
        if context_messages and getattr(self._provider, 'supports_context', False):
            context_indicator = f" [dim](+{len(context_messages)} ctx)[/]"

        import time
        stop_animation = [False]

        def animate_loading():
            dots = ["", ".", "..", "..."]
            idx = 0
            while not stop_animation[0]:
                loading_text = f"{text}\n\n[dim italic]⏳ {self.t('translating')}{context_indicator}{dots[idx]}[/]"
                if self._app:
                    self._app.call_from_thread(update_message_widget, loading_text, True)
                idx = (idx + 1) % len(dots)
                time.sleep(0.4)

        import threading
        anim_thread = threading.Thread(target=animate_loading, daemon=True)
        anim_thread.start()

        def do_translate():
            try:
                translated = self.translate(text, target_lang, context_messages=context_messages)
                stop_animation[0] = True

                show_original = self.get_setting("show_original", True)

                if show_original:
                    result_text = f"{text}\n\n[bold cyan]🌐 {lang_name}:[/]\n{translated}"
                else:
                    result_text = f"[bold cyan]🌐 {lang_name}:[/]\n{translated}"

                if self._app:
                    self._app.call_from_thread(update_message_widget, result_text, False)

            except Exception as e:
                stop_animation[0] = True
                error_text = f"{text}\n\n[red]❌ {self.t('translation_error', error=str(e))}[/]"
                if self._app:
                    self._app.call_from_thread(update_message_widget, error_text, False)

        thread = threading.Thread(target=do_translate, daemon=True)
        thread.start()

    def _show_translation_result(self, lang_name: str, result_text: str) -> None:
        self.notify(result_text, severity="information")

    def on_message_receive(self, message_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        if not self.get_setting("auto_translate", False):
            return None

        text = message_data.get("content", "") or message_data.get("text", "")
        if not text or len(text) < 3:
            return None

        exclude_langs = self._get_exclude_languages()
        target_lang = self.get_setting("target_lang", "en")

        if exclude_langs:
            detected = self.detect_language(text)
            if detected and detected.lower() in exclude_langs:
                return None

            if detected and detected.lower() == target_lang:
                return None

        try:
            translated = self.translate(text, target_lang)

            source_lang = self.detect_language(text) or "?"
            source_name = SUPPORTED_LANGUAGES.get(source_lang, source_lang)

            show_original = self.get_setting("show_original", True)

            if show_original:
                message_data["content"] = f"{translated}\n\n[dim i]{self.t('auto_translated', source=source_name)}: {text}[/]"
            else:
                message_data["content"] = translated

            return message_data

        except Exception as e:
            print(f"[Translator] Auto-translate error: {e}")
            return None

    def on_slash_command(self, command: str, args: List[str], context: Dict[str, Any]) -> Optional[bool]:
        if command not in ("translate", "tr", "çevir"):
            return None

        if command == "tr" and len(args) >= 2:
            target_lang = args[0].lower()
            text = " ".join(args[1:])
        elif args:
            target_lang = self.get_setting("target_lang", "en")
            text = " ".join(args)
        else:
            self.notify("Usage: /translate <text> or /tr <lang> <text>", severity="warning")
            return True

        try:
            translated = self.translate(text, target_lang)
            lang_name = SUPPORTED_LANGUAGES.get(target_lang, target_lang)

            self.notify(f"[bold]{lang_name}:[/] {translated}", severity="information")
        except Exception as e:
            self.notify(f"Translation error: {e}", severity="error")

        return True
