use std::collections::HashMap;
use std::time::Duration;

use crate::prelude::{Page, Sort};
use crate::task::entity::Task;

#[derive(Debug)]
pub struct Podcast {
    pub id: u64,
    pub feed_url: String,
    pub title: String,
    pub description: Option<String>,
    pub image_url: Option<String>,
    pub language: Option<String>,
    pub website: Option<String>,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub updated_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug)]
pub enum PodcastFilterMode {
    Exclude,
    Include,
}

#[derive(Debug)]
pub enum PodcastFilterAttribute {
    Duration,
    Size,
}

#[derive(Debug)]
pub struct PodcastFilter {
    pub mode: PodcastFilterMode,
    pub attribute: PodcastFilterAttribute,
    pub value: u64,
}

#[derive(Debug)]
pub struct PodcastSubscription {
    pub user_id: u64,
    pub podcast_id: u64,
    pub queue_size: u32,
    pub min_duration: Option<u64>,
    pub max_duration: Option<u64>,
    pub created_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug)]
pub struct PodcastSubscriptionUpdate {
    pub min_duration: Option<u64>,
    pub max_duration: Option<u64>,
}

#[derive(Debug)]
pub struct PodcastWithSubscription {
    pub podcast: Podcast,
    // number of non listened episodes
    pub queue_size: u32,
}

#[derive(Debug)]
#[allow(unused, reason = "no methods to list episodes yet")]
pub struct PodcastEpisode {
    pub id: u64,
    pub podcast_id: u64,
    pub guid: Option<String>,
    pub published_at: Option<chrono::DateTime<chrono::Utc>>,
    pub title: String,
    pub description: Option<String>,
    pub link: Option<String>,
    pub duration: Option<Duration>,
    pub file_url: String,
    pub file_size: Option<u64>,
    pub file_type: Option<String>,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub updated_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug, bon::Builder)]
pub struct PodcastInput {
    #[builder(into)]
    pub feed_url: String,
    #[builder(into)]
    pub title: String,
    #[builder(into)]
    pub description: Option<String>,
    #[builder(into)]
    pub image_url: Option<String>,
    #[builder(into)]
    pub language: Option<String>,
    #[builder(into)]
    pub website: Option<String>,
    #[builder(default)]
    pub episodes: Vec<PodcastEpisodeInput>,
}

#[derive(Debug, bon::Builder)]
pub struct PodcastEpisodeInput {
    #[builder(into)]
    pub guid: Option<String>,
    #[builder(into)]
    pub published_at: Option<chrono::DateTime<chrono::Utc>>,
    #[builder(into)]
    pub title: String,
    #[builder(into)]
    pub description: Option<String>,
    #[builder(into)]
    pub link: Option<String>,
    #[builder(into)]
    pub duration: Option<Duration>,
    #[builder(into)]
    pub file_url: String,
    #[builder(into)]
    pub file_size: Option<u64>,
    #[builder(into)]
    pub file_type: Option<String>,
}

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

#[derive(Debug)]
pub struct PodcastEpisodeProgress {
    pub user_id: u64,
    pub podcast_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, serde::Deserialize, serde::Serialize)]
#[serde(tag = "method")]
pub enum PodcastTask {
    SynchronizeAll,
    Synchronize { podcast_id: u64 },
}

#[derive(Clone, Copy, Debug)]
pub enum PodcastEpisodeField {
    PublishedAt,
}

#[derive(Debug)]
pub struct FindPodcastResponse {
    pub podcast: Podcast,
    pub subscription: Option<PodcastSubscription>,
    pub synchronization: Option<Task>,
}

#[derive(Debug)]
pub struct FindPodcastEpisodeResponse {
    pub episode: PodcastEpisode,
    pub podcast: Podcast,
    pub progress: Option<PodcastEpisodeProgress>,
    pub subscription: Option<PodcastSubscription>,
    pub synchronization: Option<Task>,
}

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

#[derive(Clone, Copy, Debug)]
pub struct ListPodcastParams<'a> {
    pub user_id: Option<u64>,
    pub filter: ListPodcastFilter<'a>,
    pub page: Page,
}

#[derive(Debug)]
pub struct ListPodcastResponse {
    pub podcasts: Vec<Podcast>,
    pub subscriptions: HashMap<u64, PodcastSubscription>,
    pub synchronizations: HashMap<u64, Task>,
}

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

#[derive(Clone, Copy, Debug)]
pub struct ListPodcastEpisodeParams<'a> {
    pub user_id: u64,
    pub filter: ListPodcastEpisodeFilter<'a>,
    pub sort: Sort<PodcastEpisodeField>,
    pub page: Page,
}

#[derive(Debug)]
pub struct ListPodcastEpisodeResponse {
    pub episodes: Vec<PodcastEpisode>,
    pub podcasts: HashMap<u64, Podcast>,
    pub progresses: HashMap<u64, PodcastEpisodeProgress>,
    pub subscriptions: HashMap<u64, PodcastSubscription>,
    pub synchronizations: HashMap<u64, Task>,
}
