"""
Google Cloud Translation Provider — API officielle v2 (Basic).

Utilise l'API REST Cloud Translation v2 avec une clé API Google Cloud.
Contrairement à google_provider.py (deep_translator, accès web non officiel),
ce provider est l'implémentation officielle et facturable.

Tarification (avril 2026) :
  - 500 000 caractères/mois gratuits (par projet GCP)
  - Au-delà : ~$20 / million de caractères

Prérequis :
  1. Activer « Cloud Translation API » dans Google Cloud Console
  2. Créer une clé API (restreinte à l'API Cloud Translation)
  3. Définir GOOGLE_CLOUD_API_KEY dans .env ou dans les paramètres admin
"""

import time
import threading
from typing import Any, Dict, List, Optional

import requests

from core.logging import get_logger
from .base import TranslationProvider
from .schemas import (
    BatchTranslationRequest,
    BatchTranslationResponse,
    ProviderHealthStatus,
    TranslationRequest,
    TranslationResponse,
)

logger = get_logger(__name__)

_TRANSLATE_URL = "https://translation.googleapis.com/language/translate/v2"
_MAX_CHARS_PER_REQUEST = 30_000  # Cloud Translation v2 recommends ≤ 30 000 chars

# Codes d'erreur internes
GC_QUOTA_EXCEEDED = "GC_QUOTA_EXCEEDED"
GC_INVALID_KEY = "GC_INVALID_KEY"
GC_API_NOT_ENABLED = "GC_API_NOT_ENABLED"
GC_NETWORK_ERROR = "GC_NETWORK_ERROR"
GC_UNSUPPORTED_LANGUAGE = "GC_UNSUPPORTED_LANGUAGE"
GC_TEXT_TOO_LONG = "GC_TEXT_TOO_LONG"

_RETRYABLE_CODES = {GC_QUOTA_EXCEEDED, GC_NETWORK_ERROR}

# Mapping des codes de langue pour aligner avec l'API Cloud Translation
_LANG_MAP: dict[str, str] = {
    "auto": "",  # Cloud v2 : source vide = détection auto
    "iw": "he",  # hébreu
    "jv": "jw",  # javanais
    "nb": "no",  # norvégien bokmål
}


def _normalize_lang(code: str) -> str:
    if not code or code == "auto":
        return ""  # Cloud v2 détecte automatiquement si source vide
    return _LANG_MAP.get(code.lower(), code)


class GoogleCloudProviderError(Exception):
    def __init__(self, code: str, message: str, details: Optional[Dict[str, Any]] = None):
        self.code = code
        self.message = message
        self.details = details or {}
        super().__init__(message)


