# --- Pydantic Models for Alexa.Media.Playback v1.0 ---
from typing import List, Optional, Union, Literal, Dict

from pydantic import BaseModel, Field

# --- Base Models & Common Components ---


class BasePlaybackModel(BaseModel):
    """Base model for common configurations."""

    class Config:
        extra = "ignore"
        # validate_by_name = True # Pydantic V2 equivalent for validate_by_name
        validate_by_name = (
            True  # Keep for compatibility if needed, but validate_by_name is V2
        )


class User(BasePlaybackModel):
    id: str
    access_token: Optional[str] = Field(None, alias="accessToken")


class Location(BasePlaybackModel):
    originating_locale: Optional[str] = Field(None, alias="originatingLocale")


class RequestContext(BasePlaybackModel):
    user: Optional[User] = Field(default=None)
    location: Optional[Location] = Field(default=None)


class Filter(BasePlaybackModel):
    explicit_language_allowed: Optional[bool] = Field(
        None, alias="explicitLanguageAllowed"
    )
    # Add other potential filters if needed


class MediaReferenceValue(BasePlaybackModel):
    id: str  # Often the item/track ID
    queue_id: Optional[str] = Field(
        None, alias="queueId"
    )  # ID of the queue this item belongs to
    content_id: Optional[str] = Field(
        None, alias="contentId"
    )  # Original contentId requested that resulted in this queue


class MediaReference(BasePlaybackModel):
    """Reference to a media item, often the currently playing one."""

    namespace: Literal["Alexa.Audio.PlayQueue"]
    name: Literal["item"]
    value: MediaReferenceValue


class PlaybackRepeat(BasePlaybackModel):
    status: Literal["ON", "OFF"]


class PlaybackModes(BasePlaybackModel):
    shuffle: Optional[bool] = Field(default=False)
    loop: Optional[bool] = Field(default=False)  # Loop the entire queue
    repeat: PlaybackRepeat = Field(
        default_factory=lambda: PlaybackRepeat(status="OFF")
    )  # Repeat the single item


class ContentFormat(BasePlaybackModel):
    """Describes audio formats supported by an endpoint player."""

    type: Literal["AUDIO"]
    content_type_id: str = Field(
        ..., alias="contentTypeId"
    )  # Skill-defined ID representing a specific codec/bitrate/etc.


class PlayerProperties(BasePlaybackModel):
    supported_content_formats: Optional[List[ContentFormat]] = Field(
        None, alias="supportedContentFormats"
    )


class Player(BasePlaybackModel):
    """Represents a playback capability on an endpoint."""

    playback_method_type: Literal["ALEXA_AUDIO_PLAYER_QUEUE"] = Field(
        ..., alias="playbackMethodType"
    )
    id: str  # Usually "AudioPlayer"
    properties: Optional[PlayerProperties] = Field(default=None)


class Endpoint(BasePlaybackModel):
    """Represents a target device for playback."""

    id: str = Field(..., alias="endpointId")  # Use alias='id' if JSON uses "id"
    players: Optional[List[Player]] = Field(default=None)


# --- Models for Initiate Response ---


class Control(BasePlaybackModel):
    """Represents a playback control (button/state)."""

    type: Literal["TOGGLE", "CYCLE", "COMMAND", "ADJUST"]
    name: Literal[
        "SHUFFLE", "LOOP", "REPEAT", "NEXT", "PREVIOUS", "SEEK_POSITION"
    ]  # Add other commands if applicable
    enabled: bool
    selected: Optional[bool] = Field(default=None)  # For TOGGLE
    value: Optional[PlaybackRepeat] = Field(
        default=None
    )  # For CYCLE (e.g., REPEAT status)


class RuleFeedback(BasePlaybackModel):
    type: Literal["PREFERENCE"]
    enabled: bool


class Rule(BasePlaybackModel):
    """Defines rules for the queue or item."""

    # Queue-level rules
    feedback: Optional[RuleFeedback] = Field(default=None)
    # Item-level rules
    feedback_enabled: Optional[bool] = Field(default=None, alias="feedbackEnabled")


class Speech(BasePlaybackModel):
    type: Literal["PLAIN_TEXT"]
    text: str


class Name(BasePlaybackModel):
    speech: Speech
    display: str


