//! Canonical, provider-neutral media domain model.
//!
//! This is the *single source of truth* for what a media item is across the
//! whole app. Rust (repositories, player, downloads) uses these types directly;
//! the frontend consumes the tauri-specta-generated projection in
//! `src/lib/api/bindings.ts`. There is no second hand-written copy in either
//! language, so the model cannot drift.
//!
//! No provider (Jellyfin) vocabulary belongs in this file. Translation from a
//! provider's wire shape lives beside it in `from_jellyfin.rs` and is the only
//! place provider terms touch the domain type.
//!
//! Spec: docs/specs/frontend-domain-model.md

use serde::{Deserialize, Serialize};

/// The kind of a media item — provider-neutral classification.
///
/// Replaces the stringly-typed `item_type` that carried Jellyfin's vocabulary
/// (`"Audio"`, `"MusicAlbum"`, …) across the boundary. A closed enum means a
/// typo or an unhandled kind is a compile error on the frontend, not a silent
/// runtime miss across ~127 comparison sites.
#[derive(specta::Type, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub enum MediaKind {
    // Music
    Track,
    Album,
    Artist,
    Playlist,
    // Video
    Movie,
    Series,
    Season,
    Episode,
    // Cast/crew
    Person,
    // Containers / live TV
    /// A channel *container* the user drills into (Jellyfin `Channel`).
    Channel,
    Folder,
    /// A live TV channel — playable, but a live stream with no seekable
    /// timeline (no resume/seek). Jellyfin `TvChannel`/`LiveTvChannel`.
    LiveChannel,
    /// A playable leaf inside a channel (Jellyfin `ChannelFolderItem` that is
    /// not itself a folder) — e.g. a plugin-channel VOD item that has no
    /// dedicated item type but carries its own media streams. Playable and
    /// seekable, unlike `LiveChannel`. Distinct from `Channel` (the container)
    /// and from `Other` so the UI can route it to playback.
    ChannelItem,
    /// A kind we do not model explicitly. Reached only for provider item types
    /// that map to nothing meaningful; consumers treat it like an opaque
    /// container. The mapping must be *total* — it never panics — so this is the
    /// safe sink for unknown strings. Also the `Default`, so a defaulted
    /// `MediaItem` (see the dual-carry migration) is inert rather than a lie.
    #[default]
    Other,
}

/// The kind of a media stream within an item (audio track, video track,
/// subtitle, …) — provider-neutral, replacing the stringly Jellyfin stream type.
#[derive(specta::Type, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub enum StreamKind {
    Audio,
    Video,
    Subtitle,
    /// Any stream kind we do not model explicitly (e.g. embedded image, data).
    #[default]
    Other,
}

impl MediaKind {
    /// True for kinds that are containers/collections rather than playable leaves.
    /// Presentation-neutral helper the backend can use for e.g. drill-vs-play.
    // Consumed by later migration phases (drill-vs-play routing); kept now so the
    // domain surface is complete alongside the type it describes.
    #[allow(dead_code)]
    pub fn is_container(self) -> bool {
        matches!(
            self,
            MediaKind::Album
                | MediaKind::Artist
                | MediaKind::Series
                | MediaKind::Season
                | MediaKind::Playlist
                | MediaKind::Channel
                | MediaKind::Folder
        )
    }
}