class GoogleCloudTranslationProvider(TranslationProvider):
    """
    Fournisseur Google Cloud Translation API v2 (Basic).

    Utilise des requêtes REST avec clé API.
    Réservé aux forfaits payants (Pro, Business, Enterprise).
    """

    def __init__(
        self,
        api_key: str,
        timeout: int = 30,
        max_retries: int = 3,
        retry_delay: float = 1.0,
    ):
        if not api_key:
            raise ValueError("GoogleCloudTranslationProvider requiert une clé API.")
        self._api_key = api_key
        self._timeout = timeout
        self._max_retries = max_retries
        self._retry_delay = retry_delay
        self._provider_name = "google_cloud"
        self._health_cache: Dict[str, Any] = {}
        self._health_cache_ttl = 60
        self._health_cache_lock = threading.Lock()
        self._session = requests.Session()

    def get_name(self) -> str:
        return self._provider_name

    def is_available(self) -> bool:
        current_time = time.time()
        with self._health_cache_lock:
            cached = self._health_cache.get("is_available")
            if cached and current_time - cached["timestamp"] < self._health_cache_ttl:
                return cached["value"]

        try:
            resp = self._session.post(
                _TRANSLATE_URL,
                params={"key": self._api_key},
                json={"q": "hello", "target": "fr", "format": "text"},
                timeout=self._timeout,
            )
            available = resp.ok
        except Exception:
            available = False

        with self._health_cache_lock:
            self._health_cache["is_available"] = {"value": available, "timestamp": current_time}

        return available

    def _call_api(self, texts: List[str], target_lang: str, source_lang: str) -> List[str]:
        """
        Appel REST à Cloud Translation API v2.

        Envoie plusieurs textes en une seule requête (batch natif).
        Lève GoogleCloudProviderError en cas d'erreur.
        """
        payload: Dict[str, Any] = {
            "q": texts,
            "target": target_lang,
            "format": "text",
        }
        if source_lang:  # vide = détection auto
            payload["source"] = source_lang

        try:
            resp = self._session.post(
                _TRANSLATE_URL,
                params={"key": self._api_key},
                json=payload,
                timeout=self._timeout,
            )
        except requests.Timeout:
            raise GoogleCloudProviderError(
                GC_NETWORK_ERROR,
                "Délai dépassé pour Google Cloud Translation.",
            )
        except requests.ConnectionError as exc:
            raise GoogleCloudProviderError(
                GC_NETWORK_ERROR,
                "Impossible de joindre Google Cloud Translation.",
                {"original_error": str(exc)[:200]},
            )

        if resp.status_code == 200:
            translations = resp.json().get("data", {}).get("translations", [])
            return [t.get("translatedText", "") for t in translations]

        # Gestion des erreurs HTTP
        try:
            err_body = resp.json()
            err_msg = err_body.get("error", {}).get("message", resp.text[:200])
            err_status = err_body.get("error", {}).get("status", "")
        except Exception:
            err_msg = resp.text[:200]
            err_status = ""

        if resp.status_code in (401, 403) or "API_KEY" in err_status:
            raise GoogleCloudProviderError(
                GC_INVALID_KEY,
                f"Clé API Google Cloud invalide ou API non activée : {err_msg}",
                {"http_status": resp.status_code},
            )
        if resp.status_code == 429 or "QUOTA" in err_status or "RATE_LIMIT" in err_status:
            raise GoogleCloudProviderError(
                GC_QUOTA_EXCEEDED,
                "Quota Google Cloud Translation dépassé. Réessayez plus tard.",
                {"http_status": resp.status_code},
            )
        raise GoogleCloudProviderError(
            GC_NETWORK_ERROR,
            f"Erreur Google Cloud Translation HTTP {resp.status_code}: {err_msg}",
            {"http_status": resp.status_code},
        )

    def translate_text(self, request: TranslationRequest) -> TranslationResponse:
        text = request.text
        target = _normalize_lang(request.target_language)
        source = _normalize_lang(request.source_language or "auto")

        if not text or not text.strip():
            return TranslationResponse(
                translated_text=text,
                provider_name=self._provider_name,
                from_cache=False,
            )

        if source and source == target:
            return TranslationResponse(
                translated_text=text,
                provider_name=self._provider_name,
                from_cache=False,
                source_language=source,
            )

        last_error: Optional[GoogleCloudProviderError] = None
        retries = 0

        while retries <= self._max_retries:
            try:
                results = self._call_api([text], target, source)
                translated = results[0] if results else text
                logger.info(
                    "google_cloud_translation_success",
                    chars=len(text),
                    target_lang=target,
                    retries=retries,
                )
                return TranslationResponse(
                    translated_text=translated,
                    provider_name=self._provider_name,
                    from_cache=False,
                )
            except GoogleCloudProviderError as exc:
                last_error = exc
                if exc.code not in _RETRYABLE_CODES:
                    break
                retries += 1
                if retries <= self._max_retries:
                    time.sleep(self._retry_delay * (2 ** (retries - 1)))
            except Exception as exc:
                last_error = GoogleCloudProviderError(
                    GC_NETWORK_ERROR,
                    "Erreur inattendue Google Cloud Translation.",
                    {"original_error": str(exc)[:200]},
                )
                retries += 1
                if retries <= self._max_retries:
                    time.sleep(self._retry_delay * (2 ** (retries - 1)))

        logger.error(
            "google_cloud_translation_failed",
            error_code=last_error.code if last_error else "UNKNOWN",
            chars=len(text),
            target_lang=target,
        )
        return TranslationResponse(
            translated_text=text,
            provider_name=self._provider_name,
            from_cache=False,
            error=last_error.message if last_error else "Erreur inconnue",
            error_code=last_error.code if last_error else GC_NETWORK_ERROR,
            error_details=last_error.details if last_error else {},
        )

    def translate_batch(self, requests: List[TranslationRequest]) -> List[TranslationResponse]:
        """
        Traduit plusieurs textes en utilisant le batch natif de Cloud Translation v2.

        Regroupe les requêtes partageant la même paire source/cible dans un seul
        appel API (meilleur rapport qualité/coût).
        """
        if not requests:
            return []

        tgt0 = _normalize_lang(requests[0].target_language)
        src0 = _normalize_lang(requests[0].source_language or "auto")
        uniform = all(
            _normalize_lang(r.target_language) == tgt0
            and _normalize_lang(r.source_language or "auto") == src0
            for r in requests
        )

        if uniform:
            texts = [r.text for r in requests]
            last_error: Optional[GoogleCloudProviderError] = None
            retries = 0
            while retries <= self._max_retries:
                try:
                    results = self._call_api(texts, tgt0, src0)
                    logger.info(
                        "google_cloud_batch_success",
                        count=len(texts),
                        target_lang=tgt0,
                    )
                    return [
                        TranslationResponse(
                            translated_text=r,
                            provider_name=self._provider_name,
                            from_cache=False,
                        )
                        for r in results
                    ]
                except GoogleCloudProviderError as exc:
                    last_error = exc
                    if exc.code not in _RETRYABLE_CODES:
                        break
                    retries += 1
                    if retries <= self._max_retries:
                        time.sleep(self._retry_delay * (2 ** (retries - 1)))
                except Exception as exc:
                    last_error = GoogleCloudProviderError(
                        GC_NETWORK_ERROR, str(exc)[:200]
                    )
                    retries += 1
                    if retries <= self._max_retries:
                        time.sleep(self._retry_delay * (2 ** (retries - 1)))

            # Batch failed — renvoyer l'erreur pour chaque texte
            err_msg = last_error.message if last_error else "Erreur inconnue"
            err_code = last_error.code if last_error else GC_NETWORK_ERROR
            return [
                TranslationResponse(
                    translated_text=r.text,
                    provider_name=self._provider_name,
                    from_cache=False,
                    error=err_msg,
                    error_code=err_code,
                )
                for r in requests
            ]

        # Paires source/cible hétérogènes : appel individuel
        return [self.translate_text(req) for req in requests]

    def health_check(self) -> ProviderHealthStatus:
        current_time = time.time()
        with self._health_cache_lock:
            cached = self._health_cache.get("health_check")
            if cached and current_time - cached["timestamp"] < self._health_cache_ttl:
                return cached["value"]

        from datetime import datetime, timezone

        start = time.time()
        last_check_iso = datetime.now(timezone.utc).isoformat()
        try:
            available = self.is_available()
            latency_ms = (time.time() - start) * 1000
            status = ProviderHealthStatus(
                name=self._provider_name,
                available=available,
                latency_ms=round(latency_ms, 2),
                error=None if available else "Clé API invalide ou quota dépassé",
                last_check=last_check_iso,
            )
        except Exception as exc:
            latency_ms = (time.time() - start) * 1000
            status = ProviderHealthStatus(
                name=self._provider_name,
                available=False,
                latency_ms=round(latency_ms, 2),
                error=str(exc)[:200],
                last_check=last_check_iso,
            )

        with self._health_cache_lock:
            self._health_cache["health_check"] = {"value": status, "timestamp": current_time}

        return status