class ArtSource(BasePlaybackModel):
    url: str
    size: Optional[Literal["X_SMALL", "SMALL", "MEDIUM", "LARGE", "X_LARGE"]] = Field(
        default=None
    )
    width_pixels: Optional[int] = Field(None, alias="widthPixels")
    height_pixels: Optional[int] = Field(None, alias="heightPixels")


class Art(BasePlaybackModel):
    sources: List[ArtSource]


class BaseMetadata(BasePlaybackModel):
    """Common metadata fields."""

    type: Literal[
        "TRACK", "PODCAST_EPISODE", "RADIO_STATION"
    ]  # Add other types as needed
    name: Name
    art: Optional[Art] = Field(default=None)
    background: Optional[Art] = Field(
        default=None, description="Optional background image for display devices."
    )
    # Add other common fields like artistName, albumName, etc.


class TrackMetadata(BaseMetadata):
    type: Literal["TRACK"] = "TRACK"
    # Add track-specific fields: artistName, albumName, etc.


class PodcastEpisodeMetadata(BaseMetadata):
    type: Literal["PODCAST_EPISODE"] = "PODCAST_EPISODE"
    # Add podcast-specific fields: seriesName, episodeNumber, etc.


class RadioStationMetadata(BaseMetadata):
    type: Literal["RADIO_STATION"] = "RADIO_STATION"
    # Add radio-specific fields: callSign, frequency, etc.


# Union of possible metadata types
Metadata = Union[
    TrackMetadata, PodcastEpisodeMetadata, RadioStationMetadata
]  # Add other types to Union


class Stream(BasePlaybackModel):
    id: str  # Your internal identifier for this stream instance
    uri: str  # The actual HTTPS URL for the audio stream
    offset_in_milliseconds: int = Field(..., alias="offsetInMilliseconds")
    valid_until: Optional[str] = Field(
        None,
        alias="validUntil",
        description="ISO 8601 timestamp string when the URI expires.",
    )
    content_format: Optional[ContentFormat] = Field(
        None,
        alias="contentFormat",
        description="Included if premium audio/specific format is used.",
    )
    # Add DRM info if needed


class Feedback(BasePlaybackModel):
    type: Literal["PREFERENCE"]
    value: Literal[
        "POSITIVE", "NEGATIVE", "NEUTRAL"
    ]  # Or just POSITIVE/NEGATIVE? Check docs.


class PlaybackInfo(BasePlaybackModel):
    """Placeholder for playback info (often just 'DEFAULT')."""

    type: Literal["DEFAULT"]  # Expand if other types exist

class Transcript(BasePlaybackModel):
    """Represents synchronized lyrics or captions."""

    id: str  # Your internal identifier for this transcript instance
    uri: str  # HTTPS URL for the transcript file (e.g., WebVTT)
    format: Literal["WEBVTT"]  # Format of the transcript file
    headers: Optional[List[Dict[str, str]]]


class PlayQueueItem(BasePlaybackModel):
    """Represents a single item in the play queue (used in Initiate.Response)."""

    id: str  # Unique ID for this specific item instance in the queue
    playback_info: PlaybackInfo = Field(
        default_factory=lambda: PlaybackInfo(type="DEFAULT"), alias="playbackInfo"
    )
    metadata: Metadata
    duration_in_milliseconds: Optional[int] = Field(
        None, alias="durationInMilliseconds"
    )
    controls: Optional[List[Control]] = Field(default=None)
    rules: Optional[Rule] = Field(default=None)
    stream: Stream
    feedback: Optional[Feedback] = Field(default=None)
    transcript: Optional[Transcript] = Field(
        default=None, description="Optional synchronized lyrics."
    )


class PlaybackMethod(BasePlaybackModel):
    """Contains the queue details needed by Alexa to start playback."""

    type: Literal["ALEXA_AUDIO_PLAYER_QUEUE"]
    id: str  # The unique identifier for this play queue instance
    controls: Optional[List[Control]] = Field(
        default=None
    )  # Queue-level controls (SHUFFLE, LOOP, REPEAT)
    rules: Optional[Rule] = Field(
        default=None
    )  # Queue-level rules (Feedback preference)
    first_item: PlayQueueItem = Field(
        ..., alias="firstItem", description="The first track/episode to play."
    )


# --- Request Models ---


