"""
Track C1.1 — Provider interface adapters.

Two adapter classes that bridge the legacy `TranslationProvider`
interface (translate(text, target, source) -> str) and the new
TranslationProvider interface (translate_text(request) -> Response).

Why?
  The codebase has TWO provider interfaces:
    1. New: services/providers/base.py
       - translate_text(TranslationRequest) -> TranslationResponse
       - translate_batch(List[TranslationRequest]) -> List[TranslationResponse]
    2. Legacy: services/translation_service.py
       - translate(text, target, source) -> str
       - translate_batch(texts, target, source) -> List[str]

  Both are used in production. Translators sometimes receive the new
  interface, sometimes the legacy one. Previously the translators
  duck-typed the provider to figure out which interface it supported
  (see e.g. pptx_translator._translate_with_provider). That was hacky.

  These adapters give a single, explicit way to bridge the two
  interfaces, so translators can always treat the provider as
  whichever interface they prefer.

Usage:
    # Wrap a new-style provider as legacy
    legacy = NewProviderAsLegacyAdapter(new_provider)
    legacy.translate("Hello", "fr", "auto")  # returns str

    # Wrap a legacy-style provider as new
    new = LegacyProviderAsNewAdapter(legacy_provider)
    new.translate_text(TranslationRequest(...))  # returns TranslationResponse
"""

from __future__ import annotations

from typing import List, Optional

from core.logging import get_logger

from .base import TranslationProvider as NewTranslationProvider
from .schemas import TranslationRequest, TranslationResponse, ProviderHealthStatus

logger = get_logger(__name__)


# =============================================================================
# New-style → Legacy
# =============================================================================

class NewProviderAsLegacyAdapter:
    """Adapt a new-style provider (translate_text) to the legacy interface.

    The legacy interface is what most translators and the legacy
    `translation_service` expect:
        translate(text, target_language, source_language="auto") -> str
        translate_batch(texts, target, source) -> List[str]
        get_name() -> str
        is_available() -> bool

    This adapter is for INTERNAL use — it adapts at the boundary so
    that a single provider instance can be passed to any code path.
    """

    def __init__(self, new_provider: NewTranslationProvider):
        self._new = new_provider

    # ---- Core legacy methods ----

    def translate(
        self, text: str, target_language: str, source_language: str = "auto"
    ) -> str:
        """Translate a single text — legacy signature."""
        # Lazy import to avoid circular import
        try:
            from .schemas import TranslationRequest
            request = TranslationRequest(
                text=text,
                target_language=target_language,
                source_language=source_language,
            )
            response = self._new.translate_text(request)
            if response.error:
                raise Exception(
                    f"[{response.error_code or 'UNKNOWN'}] {response.error}"
                )
            return response.translated_text
        except Exception as e:
            # If the new provider raised, re-raise so the caller
            # can decide how to handle it (legacy code expects exceptions).
            logger.debug(
                "new_provider_as_legacy_translate_error",
                error=str(e)[:200],
            )
            raise

    def translate_batch(
        self,
        texts: List[str],
        target_language: str,
        source_language: str = "auto",
    ) -> List[str]:
        """Translate multiple texts — legacy signature."""
        if not texts:
            return []

        try:
            from .schemas import TranslationRequest
            requests = [
                TranslationRequest(
                    text=t,
                    target_language=target_language,
                    source_language=source_language,
                )
                for t in texts
            ]
            responses = self._new.translate_batch(requests)
            return [r.translated_text for r in responses]
        except Exception as e:
            logger.debug(
                "new_provider_as_legacy_batch_error",
                error=str(e)[:200],
                count=len(texts),
            )
            raise

    # ---- Identity methods (pass-through) ----

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

    def is_available(self) -> bool:
        try:
            return bool(self._new.is_available())
        except Exception:
            return False

    def health_check(self) -> dict:
        """Best-effort health check, legacy-shaped (plain dict)."""
        try:
            status = self._new.health_check()
            return {
                "name": status.name,
                "available": status.available,
                "latency_ms": status.latency_ms,
                "error": status.error,
            }
        except Exception as e:
            return {
                "name": self.get_name(),
                "available": False,
                "error": str(e)[:200],
            }


# =============================================================================
# Legacy-style → New
# =============================================================================