class LegacyGoogleCloudAdapter:
    """
    Adapteur exposant GoogleCloudTranslationProvider via l'interface legacy
    (.translate / .translate_batch) utilisée par translation_service.
    """

    def __init__(self, api_key: str):
        self._provider = GoogleCloudTranslationProvider(api_key=api_key)
        self.provider_name = "google_cloud"

    def translate(self, text: str, target_language: str, source_language: str = "auto") -> str:
        from .schemas import TranslationRequest
        resp = self._provider.translate_text(
            TranslationRequest(text=text, target_language=target_language, source_language=source_language)
        )
        if resp.error:
            from utils.exceptions import TranslationProviderError
            raise TranslationProviderError(resp.error_code or "UNKNOWN", resp.error, resp.error_details)
        return resp.translated_text

    def translate_batch(
        self, texts: List[str], target_language: str, source_language: str = "auto", batch_size: int = 50
    ) -> List[str]:
        from .schemas import TranslationRequest
        reqs = [
            TranslationRequest(text=t, target_language=target_language, source_language=source_language)
            for t in texts
        ]
        responses = self._provider.translate_batch(reqs)
        result = []
        for r in responses:
            if r.error:
                from utils.exceptions import TranslationProviderError
                raise TranslationProviderError(r.error_code or "UNKNOWN", r.error, r.error_details)
            result.append(r.translated_text)
        return result
