import os
import re
import shutil
import subprocess
from typing import Any

try:
    import requests

    HAS_REQUESTS = True
except ImportError:
    HAS_REQUESTS = False

try:
    from argostranslate import package, translate

    HAS_ARGOS_LIB = True
except ImportError:
    HAS_ARGOS_LIB = False

HAS_ARGOS_CLI = shutil.which("argos-translate") is not None
HAS_ARGOS = HAS_ARGOS_LIB or HAS_ARGOS_CLI

LANGUAGE_CODE_TO_NAME = {
    "en": "English",
    "de": "German",
    "es": "Spanish",
    "fr": "French",
    "it": "Italian",
    "pt": "Portuguese",
    "ru": "Russian",
    "zh": "Chinese",
    "ja": "Japanese",
    "ko": "Korean",
    "ar": "Arabic",
    "hi": "Hindi",
    "nl": "Dutch",
    "pl": "Polish",
    "tr": "Turkish",
    "sv": "Swedish",
    "da": "Danish",
    "no": "Norwegian",
    "fi": "Finnish",
    "cs": "Czech",
    "ro": "Romanian",
    "hu": "Hungarian",
    "el": "Greek",
    "he": "Hebrew",
    "th": "Thai",
    "vi": "Vietnamese",
    "id": "Indonesian",
    "uk": "Ukrainian",
    "bg": "Bulgarian",
    "hr": "Croatian",
    "sk": "Slovak",
    "sl": "Slovenian",
    "et": "Estonian",
    "lv": "Latvian",
    "lt": "Lithuanian",
    "mt": "Maltese",
    "ga": "Irish",
    "cy": "Welsh",
}