class InitiateRequestPayload(BasePlaybackModel):
    request_context: Optional[RequestContext] = Field(
        default=None, alias="requestContext"
    )
    filters: Optional[Filter] = Field(default=None)
    content_id: str = Field(
        ...,
        alias="contentId",
        description="The identifier for the content user wants to play (from GetPlayableContent).",
    )
    current_item_reference: Optional[MediaReference] = Field(
        default=None,
        alias="currentItemReference",
        description="Present if content is already playing on the target device.",
    )
    playback_modes: PlaybackModes = Field(
        default_factory=PlaybackModes, alias="playbackModes"
    )
    playback_position: Optional[Literal["RESUME"]] = Field(
        None,
        alias="playbackPosition",
        description="Podcasts only: Indicates user asked to resume.",
    )
    endpoints: Optional[List[Endpoint]] = Field(
        default=None, description="Premium Audio only: Target device capabilities."
    )


class ReinitiateRequestPayload(BasePlaybackModel):
    request_context: RequestContext = Field(..., alias="requestContext")
    playback_snapshot: "PlaybackSnapshot" = Field(
        ..., alias="playbackSnapshot", description="State of the playback being moved."
    )
    endpoints: List[Endpoint] = Field(..., description="Target device(s) capabilities.")


class PlaybackSnapshot(BasePlaybackModel):
    """Snapshot of the playback state to be moved in Reinitiate."""

    media_reference: MediaReference = Field(
        ...,
        alias="mediaReference",
        description="Reference to the item playing on the origin device.",
    )
    offset_in_milliseconds: int = Field(
        ...,
        alias="offsetInMilliseconds",
        description="Playback position when paused on origin device.",
    )
    endpoint: Endpoint = Field(..., description="Capabilities of the *origin* device.")
    # Note: Docs imply playbackModes and filters might also be in snapshot, add if observed


# --- Top Level Request Structure ---


class PlaybackHeader(BasePlaybackModel):
    namespace: Literal["Alexa.Media.Playback"]
    name: Literal["Initiate", "Reinitiate"]
    message_id: str = Field(..., alias="messageId")
    payload_version: Literal["1.0"] = Field(..., alias="payloadVersion")


class InitiateRequest(BasePlaybackModel):
    header: PlaybackHeader
    payload: InitiateRequestPayload


class ReinitiateRequest(BasePlaybackModel):
    header: PlaybackHeader
    payload: ReinitiateRequestPayload


# --- Response Models ---


class InitiateResponsePayload(BasePlaybackModel):
    playback_method: PlaybackMethod = Field(..., alias="playbackMethod")


class ReinitiateResponsePayload(BasePlaybackModel):
    action_type: Literal["CONFIRM_QUEUE", "REPLACE_STREAM"] = Field(
        ..., alias="actionType", description="Indicates if modification is needed."
    )
    # Conditionally required based on actionType - Pydantic doesn't enforce this easily, check in code
    stream: Optional[Stream] = Field(
        default=None, description="Required if actionType is REPLACE_STREAM."
    )
    playback_method: Optional[PlaybackMethod] = Field(
        default=None,
        alias="playbackMethod",
        description="Required if actionType requires full queue replacement (less common). If CONFIRM_QUEUE, often can return just actionType.",
    )
    # Documentation shows playbackMethod being returned even for CONFIRM_QUEUE sometimes, include as optional.


class PlaybackResponseHeader(BasePlaybackModel):
    namespace: Literal["Alexa.Media.Playback"]
    name: Literal["Initiate.Response", "Reinitiate.Response"]
    message_id: str = Field(
        ..., alias="messageId"
    )  # Should correlate with request messageId
    payload_version: Literal["1.0"] = Field(..., alias="payloadVersion")


class InitiateResponse(BasePlaybackModel):
    header: PlaybackResponseHeader
    payload: InitiateResponsePayload


class ReinitiateResponse(BasePlaybackModel):
    header: PlaybackResponseHeader
    payload: ReinitiateResponsePayload


# --- Error Response Models (Simplified) ---
class ErrorResponsePayload(BasePlaybackModel):
    type: str  # e.g., "INTERNAL_ERROR", "INVALID_REQUEST", "USER_CONTEXT_MISMATCH"
    message: str


class ErrorResponseHeader(BasePlaybackModel):
    namespace: Literal["Alexa.Media", "Alexa"]  # Namespace varies slightly for errors
    name: Literal["ErrorResponse"]
    message_id: str = Field(..., alias="messageId")
    payload_version: Literal["1.0", "3"]  # Version can vary


class AlexaErrorResponse(BasePlaybackModel):
    header: ErrorResponseHeader
    payload: ErrorResponsePayload
