"""OpenAI TTS (Text-to-Speech) client.

REST API-based speech synthesis with streaming support.

Example:
    ```python
    from sdkrouter.tools.audio.tts import OpenAITTS

    tts = OpenAITTS(config)
    response = tts.speech("Hello!", voice="nova")
    Path("output.mp3").write_bytes(response.audio_bytes)
    ```
"""

from __future__ import annotations

import logging
from collections.abc import AsyncIterator, Iterator
from typing import TYPE_CHECKING

from ...._api.client import BaseResource, AsyncBaseResource
from ...._types.audio import (
    SpeechResponse,
    SpeechStreamChunk,
    SpeechStreamDone,
    TTSResponseFormat,
)
from ....exceptions import handle_api_errors, async_api_error_handler

if TYPE_CHECKING:
    from ...._config import SDKConfig

logger = logging.getLogger(__name__)


def _parse_sse_stream(
    lines: Iterator[str],
) -> Iterator[SpeechStreamChunk | SpeechStreamDone]:
    """Parse SSE text/event-stream lines into typed objects."""
    event_type: str | None = None
    for line in lines:
        if line.startswith("event:"):
            event_type = line[len("event:"):].strip()
        elif line.startswith("data:"):
            data_str = line[len("data:"):].strip()
            if event_type == "chunk":
                yield SpeechStreamChunk.model_validate_json(data_str)
            elif event_type == "done":
                yield SpeechStreamDone.model_validate_json(data_str)
            event_type = None
        elif line == "":
            event_type = None


async def _parse_sse_stream_async(
    lines: AsyncIterator[str],
) -> AsyncIterator[SpeechStreamChunk | SpeechStreamDone]:
    """Parse SSE text/event-stream lines into typed objects (async)."""
    event_type: str | None = None
    async for line in lines:
        if line.startswith("event:"):
            event_type = line[len("event:"):].strip()
        elif line.startswith("data:"):
            data_str = line[len("data:"):].strip()
            if event_type == "chunk":
                yield SpeechStreamChunk.model_validate_json(data_str)
            elif event_type == "done":
                yield SpeechStreamDone.model_validate_json(data_str)
            event_type = None
        elif line == "":
            event_type = None


class OpenAITTS(BaseResource):
    """OpenAI TTS client (sync).

    Uses OpenAI-compatible speech synthesis endpoint.

    Example:
        ```python
        tts = OpenAITTS(config)

        # Generate speech
        response = tts.speech("Hello, world!", voice="nova")
        Path("output.mp3").write_bytes(response.audio_bytes)

        # Use analysis for visualization
        for frame in response.analysis.frames:
            print(f"t={frame.t:.2f}s rms={frame.rms:.3f}")

        # Stream audio chunks
        for item in tts.speech_stream("Hello!", voice="nova"):
            if isinstance(item, SpeechStreamChunk):
                audio = item.audio_bytes
            elif isinstance(item, SpeechStreamDone):
                print(f"Done: {item.duration_s}s")
        ```
    """

    def __init__(self, config: "SDKConfig"):
        super().__init__(config, use_audio_url=True)

    def speech(
        self,
        input: str,
        *,
        model: str = "tts-1",
        voice: str = "nova",
        response_format: TTSResponseFormat = "mp3",
        speed: float = 1.0,
        instructions: str | None = None,
    ) -> SpeechResponse:
        """Generate speech from text.

        Returns a SpeechResponse containing base64 audio and per-frame
        analysis metadata for visualization.

        Args:
            input: Text to synthesize (max 4096 characters).
            model: TTS model (tts-1, tts-1-hd) or tier alias (@cheap, @quality).
            voice: Voice: alloy, ash, coral, echo, fable, nova, onyx, sage, shimmer.
            response_format: Audio format: mp3, opus, aac, flac, wav, pcm.
            speed: Playback speed (0.25 to 4.0). Default: 1.0
            instructions: Custom voice instructions (for gpt-4o-mini-tts).

        Returns:
            SpeechResponse with .audio_bytes property and .analysis data.
        """
        json_body: dict = {
            "model": model,
            "input": input,
            "voice": voice,
            "response_format": response_format,
            "speed": speed,
        }
        if instructions is not None:
            json_body["instructions"] = instructions

        logger.debug(
            "TTS speech: model=%s, voice=%s, format=%s, input_len=%d",
            model, voice, response_format, len(input),
        )

        with handle_api_errors():
            response = self._http_client.post(
                "/v1/audio/speech",
                json=json_body,
            )
            response.raise_for_status()

        return SpeechResponse.model_validate(response.json())

    def speech_stream(
        self,
        input: str,
        *,
        model: str = "tts-1",
        voice: str = "nova",
        response_format: TTSResponseFormat = "mp3",
        speed: float = 1.0,
        instructions: str | None = None,
    ) -> Iterator[SpeechStreamChunk | SpeechStreamDone]:
        """Generate speech with SSE streaming.

        Streams audio chunks with real-time analysis frames.
        Audio format defaults to MP3; set response_format="pcm" for raw PCM.
        The final yielded item is a SpeechStreamDone with summary info.

        Args:
            input: Text to synthesize (max 4096 characters).
            model: TTS model or tier alias.
            voice: Voice identifier. Default: "nova"
            response_format: Audio format: "mp3" (default) or "pcm".
            speed: Playback speed (0.25 to 4.0). Default: 1.0
            instructions: Custom voice instructions (for gpt-4o-mini-tts).

        Yields:
            SpeechStreamChunk for each audio chunk, then SpeechStreamDone.
        """
        json_body: dict = {
            "model": model,
            "input": input,
            "voice": voice,
            "response_format": response_format,
            "speed": speed,
            "stream": True,
        }
        if instructions is not None:
            json_body["instructions"] = instructions

        logger.debug(
            "TTS speech stream: model=%s, voice=%s, format=%s, input_len=%d",
            model, voice, response_format, len(input),
        )

        with handle_api_errors():
            with self._http_client.stream("POST", "/v1/audio/speech", json=json_body) as response:
                response.raise_for_status()
                yield from _parse_sse_stream(response.iter_lines())


