"""本地 faster-whisper 引擎（CTranslate2，CPU-first）。多语，对中英混说有基础能力。

依赖：pip install '.[whisper]'
"""

from __future__ import annotations

import time

from ..metrics.resource import dir_size_mb
from .base import Engine, Transcript


class WhisperEngine(Engine):
    kind = "offline"
    is_local = True

    def __init__(
        self,
        name: str = "whisper-small",
        model_size: str = "small",
        device: str = "cpu",
        compute_type: str = "int8",
        model_dir: str | None = None,
    ):
        self.name = name
        self.model_size = model_size
        self.device = device
        self.compute_type = compute_type
        self.model_dir = model_dir
        self.model = None
        self._weights_path: str | None = None

    def load(self) -> None:
        from faster_whisper import WhisperModel

        self.model = WhisperModel(
            self.model_size,
            device=self.device,
            compute_type=self.compute_type,
            download_root=self.model_dir,
        )
        self._resolve_weights_path()

    def _resolve_weights_path(self) -> None:
        # 解析磁盘权重路径用于报模型大小（best-effort）
        try:
            from huggingface_hub import snapshot_download

            repo = f"Systran/faster-whisper-{self.model_size}"
            self._weights_path = snapshot_download(repo, local_files_only=True, cache_dir=self.model_dir)
        except Exception:
            self._weights_path = self.model_dir

    def transcribe(self, audio_path: str) -> Transcript:
        if self.model is None:
            self.load()
        t0 = time.monotonic()
        # language=None → 自动检测；中英混说交给模型自身
        segments, info = self.model.transcribe(audio_path, language=None, beam_size=5)
        text = "".join(seg.text for seg in segments)  # 生成器在此 join 时才真正解码
        proc = time.monotonic() - t0
        return Transcript(text=text.strip(), audio_sec=float(info.duration), proc_sec=proc)

    def model_size_mb(self) -> float | None:
        size = dir_size_mb(self._weights_path) if self._weights_path else 0.0
        return size or None

    def unload(self) -> None:
        self.model = None
