"""Contratos STT/TTS independientes del proveedor (cloud o local)."""

from __future__ import annotations

from abc import ABC, abstractmethod
from collections.abc import AsyncIterator
from dataclasses import dataclass
from typing import Literal

AudioEncoding = Literal["mulaw", "linear16"]


@dataclass(frozen=True)
class AudioFormat:
    """Formato telefónico por defecto: 8 kHz mono."""

    encoding: AudioEncoding = "mulaw"
    sample_rate_hz: int = 8000
    channels: int = 1


@dataclass(frozen=True)
class TranscriptResult:
    text: str
    is_final: bool = True
    confidence: float | None = None


class SpeechToText(ABC):
    """Transcribe audio del cliente → texto.

    Implementaciones: ``voice.stt.google_stt.GoogleSpeechToText``,
    ``voice.stt.local_stt.LocalSpeechToText`` (futuro).
    """

    provider_name: str

    @abstractmethod
    async def recognize(
        self,
        audio: bytes,
        *,
        audio_format: AudioFormat | None = None,
        language_code: str | None = None,
    ) -> TranscriptResult:
        """Reconoce un utterance completo (batch)."""

    async def stream_recognize(
        self,
        chunks: AsyncIterator[bytes],
        *,
        audio_format: AudioFormat | None = None,
        language_code: str | None = None,
    ) -> AsyncIterator[TranscriptResult]:
        """Streaming opcional; por defecto concatena y usa ``recognize``."""
        buffer = bytearray()
        async for chunk in chunks:
            buffer.extend(chunk)
        if buffer:
            yield await self.recognize(
                bytes(buffer),
                audio_format=audio_format,
                language_code=language_code,
            )

    async def close(self) -> None:
        return None


class TextToSpeech(ABC):
    """Sintetiza texto del bot → audio PCM/μ-law para RTP.

    Implementaciones: ``voice.tts.google_tts.GoogleTextToSpeech``,
    ``voice.tts.elevenlabs_tts.ElevenLabsTextToSpeech``,
    ``voice.tts.local_tts.LocalTextToSpeech`` (futuro).
    """

    provider_name: str

    @abstractmethod
    async def synthesize(
        self,
        text: str,
        *,
        audio_format: AudioFormat | None = None,
        voice: str | None = None,
        language_code: str | None = None,
    ) -> bytes:
        """Devuelve audio en el encoding pedidos (μ-law o PCM 16-bit LE)."""

    async def close(self) -> None:
        return None
