"""Text-to-speech mixin — Piper TTS and macOS ``say`` fallback."""

from __future__ import annotations

import logging
import subprocess
import tempfile
import time
from pathlib import Path

logger = logging.getLogger(__name__)


class TTSMixin:
    """Mixin providing TTS methods for :class:`VoiceLoop`."""

    def _speak(self, text: str) -> None:
        """Speak text aloud using Piper TTS or macOS `say`."""
        if not text:
            return
        self._speaking = True
        try:
            if self.config.use_say_fallback:
                self._speak_say(text)
            else:
                self._speak_piper(text)
        finally:
            self._speaking = False

    def _speak_piper(self, text: str) -> None:
        """Speak using Piper TTS (local ONNX inference)."""
        with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
            tmp_path = tmp.name
        try:
            cmd = ["piper", "--model", str(self.config.piper_voice), "--output_file", tmp_path]
            proc = subprocess.run(cmd, input=text, capture_output=True, text=True, timeout=30)
            if proc.returncode != 0:
                logger.error("Piper failed: %s", proc.stderr)
                self._speak_say(text)
                return
            self._play_audio(tmp_path)
        finally:
            Path(tmp_path).unlink(missing_ok=True)

    def _speak_say(self, text: str) -> None:
        """Speak using macOS `say` command."""
        try:
            proc = subprocess.Popen(
                ["say", "-r", "180", text],
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL,
            )
            proc.wait(timeout=60)
        except subprocess.TimeoutExpired:
            proc.kill()
        except FileNotFoundError:
            logger.error("macOS `say` command not found")

    def _play_audio(self, path: str) -> None:
        """Play a WAV file. Can be interrupted by setting self._interrupted."""
        try:
            proc = subprocess.Popen(
                ["afplay", path],
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL,
            )
            while proc.poll() is None:
                if self._interrupted:
                    proc.terminate()
                    self._interrupted = False
                    logger.info("TTS interrupted by user")
                    return
                time.sleep(0.05)
        except FileNotFoundError:
            try:
                subprocess.run(["aplay", path], capture_output=True, timeout=60)
            except (FileNotFoundError, subprocess.TimeoutExpired):
                logger.error("No audio player found (tried afplay, aplay)")
