"""
OpenAI TTS Adapter

Uses OpenAI's TTS API for speech synthesis.
Returns PCM audio chunks resampled to 16kHz for DVGateway injection.

Features:
- Human-like voice optimization via gpt-4o-mini-tts voiceInstructions
- Automatic Korean/English voice instruction generation
- 24kHz → 16kHz resampling for DVGateway compatibility

Model Reference (2026-03):
  tts-1         — Optimized for real-time speed (default, ~200ms latency)
  tts-1-hd      — Optimized for quality (studio output, higher latency)
  gpt-4o-mini-tts — Latest neural TTS model; better prosody, emotions

Voice options:
  alloy, echo, fable, onyx, nova, shimmer (all models)
  ash, ballad, coral, sage, verse (gpt-4o-mini-tts only)

API Reference: https://platform.openai.com/docs/api-reference/audio/createSpeech
"""

from __future__ import annotations

import struct
from typing import AsyncIterable, AsyncIterator, Literal

from dvgateway.audio.codec import float32_to_slin16, resample
from dvgateway.types import (
    HumanVoiceOptions,
    HUMAN_VOICE_DEFAULTS_KO,
    TtsAdapter,
    TtsOptions,
)

OpenAITtsVoice = Literal[
    "alloy", "echo", "fable", "onyx", "nova", "shimmer",
    "ash", "ballad", "coral", "sage", "verse",
]

OpenAITtsModel = Literal["tts-1", "tts-1-hd", "gpt-4o-mini-tts"]

OPENAI_SAMPLE_RATE = 24000
DV_SAMPLE_RATE = 16000
CHUNK_BYTES = 960  # 20ms at 24kHz, 16-bit PCM


def _pcm_bytes_to_float32(buf: bytes) -> list[float]:
    num_samples = len(buf) // 2
    samples = struct.unpack(f"<{num_samples}h", buf[: num_samples * 2])
    return [s / 32768.0 for s in samples]


def _build_human_voice_instructions(hv: HumanVoiceOptions, language: str = "ko") -> str:
    """Build voiceInstructions string from HumanVoiceOptions for gpt-4o-mini-tts."""
    is_ko = language.startswith("ko")
    parts: list[str] = []

    if is_ko:
        parts.append("한국어로 자연스럽고 사람다운 방식으로 말하세요.")
    else:
        parts.append("Speak in a natural, human-like conversational manner.")

    if hv.natural_pauses:
        parts.append(
            "문장과 절 사이에 자연스러운 쉼을 넣으세요. 쉼표나 마침표에서 잠깐 멈추세요."
            if is_ko
            else "Add natural pauses between sentences and clauses. Pause briefly at commas and periods."
        )

    if hv.breathing_sounds:
        parts.append(
            "긴 문장 사이에 가볍게 숨을 들이쉬는 소리를 넣으세요."
            if is_ko
            else "Include subtle breath intake sounds between longer phrases."
        )

    if hv.filler_words:
        parts.append(
            "가끔 자연스러운 필러 단어를 사용하세요: 음, 어, 그, 저, 글쎄요."
            if is_ko
            else "Occasionally use natural filler words: um, uh, well, you know, let me think."
        )

    if hv.exclamations:
        parts.append(
            "적절한 감탄사와 맞장구를 사용하세요: 아, 네, 맞아요, 그렇죠, 오."
            if is_ko
            else "Use appropriate interjections: oh, right, I see, exactly, sure."
        )

    if hv.emotional_range > 0.5:
        parts.append(
            "감정을 풍부하게 표현하세요. 기쁨, 공감, 놀라움 등을 목소리에 담으세요."
            if is_ko
            else "Express emotions richly. Convey joy, empathy, surprise through your voice."
        )
    elif hv.emotional_range > 0.3:
        parts.append(
            "적당한 감정 표현을 유지하세요."
            if is_ko
            else "Maintain moderate emotional expression."
        )

    if hv.speech_variation > 0.5:
        parts.append(
            "말하는 속도와 톤을 자연스럽게 변화시키세요. 단조롭게 말하지 마세요."
            if is_ko
            else "Vary your speaking pace and tone naturally. Avoid monotone delivery."
        )

    return " ".join(parts)


class OpenAITtsAdapter(TtsAdapter):
    """OpenAI TTS adapter implementing the TtsAdapter interface.

    Supports human-like voice optimization via ``human_voice`` parameter.
    When enabled (default), uses gpt-4o-mini-tts with auto-generated
    voiceInstructions for natural-sounding speech.

    Args:
        api_key: OpenAI API key.
        voice: Voice name (default: nova).
        model: Model ID. When human_voice is enabled, defaults to
            ``gpt-4o-mini-tts`` for voiceInstructions support.
        voice_instructions: Custom voice instructions for gpt-4o-mini-tts.
            Auto-generated instructions are appended when human_voice is enabled.
        human_voice: Human-like voice options. Default: Korean preset.
            Set to ``None`` with explicit parameters to disable.
    """

    def __init__(
        self,
        api_key: str = "",
        voice: OpenAITtsVoice = "nova",
        model: OpenAITtsModel | None = None,
        voice_instructions: str = "",
        human_voice: HumanVoiceOptions | None | bool = True,
    ) -> None:
        self._api_key = api_key
        self._voice = voice

        # Resolve human voice: True → Korean defaults, False/None → disabled
        if human_voice is True:
            hv = HUMAN_VOICE_DEFAULTS_KO
        elif isinstance(human_voice, HumanVoiceOptions):
            hv = human_voice
        else:
            hv = None

        hv_enabled = hv is not None

        self._model: OpenAITtsModel = model or ("gpt-4o-mini-tts" if hv_enabled else "tts-1")

        # Build voice instructions: user-provided + auto-generated
        self._voice_instructions = voice_instructions
        if hv_enabled and self._model == "gpt-4o-mini-tts":
            auto = _build_human_voice_instructions(hv, "ko")
            self._voice_instructions = f"{voice_instructions} {auto}".strip() if voice_instructions else auto

    async def _synthesize_impl(self, text: str, options: TtsOptions | None = None) -> AsyncIterator[bytes]:
        try:
            from openai import AsyncOpenAI
        except ImportError as e:
            raise ImportError("Install openai: pip install openai") from e

        client = AsyncOpenAI(api_key=self._api_key)
        voice = (options.voice_id if options and options.voice_id else None) or self._voice
        speed = options.speed if options and options.speed else 1.0

        params: dict[str, object] = {
            "model": self._model,
            "voice": voice,
            "input": text,
            "response_format": "pcm",
            "speed": speed,
        }

        if self._voice_instructions and self._model == "gpt-4o-mini-tts":
            params["instructions"] = self._voice_instructions

        response = await client.audio.speech.create(**params)  # type: ignore[arg-type]
        buf = response.content if hasattr(response, "content") else await response.aread()

        offset = 0
        while offset < len(buf):
            end = min(offset + CHUNK_BYTES, len(buf))
            frame = buf[offset:end]
            offset = end

            samples_24k = _pcm_bytes_to_float32(frame)
            samples_16k = resample(samples_24k, OPENAI_SAMPLE_RATE, DV_SAMPLE_RATE)
            yield float32_to_slin16(samples_16k)

    def synthesize(self, text: str, options: TtsOptions | None = None) -> AsyncIterable[bytes]:
        return self._synthesize_impl(text, options)