class AsyncOpenAITTS(AsyncBaseResource):
    """OpenAI TTS client (async).

    Async version of OpenAITTS.

    Example:
        ```python
        tts = AsyncOpenAITTS(config)
        response = await tts.speech("Hello!", voice="nova")
        Path("output.mp3").write_bytes(response.audio_bytes)
        ```
    """

    def __init__(self, config: "SDKConfig"):
        super().__init__(config, use_audio_url=True)

    @async_api_error_handler
    async def speech(
        self,
        input: str,
        *,
        model: str = "tts-1",
        voice: str = "nova",
        response_format: TTSResponseFormat = "mp3",
        speed: float = 1.0,
        instructions: str | None = None,
    ) -> SpeechResponse:
        """Generate speech from text (async).

        Returns a SpeechResponse containing base64 audio and per-frame
        analysis metadata for visualization.

        Args:
            input: Text to synthesize (max 4096 characters).
            model: TTS model or tier alias.
            voice: Voice identifier. Default: "nova"
            response_format: Audio format. Default: "mp3"
            speed: Playback speed (0.25 to 4.0).
            instructions: Custom voice instructions.

        Returns:
            SpeechResponse with .audio_bytes property and .analysis data.
        """
        json_body: dict = {
            "model": model,
            "input": input,
            "voice": voice,
            "response_format": response_format,
            "speed": speed,
        }
        if instructions is not None:
            json_body["instructions"] = instructions

        logger.debug(
            "TTS speech async: model=%s, voice=%s, format=%s",
            model, voice, response_format,
        )

        response = await self._http_client.post(
            "/v1/audio/speech",
            json=json_body,
        )
        response.raise_for_status()
        return SpeechResponse.model_validate(response.json())

    async def speech_stream(
        self,
        input: str,
        *,
        model: str = "tts-1",
        voice: str = "nova",
        response_format: TTSResponseFormat = "mp3",
        speed: float = 1.0,
        instructions: str | None = None,
    ) -> AsyncIterator[SpeechStreamChunk | SpeechStreamDone]:
        """Generate speech with SSE streaming (async).

        Streams audio chunks with real-time analysis frames.
        Audio format defaults to MP3; set response_format="pcm" for raw PCM.
        The final yielded item is a SpeechStreamDone with summary info.

        Args:
            input: Text to synthesize (max 4096 characters).
            model: TTS model or tier alias.
            voice: Voice identifier. Default: "nova"
            response_format: Audio format: "mp3" (default) or "pcm".
            speed: Playback speed (0.25 to 4.0). Default: 1.0
            instructions: Custom voice instructions (for gpt-4o-mini-tts).

        Yields:
            SpeechStreamChunk for each audio chunk, then SpeechStreamDone.
        """
        json_body: dict = {
            "model": model,
            "input": input,
            "voice": voice,
            "response_format": response_format,
            "speed": speed,
            "stream": True,
        }
        if instructions is not None:
            json_body["instructions"] = instructions

        logger.debug(
            "TTS speech stream async: model=%s, voice=%s, format=%s, input_len=%d",
            model, voice, response_format, len(input),
        )

        async with self._http_client.stream("POST", "/v1/audio/speech", json=json_body) as response:
            response.raise_for_status()
            async for item in _parse_sse_stream_async(response.aiter_lines()):
                yield item


__all__ = [
    "OpenAITTS",
    "AsyncOpenAITTS",
]
