"""TTS engine layer — wraps pyttsx3 (Windows SAPI) with timeout and error handling."""
import threading

import pyttsx3


class TTSUnavailableError(Exception):
    """Raised when the TTS engine cannot be initialized or fails during speech."""


class TTSTimeoutError(Exception):
    """Raised when TTS playback exceeds the allowed duration."""


class TTSEngine:
    """Thin wrapper around pyttsx3 that enforces a per-call timeout."""

    def __init__(self) -> None:
        try:
            self._engine = pyttsx3.init()
        except Exception as exc:
            raise TTSUnavailableError(
                f"TTS engine initialization failed: {exc}"
            ) from exc

    def speak(self, text: str, timeout: float = 5.0) -> None:
        """Speak *text* synchronously, raising TTSTimeoutError if it takes longer than *timeout* seconds."""
        error: list[Exception] = []
        completed = threading.Event()

        def _run() -> None:
            try:
                self._engine.say(text)
                self._engine.runAndWait()
            except Exception as exc:
                error.append(exc)
            finally:
                completed.set()

        thread = threading.Thread(target=_run, daemon=True)
        thread.start()
        finished = completed.wait(timeout=timeout)

        if not finished:
            self._engine.stop()
            thread.join(timeout=1.0)
            raise TTSTimeoutError(
                f"TTS playback timed out after {timeout:.1f}s"
            )

        if error:
            raise TTSUnavailableError(
                f"TTS playback failed: {error[0]}"
            ) from error[0]
