use super::{GFeedListItem, GFeedListItemID, GTag, GTagID};
use glib::{Enum, Object, Properties, prelude::*, subclass::prelude::*};
use serde::{Deserialize, Serialize};
use std::cell::{Cell, RefCell};

#[derive(Debug, Default, Eq, PartialEq, Clone, Copy, Enum, Serialize, Deserialize)]
#[repr(u32)]
#[enum_type(name = "GSidebarSelectionType")]
pub enum SidebarSelectionType {
    #[default]
    None,
    All,
    Today,
    FeedList,
    TagList,
}

mod imp {
    use super::*;

    #[derive(Default, Serialize, Deserialize, Properties)]
    #[properties(wrapper_type = super::GSidebarSelection)]
    pub struct GSidebarSelection {
        #[property(get, set, builder(SidebarSelectionType::All))]
        pub selection_type: Cell<SidebarSelectionType>,

        #[property(get, set, name = "feedlist-id")]
        pub feedlist_id: RefCell<GFeedListItemID>,

        #[property(get, set, name = "tag-id")]
        pub tag_id: RefCell<GTagID>,

        #[property(get, set, nullable)]
        pub label: RefCell<Option<String>>,

        #[property(get, set)]
        pub count: Cell<u32>,

        #[property(get, set)]
        pub position: Cell<u32>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for GSidebarSelection {
        const NAME: &'static str = "GSidebarSelection";
        type Type = super::GSidebarSelection;
    }

    #[glib::derived_properties]
    impl ObjectImpl for GSidebarSelection {}

    impl GSidebarSelection {}
}

glib::wrapper! {
    pub struct GSidebarSelection(ObjectSubclass<imp::GSidebarSelection>);
}

impl Default for GSidebarSelection {
    fn default() -> Self {
        Object::new()
    }
}

impl Serialize for GSidebarSelection {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.imp().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for GSidebarSelection {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let inner = imp::GSidebarSelection::deserialize(deserializer)?;
        Ok(inner.into())
    }
}

impl From<imp::GSidebarSelection> for GSidebarSelection {
    fn from(inner: imp::GSidebarSelection) -> Self {
        glib::Object::builder::<GSidebarSelection>()
            .property("selection_type", inner.selection_type.get())
            .property("feedlist-id", inner.feedlist_id.borrow().clone())
            .property("tag-id", inner.tag_id.borrow().clone())
            .property("label", inner.label.borrow().clone())
            .property("count", inner.count.get())
            .property("position", inner.position.get())
            .build()
    }
}

impl GSidebarSelection {
    pub fn all(count: u32) -> Self {
        let obj = Self::default();
        let imp = obj.imp();
        imp.selection_type.set(SidebarSelectionType::All);
        imp.count.replace(count);
        obj
    }

    pub fn today(count: u32) -> Self {
        let obj = Self::default();
        let imp = obj.imp();
        imp.selection_type.set(SidebarSelectionType::Today);
        imp.count.replace(count);
        obj
    }

    pub fn from_feed_list(item: GFeedListItem, pos: u32) -> Self {
        let obj = Self::default();
        let imp = obj.imp();
        imp.selection_type.set(SidebarSelectionType::FeedList);
        imp.feedlist_id.replace(item.item_id());
        imp.label.replace(Some(item.label()));
        imp.count.replace(item.item_count());
        imp.position.set(pos);
        obj
    }

    pub fn from_tag_list(tag: GTag, pos: u32) -> Self {
        let obj = Self::default();
        let imp = obj.imp();
        imp.selection_type.set(SidebarSelectionType::TagList);
        imp.tag_id.replace(tag.tag_id());
        imp.label.replace(Some(tag.label()));
        imp.position.set(pos);
        obj
    }

    pub fn eq(&self, other: &GSidebarSelection) -> bool {
        match self.selection_type() {
            SidebarSelectionType::None => matches!(other.selection_type(), SidebarSelectionType::None),
            SidebarSelectionType::Today => matches!(other.selection_type(), SidebarSelectionType::Today),
            SidebarSelectionType::All => matches!(other.selection_type(), SidebarSelectionType::All),
            SidebarSelectionType::FeedList => match other.selection_type() {
                SidebarSelectionType::FeedList => self.feedlist_id() == other.feedlist_id(),
                _ => false,
            },
            SidebarSelectionType::TagList => match other.selection_type() {
                SidebarSelectionType::TagList => self.tag_id() == other.tag_id(),
                _ => false,
            },
        }
    }
}
