use std::{cmp::Ordering, str::FromStr};
use url::Url;

#[derive(Debug, Clone, Copy, Eq)]
pub struct IconSize {
    pub width: u32,
    pub height: u32,
}

impl PartialEq for IconSize {
    fn eq(&self, other: &Self) -> bool {
        self.width == other.width && self.height == other.height
    }
}

impl PartialOrd for IconSize {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for IconSize {
    fn cmp(&self, other: &Self) -> Ordering {
        self.total_pixels().cmp(&other.total_pixels())
    }
}

impl IconSize {
    pub fn new(width: u32, height: u32) -> Self {
        Self { width, height }
    }

    pub fn total_pixels(&self) -> u32 {
        self.width * self.height
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IconInfo {
    pub url: Url,
    pub size: Option<IconSize>,
}

impl PartialOrd for IconInfo {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for IconInfo {
    fn cmp(&self, other: &Self) -> Ordering {
        let self_size = self.size.map(|s| s.total_pixels()).unwrap_or(0);
        let other_size = other.size.map(|s| s.total_pixels()).unwrap_or(0);

        self_size.cmp(&other_size)
    }
}

impl IconInfo {
    pub fn new(url: Url, sizes_prop: Option<&str>) -> Self {
        let size = sizes_prop.map(|sizes_str| {
            let width_height: Vec<&str> = sizes_str.split('x').collect();
            let width = width_height.first().and_then(|w| u32::from_str(w).ok()).unwrap_or(0);
            let height = width_height.get(1).and_then(|h| u32::from_str(h).ok()).unwrap_or(0);
            IconSize::new(width, height)
        });

        Self { url, size }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_icon_size_cmp() {
        let size1 = IconSize::new(10, 10);
        let size2 = IconSize::new(20, 20);
        let size3 = IconSize::new(10, 10);

        assert!(size1 < size2);
        assert!(size2 > size1);
        assert!(size1 == size3);
    }

    #[test]
    fn test_icon_info_new() {
        let url = Url::parse("http://example.com/icon.png").unwrap();
        let icon_info = IconInfo::new(url.clone(), Some("128x128"));

        assert_eq!(icon_info.url, url);
        assert_eq!(icon_info.size, Some(IconSize::new(128, 128)));
    }

    #[test]
    fn icon_info_size_parsing() {
        let url = Url::parse("https://example.com/icon.png").unwrap();
        let info = IconInfo::new(url.clone(), Some("32x32"));
        assert_eq!(info.size, Some(IconSize::new(32, 32)));
        let info = IconInfo::new(url.clone(), Some("any"));
        assert_eq!(info.size.unwrap().total_pixels(), 0); // "any" → 0x0
        let info = IconInfo::new(url.clone(), None);
        assert!(info.size.is_none());
    }
}
