import httpx

from app.providers.tts.base import TTSProvider

_API_URL = "https://api.cartesia.ai/tts/bytes"
_API_VERSION = "2024-06-10"

# Sprachen, die sonic-turbo unterstützt (Stand 2026-06).
_LANG_MAP = {
    "de": "de", "en": "en", "fr": "fr", "es": "es",
    "zh": "zh", "cmn": "zh", "pt": "pt", "ja": "ja", "ko": "ko", "hi": "hi",
}

# sonic-turbo unterstützt diese Sprachen nicht — Fallback auf Piper empfohlen.
_UNSUPPORTED_LANGS = {"ar", "it", "ru", "nl", "pl", "sv", "tr"}

_LANG_NAMES = {
    "ar": "Arabisch", "it": "Italienisch", "ru": "Russisch",
    "nl": "Niederländisch", "pl": "Polnisch", "sv": "Schwedisch", "tr": "Türkisch",
}

_GENDER_CODES = {"m", "f", "any"}


def _parse_voice_map(voices_str: str) -> dict[tuple[str, str], dict]:
    """Parst 'lang:gender:uuid:name'-Zeilen in ein (lang,gender)→{uuid,name}-Dict."""
    result = {}
    for line in (voices_str or "").splitlines():
        line = line.strip()
        if not line or line.startswith("#"):
            continue
        parts = line.split(":", 3)
        if len(parts) != 4:
            continue
        lang, gender, uuid, name = [p.strip() for p in parts]
        if lang and gender in ("m", "f") and uuid:
            result[(lang, gender)] = {"uuid": uuid, "name": name}
    return result


class CartesiaTTSProvider(TTSProvider):
    def __init__(
        self,
        api_key: str,
        voice_id: str,
        model: str = "sonic-2",
        sample_rate: int = 24000,
        voices_str: str = "",
    ):
        self.api_key = (api_key or "").strip()
        self.default_voice_id = (voice_id or "").strip()
        self.model = (model or "sonic-2").strip()
        self.sample_rate = int(sample_rate)
        self.voice_map = _parse_voice_map(voices_str)

    def _resolve_voice_id(self, language: str | None, gender_hint: str | None) -> str:
        """Wählt UUID aus Voice-Map. Fallback-Kette: f→m→any→default_voice_id."""
        lang = (language or "de").lower()
        gender = (gender_hint or "any").strip().lower()

        # Präferenz-Reihenfolge: "any" → weiblich bevorzugt
        if gender == "any" or gender not in ("m", "f"):
            candidates = ["f", "m"]
        elif gender == "f":
            candidates = ["f", "m"]
        else:
            candidates = ["m", "f"]

        for g in candidates:
            entry = self.voice_map.get((lang, g))
            if entry:
                return entry["uuid"]

        return self.default_voice_id

    async def synthesize(
        self,
        text: str,
        voice: str | None = None,
        audio_format: str = "pcm",
        language: str | None = None,
    ) -> bytes:
        if not self.api_key:
            raise ValueError("CARTESIA_API_KEY is empty")
        if not text or not text.strip():
            raise ValueError("TTS input text is empty")

        # voice ist entweder ein Geschlechts-Code ("m"/"f"/"any") oder eine direkte UUID
        if voice and voice not in _GENDER_CODES:
            voice_id = voice  # direkte UUID-Übergabe
        else:
            voice_id = self._resolve_voice_id(language, voice)

        if not voice_id:
            raise ValueError("CARTESIA_VOICE_ID ist nicht konfiguriert — bitte UUID in voice-assistant.toml eintragen")

        lang_key = (language or "de").lower()
        if lang_key in _UNSUPPORTED_LANGS:
            lang_label = _LANG_NAMES.get(lang_key, lang_key)
            raise RuntimeError(
                f"Cartesia unterstützt {lang_label} ({lang_key.upper()}) noch nicht als TTS-Sprache"
                " — bitte Piper oder einen anderen Provider wählen"
            )
        lang_code = _LANG_MAP.get(lang_key, "de")

        payload = {
            "model_id": self.model,
            "transcript": text.strip(),
            "voice": {"mode": "id", "id": voice_id},
            "output_format": {
                "container": "raw",
                "encoding": "pcm_s16le",
                "sample_rate": self.sample_rate,
            },
            "language": lang_code,
        }

        timeout = httpx.Timeout(connect=10.0, read=60.0, write=10.0, pool=10.0)
        async with httpx.AsyncClient(timeout=timeout) as client:
            try:
                response = await client.post(
                    _API_URL,
                    headers={
                        "X-API-Key": self.api_key,
                        "Cartesia-Version": _API_VERSION,
                        "Content-Type": "application/json",
                    },
                    json=payload,
                )
                response.raise_for_status()
            except httpx.HTTPStatusError as exc:
                raise RuntimeError(
                    f"Cartesia TTS error {exc.response.status_code}: {exc.response.text}"
                ) from exc
            except httpx.TimeoutException as exc:
                raise RuntimeError("Cartesia TTS timeout") from exc
            except httpx.HTTPError as exc:
                raise RuntimeError(f"Cartesia TTS transport error: {exc}") from exc

        if not response.content:
            raise RuntimeError("Cartesia TTS returned empty audio")
        return response.content
