"""Data models and serialization for the Tea Party pipeline."""

from __future__ import annotations

import json
from enum import Enum
from typing import Literal

from pydantic import BaseModel, field_validator

from alice_cli.errors import AliceCLIError

# ── Error ─────────────────────────────────────────────────────────────────────


class ScriptParseError(AliceCLIError):
    """Raised when script JSON parsing or validation fails."""

    def __init__(self, message: str) -> None:
        super().__init__(message)


# ── Models ────────────────────────────────────────────────────────────────────


class Turn(BaseModel):
    """A single speaker segment in a conversational script."""

    speaker: Literal["Host_A", "Host_B"]
    text: str

    @field_validator("text")
    @classmethod
    def text_not_empty(cls, v: str) -> str:
        if not v.strip():
            raise ValueError("Turn text must not be empty")
        return v


class Script(BaseModel):
    """A conversational script (one or more speakers)."""

    turns: list[Turn]

    @field_validator("turns")
    @classmethod
    def minimum_turns(cls, v: list[Turn]) -> list[Turn]:
        if len(v) < 2:
            raise ValueError("Script must have at least 2 turns")
        return v


class GenerationMode(str, Enum):
    """Script generation style."""

    CONVERSATION = "conversation"
    NARRATOR = "narrator"
    BRIEFING = "briefing"
    INTERVIEW = "interview"


class TeaPartyMetadata(BaseModel):
    """Metadata stored in the Podcast_Record for Cloud Locker persistence."""

    artifact_key: str
    audio_bucket: str
    voice_a: str
    voice_b: str
    duration_seconds: float
    turn_count: int
    mode: str
    lang: str
    rehearsed_from: str | None = None
    published_url: str | None = None
    published_at: str | None = None


# ── Serialization ─────────────────────────────────────────────────────────────


def serialize_script(script: Script, pretty: bool = False) -> str:
    """Convert a Script to a JSON string.

    Args:
        script: The Script object to serialize.
        pretty: If True, produce indented JSON. Default is compact.

    Returns:
        JSON string representation.
    """
    if pretty:
        return script.model_dump_json(indent=2)
    return script.model_dump_json()


def parse_script(json_string: str) -> Script:
    """Parse a JSON string into a Script object.

    Args:
        json_string: JSON string with a ``turns`` array.

    Returns:
        Validated Script object.

    Raises:
        ScriptParseError: If JSON is malformed or missing required fields.
    """
    try:
        data = json.loads(json_string)
    except (json.JSONDecodeError, TypeError) as exc:
        raise ScriptParseError(f"Invalid JSON: {exc}") from exc

    if not isinstance(data, dict) or "turns" not in data:
        raise ScriptParseError("JSON must be an object with a 'turns' array")

    try:
        return Script.model_validate(data)
    except Exception as exc:  # noqa: BLE001
        raise ScriptParseError(f"Script validation failed: {exc}") from exc
