import asyncio

import httpx

from app.providers.tts.base import TTSProvider

# Preview-TTS-Modelle liefern gelegentlich HTTP 200 mit LEEREM Body oder ein
# transientes 5xx. Solche Aussetzer kurz wiederholen, statt die Runde abzubrechen.
_MAX_ATTEMPTS = 3
_RETRY_BACKOFF = 0.6  # Sekunden, linear ansteigend


class OpenRouterTTSProvider(TTSProvider):
    def __init__(self, api_key: str, model: str, voice: str):
        self.api_key = (api_key or "").strip()
        self.model = (model or "").strip()
        self.voice = (voice or "").strip()

    async def synthesize(
        self,
        text: str,
        voice: str | None = None,
        audio_format: str = "pcm",
        language: str | None = None,  # Cloud-TTS steuert Sprache über Stimme/Modell
    ) -> bytes:
        if not self.api_key:
            raise ValueError("OPENROUTER_API_KEY is empty")
        if not self.model:
            raise ValueError("OPENROUTER_TTS_MODEL is empty")
        if not text or not text.strip():
            raise ValueError("TTS input text is empty")

        effective_voice = (voice or self.voice).strip()
        if not effective_voice:
            raise ValueError("TTS voice is required for OpenRouter TTS")

        payload = {
            "model": self.model,
            "input": text.strip(),
            "voice": effective_voice,
            "response_format": audio_format,
        }

        timeout = httpx.Timeout(connect=10.0, read=120.0, write=30.0, pool=10.0)

        async with httpx.AsyncClient(timeout=timeout) as client:
            last_error = "OpenRouter TTS returned empty audio content"
            for attempt in range(_MAX_ATTEMPTS):
                try:
                    response = await client.post(
                        "https://openrouter.ai/api/v1/audio/speech",
                        headers={
                            "Authorization": f"Bearer {self.api_key}",
                            "Content-Type": "application/json",
                        },
                        json=payload,
                    )
                    response.raise_for_status()
                except httpx.HTTPStatusError as exc:
                    status = exc.response.status_code
                    # 4xx (z. B. ungueltige Stimme) ist nicht transient -> sofort melden.
                    if status < 500:
                        raise RuntimeError(
                            f"OpenRouter TTS error {status}: {exc.response.text}"
                        ) from exc
                    last_error = f"OpenRouter TTS error {status}: {exc.response.text}"
                except httpx.TimeoutException:
                    last_error = "OpenRouter TTS timeout"
                except httpx.HTTPError as exc:
                    raise RuntimeError(f"OpenRouter TTS transport error: {exc}") from exc
                else:
                    if response.content:
                        return response.content
                    # HTTP 200 mit leerem Body -> transienter Aussetzer, erneut versuchen.

                if attempt < _MAX_ATTEMPTS - 1:
                    await asyncio.sleep(_RETRY_BACKOFF * (attempt + 1))

        raise RuntimeError(last_error)

