use glib::{Enum, Properties, prelude::*, subclass::prelude::*};
use news_flash::models::{ArticleOrder, OrderBy};
use serde::{Deserialize, Serialize};
use std::cell::Cell;

mod imp {
    use super::*;

    #[derive(Debug, Serialize, Deserialize, Properties)]
    #[properties(wrapper_type = super::ArticleListSettings)]
    pub struct ArticleListSettings {
        #[property(get, set, builder(GArticleOrder::NewestFirst))]
        #[serde(default)]
        pub order: Cell<GArticleOrder>,

        #[property(get, set, name = "order-by", builder(GOrderBy::Published))]
        #[serde(default)]
        pub order_by: Cell<GOrderBy>,

        #[property(get, set, name = "show-thumbnails", default = true)]
        #[serde(default)]
        pub show_thumbnails: Cell<bool>,

        #[property(get, set, name = "hide-future-articles", default = false)]
        #[serde(default)]
        pub hide_future_articles: Cell<bool>,
    }

    impl Default for ArticleListSettings {
        fn default() -> Self {
            Self {
                order: Cell::new(GArticleOrder::default()),
                order_by: Cell::new(GOrderBy::default()),
                show_thumbnails: Cell::new(true),
                hide_future_articles: Cell::new(false),
            }
        }
    }

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

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

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

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

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

impl From<imp::ArticleListSettings> for ArticleListSettings {
    fn from(inner: imp::ArticleListSettings) -> Self {
        glib::Object::builder()
            .property("order", inner.order.get())
            .property("order-by", inner.order_by.get())
            .property("show-thumbnails", inner.show_thumbnails.get())
            .property("hide-future-articles", inner.hide_future_articles.get())
            .build()
    }
}

impl Default for ArticleListSettings {
    fn default() -> Self {
        imp::ArticleListSettings::default().into()
    }
}

#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Enum)]
#[repr(u32)]
#[enum_type(name = "GArticleOrder")]
pub enum GArticleOrder {
    #[default]
    NewestFirst,
    OldestFirst,
}

impl From<u32> for GArticleOrder {
    fn from(value: u32) -> Self {
        match value {
            0 => Self::NewestFirst,
            1 => Self::OldestFirst,
            _ => Self::NewestFirst,
        }
    }
}

impl From<i32> for GArticleOrder {
    fn from(value: i32) -> Self {
        match value {
            0 => Self::NewestFirst,
            1 => Self::OldestFirst,
            _ => Self::NewestFirst,
        }
    }
}

impl From<GArticleOrder> for u32 {
    fn from(value: GArticleOrder) -> Self {
        match value {
            GArticleOrder::NewestFirst => 0,
            GArticleOrder::OldestFirst => 1,
        }
    }
}

impl From<GArticleOrder> for i32 {
    fn from(value: GArticleOrder) -> Self {
        match value {
            GArticleOrder::NewestFirst => 0,
            GArticleOrder::OldestFirst => 1,
        }
    }
}

impl From<GArticleOrder> for ArticleOrder {
    fn from(value: GArticleOrder) -> Self {
        match value {
            GArticleOrder::NewestFirst => ArticleOrder::NewestFirst,
            GArticleOrder::OldestFirst => ArticleOrder::OldestFirst,
        }
    }
}

impl From<ArticleOrder> for GArticleOrder {
    fn from(value: ArticleOrder) -> Self {
        match value {
            ArticleOrder::NewestFirst => GArticleOrder::NewestFirst,
            ArticleOrder::OldestFirst => GArticleOrder::OldestFirst,
        }
    }
}

#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Enum)]
#[repr(u32)]
#[enum_type(name = "GOrderBy")]
pub enum GOrderBy {
    Updated,
    #[default]
    Published,
}

impl From<u32> for GOrderBy {
    fn from(value: u32) -> Self {
        match value {
            0 => Self::Updated,
            1 => Self::Published,
            _ => Self::Updated,
        }
    }
}

impl From<i32> for GOrderBy {
    fn from(value: i32) -> Self {
        match value {
            0 => Self::Updated,
            1 => Self::Published,
            _ => Self::Updated,
        }
    }
}

impl From<GOrderBy> for u32 {
    fn from(value: GOrderBy) -> Self {
        match value {
            GOrderBy::Updated => 0,
            GOrderBy::Published => 1,
        }
    }
}

impl From<GOrderBy> for i32 {
    fn from(value: GOrderBy) -> Self {
        match value {
            GOrderBy::Updated => 0,
            GOrderBy::Published => 1,
        }
    }
}

impl From<GOrderBy> for OrderBy {
    fn from(value: GOrderBy) -> Self {
        match value {
            GOrderBy::Updated => OrderBy::Updated,
            GOrderBy::Published => OrderBy::Published,
        }
    }
}

impl From<OrderBy> for GOrderBy {
    fn from(value: OrderBy) -> Self {
        match value {
            OrderBy::Updated => GOrderBy::Updated,
            OrderBy::Published => GOrderBy::Published,
        }
    }
}
