use std::collections::HashMap;

use chrono::{DateTime, NaiveDate, Utc};

use crate::language::Language;
use crate::media::entity::MediaFile;
use crate::prelude::{Page, Sort};

#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize)]
#[serde(tag = "type")]
pub enum TvShowSource {
    Tmdb { tmdb_id: u64 },
}

impl TvShowSource {
    pub fn as_tmdb_id(&self) -> Option<u64> {
        match self {
            Self::Tmdb { tmdb_id } => Some(*tmdb_id),
        }
    }
}

#[derive(Clone, Debug)]
pub struct TvShow {
    pub id: u64,
    pub source: TvShowSource,
    pub original_name: String,
    pub original_language: String,
    pub origin_country: Vec<String>,
    pub poster_url: Option<String>,
    pub backdrop_url: Option<String>,
    pub homepage: String,
    pub name: String,
    pub language: Language,
    pub overview: Option<String>,
    pub tagline: Option<String>,
    pub first_air_date: Option<NaiveDate>,
    pub in_production: bool,
    pub adult: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Clone, Debug)]
pub struct ExternalTvShow {
    pub source: TvShowSource,
    pub original_name: String,
    pub original_language: String,
    pub origin_country: Vec<String>,
    pub homepage: String,
    pub name: String,
    pub language: Language,
    pub overview: Option<String>,
    pub tagline: Option<String>,
    pub backdrop_url: Option<String>,
    pub poster_url: Option<String>,
    pub first_air_date: Option<NaiveDate>,
    pub number_of_seasons: u64,
    pub in_production: bool,
    pub adult: bool,
    pub popularity: f64,
    pub vote_count: u64,
    pub vote_average: f64,
}

#[derive(Clone, Debug)]
pub struct TvShowSeason {
    pub id: u64,
    pub source: TvShowSource,
    pub tvshow_id: u64,
    pub season_number: u64,
    pub air_date: Option<NaiveDate>,
    pub language: Language,
    pub name: String,
    pub overview: Option<String>,
    pub poster_url: Option<String>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Clone, Debug)]
pub struct ExternalTvShowSeason {
    pub source: TvShowSource,
    pub air_date: Option<NaiveDate>,
    pub language: Language,
    pub name: String,
    pub overview: Option<String>,
    pub season_number: u64,
    pub poster_url: Option<String>,
    pub episodes: Vec<ExternalTvShowEpisode>,
}

#[derive(Clone, Debug)]
pub struct TvShowEpisode {
    pub id: u64,
    pub source: TvShowSource,
    pub tvshow_season_id: u64,
    pub language: Language,
    pub air_date: Option<chrono::NaiveDate>,
    pub name: String,
    pub overview: Option<String>,
    pub production_code: String,
    pub episode_number: u64,
    // duration in minutes
    pub runtime: Option<u32>,
    pub still_url: Option<String>,
    pub vote_average: f64,
    pub vote_count: u64,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Clone, Debug)]
pub struct ExternalTvShowEpisode {
    pub source: TvShowSource,
    pub language: Language,
    pub air_date: Option<chrono::NaiveDate>,
    pub name: String,
    pub overview: Option<String>,
    pub production_code: String,
    pub episode_number: u64,
    pub season_number: u64,
    pub runtime: Option<u32>,
    pub still_url: Option<String>,
    pub vote_average: f64,
    pub vote_count: u64,
}

#[derive(Clone, Debug)]
pub struct TvShowWithSubscription {
    pub tvshow: TvShow,
    pub subscription: TvShowSubscription,
}

#[derive(Clone, Debug)]
pub struct TvShowSubscription {
    pub user_id: u64,
    pub tvshow_id: u64,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[serde(tag = "method")]
pub enum TvShowTask {
    SynchronizeAll,
    SynchronizeLocated { tvshow_id: u64, language: Language },
    Synchronize { tvshow_id: u64 },
    SynchronizeSeasonLocated(SynchronizeSeasonParams),
}

#[derive(Debug)]
pub struct TvShowEpisodeProgressInput {
    pub user_id: u64,
    pub tvshow_episode_id: u64,
    pub progress: u64,
    pub completed: bool,
}

#[derive(Debug)]
pub struct TvShowEpisodeProgress {
    pub user_id: u64,
    pub tvshow_episode_id: u64,
    pub progress: u64,
    pub completed: bool,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub updated_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug)]
