"""TTS client for CosyVoice3 Singlish TTS server.

Sends text to the TTS HTTP endpoint and returns the streaming WAV response
as raw bytes.  The orchestrator can then forward the audio to the dashboard
for operator-side playback and/or to the robot's speaker on the Ghost PC.
"""

from __future__ import annotations

import logging
import os
from dataclasses import dataclass, field
from typing import Optional

logger = logging.getLogger(__name__)


@dataclass
class TTSConfig:
    """Configuration for the TTS client.

    Attributes:
        endpoint: Full URL for the TTS REST endpoint.
        timeout_s: Request timeout in seconds (covers full streaming response).
    """

    endpoint: str = field(
        default_factory=lambda: os.getenv(
            "TTS_ENDPOINT", "http://kluster.klass.dev:8200/tts"
        )
    )
    timeout_s: int = 15


class TTSClient:
    """Async HTTP client for the CosyVoice3 TTS server.

    Usage::

        client = TTSClient()
        wav_bytes = await client.speak("Hello, are you okay?")
        if wav_bytes:
            # wav_bytes contains the raw WAV audio data
            ...
        await client.close()

    The TTS server (POST /tts) accepts::

        {"text": "...", "sample_rate": 16000}

    and responds with a streaming ``audio/wav`` response.  The client
    collects the full stream and returns it as ``bytes`` (or ``None``
    on failure).
    """

    def __init__(self, config: Optional[TTSConfig] = None) -> None:
        self.config = config or TTSConfig()
        self._session: Optional[object] = None  # aiohttp.ClientSession, typed as object for optional dep

    async def speak(self, text: str, sample_rate: int = 16000) -> Optional[bytes]:
        """Send text to TTS server and return the WAV audio bytes.

        Args:
            text: The message to synthesise.
            sample_rate: Audio sample rate in Hz (default 16000).

        Returns:
            Raw WAV bytes on success, None on any error.
        """
        if not text:
            logger.warning("TTSClient.speak called with empty text — skipping")
            return None

        try:
            import aiohttp
        except ImportError:
            logger.error(
                "aiohttp is not installed — cannot call TTS server. "
                "Install it with: pip install aiohttp"
            )
            return None

        if self._session is None:
            self._session = aiohttp.ClientSession()

        try:
            timeout = aiohttp.ClientTimeout(total=self.config.timeout_s)
            async with self._session.post(  # type: ignore[union-attr]
                self.config.endpoint,
                json={"text": text, "sample_rate": sample_rate},
                timeout=timeout,
            ) as resp:
                if resp.status == 200:
                    # Collect the streaming WAV into a buffer
                    chunks = bytearray()
                    async for chunk in resp.content.iter_any():
                        chunks.extend(chunk)
                    logger.info("TTS spoke: %r (%d bytes)", text[:80], len(chunks))
                    return bytes(chunks)

                error_body = await resp.text()
                logger.error(
                    "TTS server returned %d: %s", resp.status, error_body[:200]
                )
                return None

        except Exception as exc:
            logger.error("TTS request failed: %s", exc)
            return None

    async def close(self) -> None:
        """Close the underlying aiohttp session."""
        if self._session is not None:
            await self._session.close()  # type: ignore[union-attr]
            self._session = None