class TranslatorHandler:
    def __init__(self, libretranslate_url: str | None = None):
        self.libretranslate_url = libretranslate_url or os.getenv(
            "LIBRETRANSLATE_URL",
            "http://localhost:5000",
        )
        self.has_argos = HAS_ARGOS
        self.has_argos_lib = HAS_ARGOS_LIB
        self.has_argos_cli = HAS_ARGOS_CLI
        self.has_requests = HAS_REQUESTS

    def get_supported_languages(self, libretranslate_url: str | None = None):
        languages = []
        url = libretranslate_url or self.libretranslate_url

        if self.has_requests:
            try:
                response = requests.get(f"{url}/languages", timeout=5)
                if response.status_code == 200:
                    libretranslate_langs = response.json()
                    languages.extend(
                        {
                            "code": lang.get("code"),
                            "name": lang.get("name"),
                            "source": "libretranslate",
                        }
                        for lang in libretranslate_langs
                    )
                    return languages
            except Exception as e:
                # Log or handle the exception appropriately
                print(f"Failed to fetch LibreTranslate languages: {e}")

        if self.has_argos_lib:
            try:
                installed_packages = package.get_installed_packages()
                argos_langs = set()
                for pkg in installed_packages:
                    argos_langs.add((pkg.from_code, pkg.from_name))
                    argos_langs.add((pkg.to_code, pkg.to_name))

                for code, name in sorted(argos_langs):
                    languages.append(
                        {
                            "code": code,
                            "name": name,
                            "source": "argos",
                        },
                    )
            except Exception as e:
                print(f"Failed to fetch Argos languages: {e}")
        elif self.has_argos_cli:
            try:
                cli_langs = self._get_argos_languages_cli()
                languages.extend(cli_langs)
            except Exception as e:
                print(f"Failed to fetch Argos languages via CLI: {e}")

        return languages

    def translate_text(
        self,
        text: str,
        source_lang: str,
        target_lang: str,
        use_argos: bool = False,
        libretranslate_url: str | None = None,
    ) -> dict[str, Any]:
        if not text:
            msg = "Text cannot be empty"
            raise ValueError(msg)

        if use_argos and self.has_argos:
            return self._translate_argos(text, source_lang, target_lang)

        if self.has_requests:
            try:
                url = libretranslate_url or self.libretranslate_url
                return self._translate_libretranslate(
                    text,
                    source_lang=source_lang,
                    target_lang=target_lang,
                    libretranslate_url=url,
                )
            except Exception as e:
                if self.has_argos:
                    return self._translate_argos(text, source_lang, target_lang)
                raise e

        if self.has_argos:
            return self._translate_argos(text, source_lang, target_lang)

        msg = "No translation backend available. Install requests for LibreTranslate or argostranslate for local translation."
        raise RuntimeError(msg)

    def _translate_libretranslate(
        self,
        text: str,
        source_lang: str,
        target_lang: str,
        libretranslate_url: str | None = None,
    ) -> dict[str, Any]:
        if not self.has_requests:
            msg = "requests library not available"
            raise RuntimeError(msg)

        url = libretranslate_url or self.libretranslate_url
        response = requests.post(
            f"{url}/translate",
            json={
                "q": text,
                "source": source_lang,
                "target": target_lang,
                "format": "text",
            },
            timeout=30,
        )

        if response.status_code != 200:
            msg = f"LibreTranslate API error: {response.status_code} - {response.text}"
            raise RuntimeError(msg)

        result = response.json()
        return {
            "translated_text": result.get("translatedText", ""),
            "source_lang": result.get("detectedLanguage", {}).get(
                "language",
                source_lang,
            ),
            "target_lang": target_lang,
            "source": "libretranslate",
        }

    def _translate_argos(
        self,
        text: str,
        source_lang: str,
        target_lang: str,
    ) -> dict[str, Any]:
        if source_lang == "auto":
            if self.has_argos_lib:
                detected_lang = self._detect_language(text)
                if detected_lang:
                    source_lang = detected_lang
                else:
                    msg = "Could not auto-detect language. Please select a source language manually."
                    raise ValueError(msg)
            else:
                msg = (
                    "Auto-detection is not supported with CLI-only installation. "
                    "Please select a source language manually or install the Python library: pip install argostranslate"
                )
                raise ValueError(msg)

        if self.has_argos_lib:
            return self._translate_argos_lib(text, source_lang, target_lang)
        if self.has_argos_cli:
            return self._translate_argos_cli(text, source_lang, target_lang)
        msg = "Argos Translate not available (neither library nor CLI)"
        raise RuntimeError(msg)

    def _translate_argos_lib(
        self,
        text: str,
        source_lang: str,
        target_lang: str,
    ) -> dict[str, Any]:
        try:
            installed_packages = package.get_installed_packages()
            translation_package = None

            for pkg in installed_packages:
                if pkg.from_code == source_lang and pkg.to_code == target_lang:
                    translation_package = pkg
                    break

            if translation_package is None:
                msg = (
                    f"No translation package found for {source_lang} -> {target_lang}. "
                    "Install packages using: argostranslate --update-languages"
                )
                raise ValueError(msg)

            translated_text = translate.translate(text, source_lang, target_lang)
            return {
                "translated_text": translated_text,
                "source_lang": source_lang,
                "target_lang": target_lang,
                "source": "argos",
            }
        except Exception as e:
            msg = f"Argos Translate error: {e}"
            raise RuntimeError(msg)

    def _translate_argos_cli(
        self,
        text: str,
        source_lang: str,
        target_lang: str,
    ) -> dict[str, Any]:
        if source_lang == "auto" or not source_lang:
            msg = "Auto-detection is not supported with CLI. Please select a source language manually."
            raise ValueError(msg)

        if not target_lang:
            msg = "Target language is required."
            raise ValueError(msg)

        if not isinstance(source_lang, str) or not isinstance(target_lang, str):
            msg = "Language codes must be strings."
            raise ValueError(msg)

        if len(source_lang) != 2 or len(target_lang) != 2:
            msg = f"Invalid language codes: {source_lang} -> {target_lang}"
            raise ValueError(msg)

        executable = shutil.which("argos-translate")
        if not executable:
            msg = "argos-translate executable not found in PATH"
            raise RuntimeError(msg)

        try:
            args = [
                executable,
                "--from-lang",
                source_lang,
                "--to-lang",
                target_lang,
                text,
            ]
            result = subprocess.run(args, capture_output=True, text=True, check=True)  # noqa: S603
            translated_text = result.stdout.strip()
            if not translated_text:
                msg = "Translation returned empty result"
                raise RuntimeError(msg)
            return {
                "translated_text": translated_text,
                "source_lang": source_lang,
                "target_lang": target_lang,
                "source": "argos",
            }
        except subprocess.CalledProcessError as e:
            error_msg = (
                e.stderr.decode()
                if isinstance(e.stderr, bytes)
                else (e.stderr or str(e))
            )
            msg = f"Argos Translate CLI error: {error_msg}"
            raise RuntimeError(msg)
        except Exception as e:
            msg = f"Argos Translate CLI error: {e!s}"
            raise RuntimeError(msg)

    def _detect_language(self, text: str) -> str | None:
        if not self.has_argos_lib:
            return None

        try:
            from argostranslate import translate

            installed_packages = package.get_installed_packages()
            if not installed_packages:
                return None

            detected = translate.detect_language(text)
            if detected:
                return detected.code
        except Exception as e:
            print(f"Language detection failed: {e}")

        return None

    def _get_argos_languages_cli(self) -> list[dict[str, str]]:
        languages = []
        argospm = shutil.which("argospm")
        if not argospm:
            return languages

        try:
            result = subprocess.run(  # noqa: S603
                [argospm, "list"],
                capture_output=True,
                text=True,
                timeout=10,
                check=True,
            )
            installed_packages = result.stdout.strip().split("\n")
            argos_langs = set()

            for pkg_name in installed_packages:
                if not pkg_name.strip():
                    continue
                match = re.match(r"translate-([a-z]{2})_([a-z]{2})", pkg_name.strip())
                if match:
                    from_code = match.group(1)
                    to_code = match.group(2)
                    argos_langs.add(from_code)
                    argos_langs.add(to_code)

            for code in sorted(argos_langs):
                name = LANGUAGE_CODE_TO_NAME.get(code, code.upper())
                languages.append(
                    {
                        "code": code,
                        "name": name,
                        "source": "argos",
                    },
                )
        except subprocess.CalledProcessError as e:
            print(f"argospm list failed: {e.stderr or str(e)}")
        except Exception as e:
            print(f"Error parsing argospm output: {e}")

        return languages

    def install_language_package(
        self,
        package_name: str = "translate",
    ) -> dict[str, Any]:
        argospm = shutil.which("argospm")
        if not argospm:
            msg = "argospm not found in PATH. Install argostranslate first."
            raise RuntimeError(msg)

        try:
            result = subprocess.run(  # noqa: S603
                [argospm, "install", package_name],
                capture_output=True,
                text=True,
                timeout=300,
                check=True,
            )
            return {
                "success": True,
                "message": f"Successfully installed {package_name}",
                "output": result.stdout,
            }
        except subprocess.TimeoutExpired:
            msg = f"Installation of {package_name} timed out after 5 minutes"
            raise RuntimeError(msg)
        except subprocess.CalledProcessError as e:
            msg = f"Failed to install {package_name}: {e.stderr or str(e)}"
            raise RuntimeError(msg)
        except Exception as e:
            msg = f"Error installing {package_name}: {e!s}"
            raise RuntimeError(msg)
