"""Lokaler STT-Provider auf Basis von faster-whisper (CTranslate2).

Optionale Dependency: `pip install -e .[local]`. Das Whisper-Modell wird beim
ersten Aufruf geladen (und ggf. heruntergeladen) und prozessweit zwischengespeichert.
Die Transkription ist CPU/GPU-lastig und laeuft daher in einem Thread, damit der
Event-Loop frei bleibt.
"""

import asyncio
import io
from functools import lru_cache

from app.config import settings
from app.providers.stt.base import STTProvider


@lru_cache(maxsize=2)
def _load_model(model_size: str, device: str, compute_type: str):
    try:
        from faster_whisper import WhisperModel
    except ModuleNotFoundError as exc:  # pragma: no cover - haengt von Installation ab
        raise RuntimeError(
            "faster-whisper ist nicht installiert. Installieren mit: pip install -e .[local]"
        ) from exc
    try:
        return WhisperModel(model_size, device=device, compute_type=compute_type)
    except Exception:
        # GPU/Compute-Type nicht verfuegbar -> robuster CPU-Fallback (int8).
        if device != "cpu":
            return WhisperModel(model_size, device="cpu", compute_type="int8")
        raise


class FasterWhisperProvider(STTProvider):
    def __init__(self, model_size: str | None = None, device: str | None = None,
                 compute_type: str | None = None):
        self.model_size = model_size or settings.faster_whisper_model
        self.device = device or settings.faster_whisper_device
        self.compute_type = compute_type or settings.faster_whisper_compute_type

    def _transcribe_sync(
        self, audio_bytes: bytes, language: str | None
    ) -> tuple[str, str | None]:
        model = _load_model(self.model_size, self.device, self.compute_type)
        segments, info = model.transcribe(io.BytesIO(audio_bytes), language=language)
        text = "".join(segment.text for segment in segments).strip()
        detected = getattr(info, "language", None)
        return text, detected

    async def transcribe(self, audio_bytes: bytes, fmt: str, language: str | None = None) -> str:
        if not audio_bytes:
            raise ValueError("STT input audio is empty")
        text, _ = await asyncio.to_thread(self._transcribe_sync, audio_bytes, language)
        return text

    async def transcribe_detect(
        self, audio_bytes: bytes, fmt: str, language: str | None = None
    ) -> tuple[str, str | None]:
        if not audio_bytes:
            raise ValueError("STT input audio is empty")
        return await asyncio.to_thread(self._transcribe_sync, audio_bytes, language)