pub struct TvShowEpisodeFile {
    pub tvshow_episode_id: u64,
    pub media_file: MediaFile,
}

#[derive(Debug)]
pub struct FindTvShowResponse {
    pub tvshow: TvShow,
    pub subscription: Option<TvShowSubscription>,
}

#[derive(Debug)]
pub struct ListTvShowResponse {
    pub tvshows: Vec<TvShow>,
    pub subscriptions: HashMap<u64, TvShowSubscription>,
}

#[derive(Debug)]
pub struct FindTvShowEpisodeResponse {
    pub tvshow: TvShow,
    pub season: TvShowSeason,
    pub episode: TvShowEpisode,
    pub files: Vec<TvShowEpisodeFile>,
    pub progress: Option<TvShowEpisodeProgress>,
    pub subscription: Option<TvShowSubscription>,
}

#[derive(Debug)]
pub struct ListTvShowEpisodeResponse {
    pub episodes: Vec<TvShowEpisode>,
    // season_id as key
    pub seasons: HashMap<u64, TvShowSeason>,
    // tvshow_id as key
    pub tvshows: HashMap<u64, TvShow>,
    // episode_id as key
    pub files: HashMap<u64, Vec<TvShowEpisodeFile>>,
    // episode_id as key
    pub progresses: HashMap<u64, TvShowEpisodeProgress>,
    // tvshow_id as key
    pub subscriptions: HashMap<u64, TvShowSubscription>,
}

#[derive(Debug)]
pub struct FindTvShowSeasonResponse {
    pub tvshow: TvShow,
    pub season: TvShowSeason,
    pub subscription: Option<TvShowSubscription>,
}

#[derive(Debug)]
pub struct ListTvShowSeasonResponse {
    // season_id as key
    pub seasons: Vec<TvShowSeason>,
    // tvshow_id as key
    pub tvshows: HashMap<u64, TvShow>,
    // tvshow_id as key
    pub subscriptions: HashMap<u64, TvShowSubscription>,
}

#[derive(Clone, Copy, Debug)]
pub enum TvShowSeasonField {
    SeasonNumber,
}

#[derive(Clone, Copy, Debug)]
pub struct ListTvShowSeasonFilter<'a> {
    pub tvshow_id: Option<u64>,
    pub season_ids: &'a [u64],
}

#[derive(Clone, Copy, Debug)]
pub struct ListTvShowSeasonParams<'a> {
    pub user_id: u64,
    pub language: Language,
    pub filter: ListTvShowSeasonFilter<'a>,
    pub sort: Sort<TvShowSeasonField>,
    pub page: Page,
}

#[derive(Clone, Copy, Debug)]
pub enum TvShowEpisodeField {
    AirDate,
    EpisodeNumber,
}

#[derive(Clone, Copy, Debug)]
pub struct ListTvShowEpisodeFilter<'a> {
    pub tvshow_ids: &'a [u64],
    pub season_ids: &'a [u64],
    pub subscribed: Option<bool>,
    pub watched: Option<bool>,
}

#[derive(Clone, Copy, Debug)]
pub struct ListTvShowEpisodeParams<'a> {
    pub user_id: u64,
    pub language: Language,
    pub filter: ListTvShowEpisodeFilter<'a>,
    pub sort: Sort<TvShowEpisodeField>,
    pub page: Page,
}

#[derive(Clone, Debug)]
pub struct ListEpisodeFileParams<'a> {
    pub episode_ids: &'a [u64],
    pub content_type: Option<String>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct SynchronizeSeasonParams {
    pub tvshow_id: u64,
    pub source: TvShowSource,
    pub season: u64,
    pub language: Language,
}

#[derive(Clone, Copy, Debug)]
pub struct ListTvShowFilter<'a> {
    pub tvshow_ids: &'a [u64],
    pub subscribed: Option<bool>,
}

#[derive(Clone, Copy, Debug)]
pub struct ListTvShowParams<'a> {
    pub user_id: u64,
    pub language: Language,
    pub filter: ListTvShowFilter<'a>,
    pub page: Page,
}
