"""Piper TTS via the Wyoming protocol over TCP."""

import sys

from wyoming.audio import AudioChunk, AudioStart, AudioStop
from wyoming.client import AsyncTcpClient
from wyoming.info import Describe, Info
from wyoming.tts import Synthesize, SynthesizeVoice

from ..audio import wav_duration, write_wav_from_pcm
from ..settings import settings
from .base import TTSEngine, TTSResult


class PiperEngine(TTSEngine):
    """Piper TTS accessed via Wyoming protocol (Docker container)."""

    name = "piper"
    default_voice = "en_US-lessac-medium"

    def __init__(self, host: str, port: int) -> None:
        self._host = host
        self._port = port

    async def synthesize(self, text: str, voice: str | None = None) -> TTSResult:
        voice = voice or self.default_voice

        async with AsyncTcpClient(self._host, self._port) as client:
            # Send synthesize request
            synth = Synthesize(text=text, voice=SynthesizeVoice(name=voice))
            await client.write_event(synth.event())

            # Collect audio chunks
            pcm_chunks: list[bytes] = []
            sample_rate = 22050
            sample_width = 2
            channels = 1

            while True:
                event = await client.read_event()
                if event is None:
                    break

                if AudioStart.is_type(event.type):
                    start = AudioStart.from_event(event)
                    sample_rate = start.rate
                    sample_width = start.width
                    channels = start.channels

                elif AudioChunk.is_type(event.type):
                    chunk = AudioChunk.from_event(event)
                    pcm_chunks.append(chunk.audio)

                elif AudioStop.is_type(event.type):
                    break

        pcm_data = b"".join(pcm_chunks)
        if not pcm_data:
            raise RuntimeError("Piper returned no audio data")

        path = write_wav_from_pcm(
            pcm_data, sample_rate, sample_width, channels, prefix="piper-"
        )

        return TTSResult(
            audio_path=path,
            sample_rate=sample_rate,
            duration_seconds=wav_duration(path),
            engine=self.name,
            voice=voice,
        )

    async def list_voices(self) -> list[str]:
        try:
            async with AsyncTcpClient(self._host, self._port) as client:
                await client.write_event(Describe().event())
                event = await client.read_event()
                if event is None or not Info.is_type(event.type):
                    return []

                info = Info.from_event(event)
                voices = []
                for tts_prog in info.tts:
                    for v in tts_prog.voices:
                        if v.name.lower() not in settings.blacklisted_voices:
                            voices.append(v.name)
                return sorted(voices)
        except (OSError, ConnectionError) as e:
            print(f"Piper voice listing failed: {e}", file=sys.stderr)
            return []

    async def check_health(self) -> dict:
        try:
            async with AsyncTcpClient(self._host, self._port) as client:
                await client.write_event(Describe().event())
                event = await client.read_event()
                if event and Info.is_type(event.type):
                    return {"status": "healthy", "engine": self.name}
        except (OSError, ConnectionError) as e:
            return {"status": "unhealthy", "engine": self.name, "error": str(e)}
        return {"status": "unhealthy", "engine": self.name, "error": "no response"}
