use std::collections::HashMap;

use crate::api::ClientVersion;
use crate::util::requester::Requester;
use serde;

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(untagged)]
pub enum FlexibleNumber {
    String(String),
    Int(u64),
}
impl FlexibleNumber {
    pub fn to_u8(&self) -> Option<u8> {
        match self {
            FlexibleNumber::String(value) => value.parse::<u8>().ok(),
            FlexibleNumber::Int(value) => u8::try_from(*value).ok(),
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct VideosRequest {
    //"versionInstallDate":"2025-06-03T18:20:20Z","languageCode":"en","appInstallDate":"2025-06-03T18:20:20Z","server":"spacemoehre","sexu
    pub clientHash: Option<String>, // "a07b23c9b07813c65050e2a4041ca777",
    pub blockedKeywords: Option<String>, // "kittens",
    pub countryCode: Option<String>, // "DE",
    pub clientVersion: Option<String>, // "2.1.4-22b",
    pub timestamp: Option<String>,  // "1748976686",
    pub blockedUploaders: Option<String>, // "",
    pub anonId: Option<String>,     // "1AB8A060-A47D-47EF-B9CB-63980ED84C8A",
    pub debugTools: Option<bool>,   // false,
    pub versionInstallDate: Option<String>, // "2025-06-03T18:20:20Z",
    pub languageCode: Option<String>, // "en",
    pub appInstallDate: Option<String>, // "2025-06-03T18:20:20Z",
    pub server: Option<String>,     // "spacemoehre",
    pub sexuality: Option<String>,  // "straight",
    pub channel: Option<String>,    //"youtube",
    pub sort: Option<String>,       //"new",
    pub query: Option<String>,      //"kittens",
    pub page: Option<FlexibleNumber>, //1,
    pub perPage: Option<FlexibleNumber>, //10,
    // Your server's global options will be sent in the videos request
    //   pub flavor: "mint chocolate chip"
    pub featured: Option<String>,           // "featured",
    pub category: Option<String>,           // "pmv"
    pub sites: Option<String>,              //
    pub all_provider_sites: Option<String>, //
    pub filter: Option<String>,             //
    pub language: Option<String>,           //
    pub networks: Option<String>,           //
    pub stars: Option<String>,              //
    pub categories: Option<String>,
    pub duration: Option<String>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct ServerOptions {
    pub featured: Option<String>, // "featured",
    pub category: Option<String>, // "pmv"
    pub sites: Option<String>,    //
    pub filter: Option<String>,
    pub language: Option<String>, // "en"
    pub public_url_base: Option<String>,
    pub requester: Option<Requester>,
    pub network: Option<String>,    //
    pub stars: Option<String>,      //
    pub categories: Option<String>, //
    pub duration: Option<String>,   //
    pub sort: Option<String>,       //
    pub sexuality: Option<String>,  //
    pub client_version: Option<ClientVersion>,
}

#[derive(serde::Serialize, Debug)]
pub struct PageInfo {
    pub hasNextPage: bool,   // true,
    pub resultsPerPage: u32, // 10
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct VideoEmbed {
    pub html: String,
    pub source: String,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct VideoItem {
    pub duration: u32, // 110,
    #[serde(default)]
    pub isLive: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub views: Option<u32>, // 14622653,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub rating: Option<f32>, // 0.0,
    pub id: String, // "c85017ca87477168d648727753c4ded8a35f173e22ef93743e707b296becb299",
    pub title: String, // "20 Minutes of Adorable Kittens   BEST Compilation",
    pub url: String, // "https://www.youtube.com/watch?v=y0sF5xhGreA",
    pub channel: String, // "youtube",
    pub thumb: String, // "https://i.ytimg.com/vi/y0sF5xhGreA/hqdefault.jpg",
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uploader: Option<String>, // "The Pet Collective",
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uploaderUrl: Option<String>, // "https://www.youtube.com/@petcollective",
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uploaderId: Option<String>, // "petcollective",
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verified: Option<bool>, // false,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tags: Option<Vec<String>>, // [],
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uploadedAt: Option<u64>, // 1741142954
    #[serde(skip_serializing_if = "Option::is_none")]
    pub formats: Option<Vec<VideoFormat>>, // Additional HTTP headers if needed
    #[serde(skip_serializing_if = "Option::is_none")]
    pub embed: Option<VideoEmbed>, // Optional embed information
    #[serde(skip_serializing_if = "Option::is_none")]
    pub preview: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub aspectRatio: Option<f32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub playbackMethod: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub http_headers: Option<HashMap<String, String>>,
}
impl VideoItem {
    pub fn new(
        id: String,
        title: String,
        url: String,
        channel: String,
        thumb: String,
        duration: u32,
    ) -> Self {
        VideoItem {
            duration: duration, // Placeholder, adjust as needed
            isLive: false,
            views: None,  // Placeholder, adjust as needed
            rating: None, // Placeholder, adjust as needed
            id,
            title,
            url,
            channel,
            thumb,
            uploader: None,
            uploaderUrl: None,
            uploaderId: None,
            verified: None,
            tags: None, // Placeholder, adjust as needed
            uploadedAt: None,
            formats: None, // Placeholder for formats
            embed: None,   // Placeholder for embed information
            preview: None,
            aspectRatio: None,
            playbackMethod: None,
            http_headers: None,
        }
    }
    pub fn from(s: String) -> Result<Self, serde_json::Error> {
        serde_json::from_str::<VideoItem>(&s)
    }
    pub fn tags(mut self, tags: Vec<String>) -> Self {
        if tags.is_empty() {
            return self;
        }
        self.tags = Some(tags);
        self
    }
    pub fn uploader(mut self, uploader: String) -> Self {
        self.uploader = Some(uploader);
        self
    }
    pub fn uploader_url(mut self, uploader_url: String) -> Self {
        self.uploaderUrl = Some(uploader_url);
        self
    }
    pub fn views(mut self, views: u32) -> Self {
        self.views = Some(views);
        self
    }
    pub fn rating(mut self, rating: f32) -> Self {
        self.rating = Some(rating);
        self
    }
    pub fn uploaded_at(mut self, uploaded_at: u64) -> Self {
        self.uploadedAt = Some(uploaded_at);
        self
    }
    pub fn formats(mut self, formats: Vec<VideoFormat>) -> Self {
        if formats.is_empty() {
            return self;
        }
        self.formats = Some(formats);
        self
    }
    pub fn preview(mut self, preview: String) -> Self {
        self.preview = Some(preview);
        self
    }

    pub fn aspect_ratio(mut self, aspect_ratio: f32) -> Self {
        self.aspectRatio = Some(aspect_ratio);
        self
    }

    pub fn playback_method(mut self, playback_method: String) -> Self {
        self.playbackMethod = Some(playback_method);
        self
    }

    pub fn http_headers(mut self, headers: HashMap<String, String>) -> Self {
        self.http_headers = Some(headers);
        self
    }

    /// Re-applies the request host to every host-relative proxy URL on this item
    /// (its `url`, `preview`, and each format's `url`). Cached items may have been
    /// built without a host; see [`crate::providers::ensure_proxy_url_host`].
    pub fn ensure_proxy_host(&mut self, public_url_base: &str) {
        self.url = crate::providers::ensure_proxy_url_host(&self.url, public_url_base);
        if let Some(preview) = &self.preview {
            self.preview =
                Some(crate::providers::ensure_proxy_url_host(preview, public_url_base));
        }
        if let Some(formats) = &mut self.formats {
            for format in formats.iter_mut() {
                format.url = crate::providers::ensure_proxy_url_host(&format.url, public_url_base);
            }
        }
    }

    #[cfg(any(
        not(hottub_single_provider),
        hottub_provider = "chaturbate",
        hottub_provider = "camsoda"
    ))]
    pub fn is_live(mut self, is_live: bool) -> Self {
        self.isLive = is_live;
        self
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct VideoFormat {
    pub url: String,
    quality: String,
    format: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    format_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    format_note: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    filesize: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    asr: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    fps: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    width: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    height: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    tbr: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    language: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    language_preference: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    ext: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    vcodec: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    acodec: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    dynamic_range: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    abr: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    vbr: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    container: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    protocol: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    audio_ext: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    video_ext: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    resolution: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    http_headers: Option<HashMap<String, String>>,
}
impl VideoFormat {
    pub fn new(url: String, quality: String, format: String) -> Self {
        let _ = format;
        VideoFormat {
            url,
            quality,
            format: format, // Default format
            format_id: Some("mp4-1080".to_string()),
            format_note: None,
            filesize: None,
            asr: None,
            fps: None,
            width: None,
            height: None,
            tbr: None,
            language: None,
            language_preference: None,
            ext: Some("mp4".to_string()),
            vcodec: None,
            acodec: None,
            dynamic_range: None,
            abr: None,
            vbr: None,
            container: None,
            protocol: Some("https".to_string()),
            audio_ext: Some("none".to_string()),
            video_ext: Some("mp4".to_string()),
            resolution: None,
            http_headers: None,
        }
    }
        pub fn m3u8(url: String, quality: String, format: String) -> Self {
        let _ = format;
        VideoFormat {
            url,
            quality,
            format: format, // Default format
            format_id: Some("m3u8-1080".to_string()),
            format_note: None,
            filesize: None,
            asr: None,
            fps: None,
            width: None,
            height: None,
            tbr: None,
            language: None,
            language_preference: None,
            ext: Some("m3u8".to_string()),
            vcodec: None,
            acodec: None,
            dynamic_range: None,
            abr: None,
            vbr: None,
            container: None,
            protocol: Some("m3u8_native".to_string()),
            audio_ext: Some("none".to_string()),
            video_ext: Some("m3u8".to_string()),
            resolution: None,
            http_headers: None,
        }
    }
    pub fn add_http_header(&mut self, key: String, value: String) {
        if self.http_headers.is_none() {
            self.http_headers = Some(HashMap::new());
        }
        if let Some(headers) = &mut self.http_headers {
            headers.insert(key, value);
        }
    }
    pub fn http_header(&mut self, key: String, value: String) -> Self {
        if self.http_headers.is_none() {
            self.http_headers = Some(HashMap::new());
        }
        if let Some(headers) = &mut self.http_headers {
            headers.insert(key, value);
        }
        self.to_owned()
    }
    pub fn http_headers_pairs(&self) -> Vec<(String, String)> {
        self.http_headers
            .clone()
            .unwrap_or_default()
            .into_iter()
            .collect()
    }
    pub fn format_id(mut self, format_id: String) -> Self {
        self.format_id = Some(format_id);
        self
    }
    pub fn format_note(mut self, format_note: String) -> Self {
        self.format_note = Some(format_note);
        self
    }
     pub fn filesize(mut self, filesize: u32) -> Self {
         self.filesize = Some(filesize);
         self
     }
     pub fn asr(mut self, asr: u32) -> Self {
         self.asr = Some(asr);
         self
    }
    pub fn fps(mut self, fps: u32) -> Self {
        self.fps = Some(fps);
        self
    }
    pub fn width(mut self, width: u32) -> Self {
        self.width = Some(width);
        self
    }
    pub fn height(mut self, height: u32) -> Self {
        self.height = Some(height);
        self
    }
    pub fn tbr(mut self, tbr: u32) -> Self {
        self.tbr = Some(tbr);
        self
    }
    pub fn language(mut self, language: String) -> Self {
        self.language = Some(language);
        self
    }
    pub fn language_preference(mut self, language_preference: u32) -> Self {
        self.language_preference = Some(language_preference);
        self
    }
    pub fn ext(mut self, ext: String) -> Self {
        self.ext = Some(ext);
        self
    }
    pub fn vcodec(mut self, vcodec: String) -> Self {
        self.vcodec = Some(vcodec);
        self
    }
    pub fn acodec(mut self, acodec: String) -> Self {
        self.acodec = Some(acodec);
        self
    }
    pub fn dynamic_range(mut self, dynamic_range: String) -> Self {
        self.dynamic_range = Some(dynamic_range);
        self
    }
    pub fn abr(mut self, abr: u32) -> Self {
        self.abr = Some(abr);
        self
    }
    pub fn vbr(mut self, vbr: u32) -> Self {
        self.vbr = Some(vbr);
        self
    }
    pub fn container(mut self, container: String) -> Self {
        self.container = Some(container);
        self
    }
    pub fn protocol(mut self, protocol: String) -> Self {
        self.protocol = Some(protocol);
        self
    }
    pub fn audio_ext(mut self, audio_ext: String) -> Self {
        self.audio_ext = Some(audio_ext);
        self
    }
    pub fn video_ext(mut self, video_ext: String) -> Self {
        self.video_ext = Some(video_ext);
        self
    }
    pub fn resolution(mut self, resolution: String) -> Self {
        self.resolution = Some(resolution);
        self
    }
    pub fn http_headers(mut self, http_headers: HashMap<String, String>) -> Self {
        self.http_headers = Some(http_headers);
        self
    }
}
#[derive(serde::Serialize, Debug)]
pub struct Videos {
    pub pageInfo: PageInfo,
    pub items: Vec<VideoItem>,
}
