"""
TTS Injection Streams

Injects synthesized speech (PCM audio) back into an active call or conference.

Two transport options:
  1. WebSocket (low-latency, streaming) — preferred for real-time TTS
  2. HTTP POST (simple, fire-and-forget) — for short announcements

Audio format: 16kHz, 16-bit signed PCM (slin16), little-endian
"""

from __future__ import annotations

import uuid
from typing import AsyncIterable

from dvgateway.transport.http_client import HttpClient
from dvgateway.transport.ws_pool import WsPool, WsSubscription
from dvgateway.types import Logger


class TtsInjector:
    """Injects synthesized speech into active calls or conferences."""

    def __init__(self, ws_pool: WsPool, http: HttpClient, logger: Logger) -> None:
        self._ws_pool = ws_pool
        self._http = http
        self._logger = logger

    async def inject_streaming(
        self,
        linked_id: str,
        audio_chunks: AsyncIterable[bytes],
    ) -> None:
        """
        Inject TTS audio into a 1:1 call via streaming WebSocket.

        Best for real-time TTS where audio arrives in chunks — first audio
        is injected immediately, minimising latency (target: <150ms TTFA).
        """
        sub_id = f"tts-ws-{linked_id}-{uuid.uuid4()}"
        ws_ready = False
        pending_send: list[bytes] = []

        def on_connected(sid: str) -> None:
            nonlocal ws_ready
            if sid != sub_id:
                return
            ws_ready = True
            for buf in pending_send:
                import asyncio
                asyncio.ensure_future(self._ws_pool.send(sub_id, buf))
            pending_send.clear()

        self._ws_pool.once("connected", on_connected)

        await self._ws_pool.subscribe(WsSubscription(
            id=sub_id,
            path=f"/api/v1/ws/tts/{linked_id}",
            params={},
            on_message=lambda _: None,  # TTS WS is write-only
            on_close=lambda: self._logger.debug({"linked_id": linked_id}, "TTS WebSocket closed"),
        ))

        try:
            async for chunk in audio_chunks:
                if ws_ready:
                    await self._ws_pool.send(sub_id, chunk)
                else:
                    pending_send.append(chunk)
        finally:
            self._ws_pool.unsubscribe(sub_id)

    async def inject_direct(self, linked_id: str, pcm_buffer: bytes) -> None:
        """Inject a pre-buffered PCM audio clip into a call via HTTP POST."""
        await self._http.post_binary(
            f"/api/v1/tts/{linked_id}",
            pcm_buffer,
            "audio/pcm;rate=16000",
        )
        self._logger.debug({"linked_id": linked_id, "bytes": len(pcm_buffer)}, "TTS injected via HTTP")

    async def broadcast_to_conference(
        self,
        conf_id: str,
        audio_chunks: AsyncIterable[bytes],
    ) -> None:
        """Broadcast TTS audio to all participants in a conference."""
        chunks: list[bytes] = []
        async for chunk in audio_chunks:
            chunks.append(chunk)
        full = b"".join(chunks)

        await self._http.post_binary(
            f"/api/v1/tts/conf/{conf_id}",
            full,
            "audio/pcm;rate=16000",
        )
        self._logger.debug({"conf_id": conf_id, "bytes": len(full)}, "TTS broadcast to conference")
