"""Speech-to-text transcription.

Default language → model mapping (all via faster-whisper):
  "zh"   → Systran/faster-whisper-small  (language=zh, ~250MB)
  "en"   → Systran/faster-whisper-small.en (~250MB, en-only)
  "auto" → Systran/faster-whisper-small   (~250MB, multilingual auto-detect)

All three parameters can be overridden via config:
  stt_model        — any HuggingFace faster-whisper model id
  stt_device       — "cpu" (default) or "cuda"
  stt_compute_type — "int8" (default), "float16", "float32", etc.

Install:
  pip install 'EvoScientist[stt]'
"""

from __future__ import annotations

import asyncio
import logging
from pathlib import Path

logger = logging.getLogger(__name__)

_AUDIO_EXTS = frozenset(
    {
        ".ogg",
        ".mp3",
        ".m4a",
        ".wav",
        ".flac",
        ".opus",
        ".weba",
        ".webm",
    }
)

# Default language → HuggingFace model id
STT_MODELS: dict[str, str] = {
    "zh": "Systran/faster-whisper-small",
    "en": "Systran/faster-whisper-small.en",
    "auto": "Systran/faster-whisper-small",
}

# Single cached engine — re-created only when settings change
_engine: _WhisperEngine | None = None
_engine_key: tuple[str, str, str] | None = None  # (model_id, device, compute_type)


# ── Engine ────────────────────────────────────────────────────────────


class _WhisperEngine:
    """faster-whisper transcription engine."""

    def __init__(self, model_id: str, device: str, compute_type: str) -> None:
        from faster_whisper import WhisperModel  # type: ignore[import]

        self._model = WhisperModel(model_id, device=device, compute_type=compute_type)

    def transcribe(self, file_path: str, language: str | None) -> str:
        segments, _ = self._model.transcribe(
            file_path,
            language=language,
            beam_size=5,
            vad_filter=True,
            vad_parameters={"min_silence_duration_ms": 500},
        )
        # Skip segments where the model is not confident there is real speech
        parts = [s.text.strip() for s in segments if s.no_speech_prob < 0.6]
        return " ".join(parts).strip()


def _get_engine(
    model_id: str,
    device: str = "cpu",
    compute_type: str = "int8",
) -> _WhisperEngine:
    global _engine, _engine_key
    key = (model_id, device, compute_type)
    if _engine is None or _engine_key != key:
        logger.info(
            f"[STT] Loading model '{model_id}' device={device} compute={compute_type}"
        )
        _engine = _WhisperEngine(model_id, device, compute_type)
        _engine_key = key
    return _engine


# ── Public API ────────────────────────────────────────────────────────


def is_audio_file(file_path: str) -> bool:
    """Return True if *file_path* has an audio extension."""
    return Path(file_path).suffix.lower() in _AUDIO_EXTS


async def transcribe_file(
    file_path: str,
    language: str = "auto",
    model: str = "",
    device: str = "cpu",
    compute_type: str = "int8",
) -> str | None:
    """Transcribe an audio file asynchronously.

    Args:
        file_path:    Path to the audio file.
        language:     Language hint — ``"zh"``, ``"en"``, or ``"auto"`` (default).
        model:        Override the HuggingFace model id. Empty = use STT_MODELS mapping.
        device:       Inference device — ``"cpu"`` (default) or ``"cuda"``.
        compute_type: Quantisation — ``"int8"`` (default), ``"float16"``, etc.

    Returns the transcript string, or ``None`` on error / silence.
    """
    if not is_audio_file(file_path):
        return None
    try:
        model_id = model or STT_MODELS.get(language, STT_MODELS["auto"])
        lang: str | None = None if language == "auto" else language
        engine = _get_engine(model_id, device, compute_type)
        loop = asyncio.get_running_loop()
        result: str = await loop.run_in_executor(
            None, engine.transcribe, file_path, lang
        )
        return result or None
    except ImportError as e:
        logger.warning(
            f"[STT] Missing dependency: {e}. "
            "Install with: pip install 'EvoScientist[stt]'"
        )
        return None
    except Exception as e:
        logger.error(f"[STT] Transcription failed for {file_path}: {e}")
        return None