class LegacyProviderAsNewAdapter(NewTranslationProvider):
    """Adapt a legacy-style provider (translate(text, ...)) to the new interface.

    Most new code expects the new interface:
        translate_text(TranslationRequest) -> TranslationResponse
        translate_batch(List[TranslationRequest]) -> List[TranslationResponse]

    A "legacy" provider is anything that has:
        translate(text, target, source) -> str
        translate_batch(texts, target, source) -> List[str]
        get_name() -> str
        is_available() -> bool
    """

    def __init__(self, legacy_provider, provider_name: str = None):
        self._legacy = legacy_provider
        # If a custom name is provided, use it; otherwise inherit from the
        # legacy provider.
        if provider_name is not None:
            self._name = provider_name
        else:
            try:
                self._name = legacy_provider.get_name()
            except Exception:
                self._name = "legacy"

    # ---- Implement the abstract methods of NewTranslationProvider ----

    def translate_text(self, request: TranslationRequest) -> TranslationResponse:
        """Translate a single text — new interface."""
        start = time.time()
        try:
            translated = self._legacy.translate(
                text=request.text,
                target_language=request.target_language,
                source_language=request.source_language or "auto",
            )
            elapsed_ms = round((time.time() - start) * 1000, 2)
            return TranslationResponse(
                translated_text=translated or "",
                provider_name=self._name,
                source_language=request.source_language or "auto",
                target_language=request.target_language,
                elapsed_ms=elapsed_ms,
            )
        except Exception as e:
            elapsed_ms = round((time.time() - start) * 1000, 2)
            return TranslationResponse(
                translated_text="",
                provider_name=self._name,
                source_language=request.source_language or "auto",
                target_language=request.target_language,
                elapsed_ms=elapsed_ms,
                error=str(e)[:200],
                error_code="LEGACY_PROVIDER_ERROR",
            )

    def translate_batch(
        self, requests: List[TranslationRequest]
    ) -> List[TranslationResponse]:
        """Translate a batch of texts — new interface."""
        if not requests:
            return []

        # Group by language pair so we can call the legacy
        # translate_batch efficiently (legacy signature is per-language-pair).
        from collections import defaultdict
        groups: dict = defaultdict(list)
        for i, r in enumerate(requests):
            key = (r.target_language, r.source_language or "auto")
            groups[key].append((i, r))

        results: List[TranslationResponse] = [None] * len(requests)  # type: ignore

        for (target_lang, source_lang), items in groups.items():
            texts = [r.text for _, r in items]
            try:
                translated = self._legacy.translate_batch(
                    texts=texts,
                    target_language=target_lang,
                    source_language=source_lang,
                )
            except Exception as e:
                # If legacy batch fails, fall back to per-text
                logger.debug(
                    "legacy_batch_fallback_to_individual",
                    error=str(e)[:200],
                )
                translated = []
                for t in texts:
                    try:
                        translated.append(
                            self._legacy.translate(t, target_lang, source_lang)
                        )
                    except Exception as individual_err:
                        translated.append("")  # legacy code expects str

            for (orig_idx, r), t in zip(items, translated):
                results[orig_idx] = TranslationResponse(
                    translated_text=t or "",
                    provider_name=self._name,
                    source_language=source_lang,
                    target_language=target_lang,
                )

        return results

    def get_name(self) -> str:
        # Always prefer the explicit name set at construction. Only fall
        # back to the legacy provider's get_name if no name was set.
        if hasattr(self, "_name") and self._name:
            return self._name
        try:
            return self._legacy.get_name()
        except Exception:
            return "legacy"

    def is_available(self) -> bool:
        try:
            return bool(self._legacy.is_available())
        except Exception:
            return False


# =============================================================================
# Auto-detect helper
# =============================================================================

import time  # noqa: E402


def coerce_to_legacy(provider) -> NewProviderAsLegacyAdapter:
    """Coerce any provider to the legacy interface.

    If the provider is already a legacy provider, return it as-is.
    If it's a new-style provider, wrap it in NewProviderAsLegacyAdapter.
    """
    if isinstance(provider, NewProviderAsLegacyAdapter):
        return provider
    if isinstance(provider, NewTranslationProvider):
        return NewProviderAsLegacyAdapter(provider)
    # Already legacy-shaped — pass through
    return provider


def coerce_to_new(provider, name: str = None) -> NewTranslationProvider:
    """Coerce any provider to the new interface.

    If the provider is already a new-style provider, return it as-is.
    If it's a legacy provider, wrap it in LegacyProviderAsNewAdapter.
    """
    if isinstance(provider, NewTranslationProvider):
        return provider
    return LegacyProviderAsNewAdapter(provider, provider_name=name)
