"""Response models for the murmr API."""

from __future__ import annotations

import base64
from typing import Literal, Optional

from pydantic import BaseModel, ConfigDict


class SyncAudioResponse(BaseModel):
    """Response from a synchronous speech request (audio bytes returned directly)."""

    model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)

    audio: bytes
    content_type: str
    duration_ms: int = 0
    total_time_ms: int = 0


class AsyncJobResponse(BaseModel):
    """Response from submitting a batch speech job."""

    model_config = ConfigDict(frozen=True)

    id: str
    status: Literal["queued"] = "queued"
    created_at: str


class JobStatus(BaseModel):
    """Status of an async batch job, returned by polling."""

    model_config = ConfigDict(frozen=True)

    id: str
    status: Literal["queued", "processing", "completed", "failed"]
    created_at: str
    completed_at: Optional[str] = None
    duration_ms: Optional[int] = None
    error: Optional[str] = None
    audio_base64: Optional[str] = None
    content_type: Optional[str] = None
    response_format: Optional[str] = None

    @property
    def audio_bytes(self) -> Optional[bytes]:
        """Decode audio_base64 into raw bytes, or None if not present."""
        if self.audio_base64 is None:
            return None
        return base64.b64decode(self.audio_base64)


class AudioStreamChunk(BaseModel):
    """A single chunk from an SSE audio stream."""

    model_config = ConfigDict(frozen=True)

    audio: Optional[str] = None
    chunk: Optional[str] = None
    chunk_index: Optional[int] = None
    sample_rate: Optional[int] = None
    format: Optional[str] = None
    first_chunk_latency_ms: Optional[float] = None
    done: Optional[bool] = None
    total_chunks: Optional[int] = None
    total_time_ms: Optional[float] = None
    error: Optional[str] = None

    @property
    def audio_bytes(self) -> bytes:
        """Decode the audio/chunk field from base64 into raw bytes."""
        data = self.audio or self.chunk
        if not data:
            return b""
        return base64.b64decode(data)


class LongFormResult(BaseModel):
    """Result of a long-form audio generation."""

    model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)

    audio: bytes
    total_chunks: int
    duration_ms: int
    character_count: int


class SavedVoice(BaseModel):
    """A saved voice available for reuse."""

    model_config = ConfigDict(frozen=True)

    id: str
    name: str
    description: str
    language: str
    language_name: Optional[str] = None
    audio_preview_url: Optional[str] = None
    created_at: str


class VoiceListResponse(BaseModel):
    """Response from listing saved voices."""

    model_config = ConfigDict(frozen=True)

    voices: list[SavedVoice]
    saved_count: int
    saved_limit: int
    total: int


class VoiceSaveResponse(BaseModel):
    """Response from saving a voice."""

    model_config = ConfigDict(frozen=True)

    id: str
    name: str
    language: str
    description: str
    prompt_size_bytes: int
    created_at: str
    success: bool
    has_audio_preview: bool


class VoiceDeleteResponse(BaseModel):
    """Response from deleting a voice."""

    model_config = ConfigDict(frozen=True)

    success: bool
    id: str
    message: str


class ExtractEmbeddingsResponse(BaseModel):
    """Response from extracting voice embeddings from audio."""

    model_config = ConfigDict(frozen=True)

    prompt_data: str
    prompt_size_bytes: int
