use core::fmt::{Debug, Formatter};

use alloc::rc::Rc;
use chrono::NaiveDateTime;
use password_hash::Key;
use serde::{Deserialize, Serialize};
use slint::{Model, SharedString, VecModel};
use spectre_api_sys::{SpectreAlgorithm, SpectreCounter, SpectreResultType};

#[derive(Deserialize, Serialize, Default)]
pub struct SpectreUsersConfig {
    #[serde(with = "vec_model")]
    pub users: Rc<VecModel<SpectreUserConfig>>,
}

impl Debug for SpectreUsersConfig {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("SpectreUsersConfig")
            .field_with("users", |f| {
                f.debug_list().entries(self.users.iter()).finish()
            })
            .finish()
    }
}

impl Clone for SpectreUsersConfig {
    fn clone(&self) -> Self {
        Self {
            users: Rc::new(self.users.iter().collect()),
        }
    }
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct SpectreUserConfig {
    pub username: SharedString,
    #[serde(with = "serde_bytes")]
    pub encrypted_key: Key,
    #[serde(with = "serde_bytes")]
    pub key_id: [u8; 32],
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct SpectreSiteConfig {
    #[serde(with = "with_repr")]
    pub algorithm: SpectreAlgorithm,
    pub counter: SpectreCounter::Type,
    #[serde(rename = "type")]
    #[serde(with = "with_repr")]
    pub result_type: SpectreResultType,
    pub password: Option<SharedString>,
    #[serde(with = "with_repr")]
    pub login_type: SpectreResultType,
    pub login_name: Option<SharedString>,
    pub uses: u32,
    pub last_used: NaiveDateTime,
}

impl Default for SpectreSiteConfig {
    fn default() -> Self {
        Self {
            algorithm: SpectreAlgorithm::Current,
            counter: SpectreCounter::Default,
            result_type: SpectreResultType::SpectreResultDefaultResult,
            password: None,
            login_type: SpectreResultType::SpectreResultDefaultLogin,
            login_name: None,
            uses: 0,
            last_used: Default::default(),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SpectreSite {
    pub username: SharedString,
    pub site_name: SharedString,
    pub config: SpectreSiteConfig,
}

mod vec_model {
    use alloc::{rc::Rc, vec::Vec};
    use serde::{Deserialize, Deserializer, Serialize, Serializer};
    use slint::{Model, VecModel};

    #[allow(unused)]
    pub fn serialize<T, S>(value: &Rc<VecModel<T>>, serializer: S) -> Result<S::Ok, S::Error>
    where
        T: Serialize,
        VecModel<T>: Model<Data = T>,
        S: Serializer,
    {
        let vec: Vec<T> = value.iter().collect();
        vec.serialize(serializer)
    }

    #[allow(unused)]
    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Rc<VecModel<T>>, D::Error>
    where
        T: Deserialize<'de>,
        D: Deserializer<'de>,
    {
        let vec = Vec::<T>::deserialize(deserializer)?;
        Ok(Rc::new(VecModel::from(vec)))
    }
}

mod with_repr {
    use serde::{Deserialize, Deserializer, Serializer};
    use spectre_api_sys::{SpectreAlgorithm, SpectreResultType};

    pub trait ReprConvert: Copy {
        type Repr: Copy;

        fn into_repr(self) -> Self::Repr;
        fn from_repr(repr: Self::Repr) -> Self;
    }

    impl ReprConvert for SpectreAlgorithm {
        type Repr = u32;

        fn from_repr(repr: Self::Repr) -> Self {
            unsafe { core::mem::transmute::<Self::Repr, Self>(repr) }
        }

        fn into_repr(self) -> Self::Repr {
            self as Self::Repr
        }
    }

    impl ReprConvert for SpectreResultType {
        type Repr = u32;

        fn from_repr(repr: Self::Repr) -> Self {
            unsafe { core::mem::transmute::<Self::Repr, Self>(repr) }
        }

        fn into_repr(self) -> Self::Repr {
            self as Self::Repr
        }
    }

    #[allow(unused)]
    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
    where
        T: ReprConvert<Repr = u32>,
        S: Serializer,
    {
        serializer.serialize_u32(value.into_repr())
    }

    #[allow(unused)]
    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
    where
        T: ReprConvert<Repr = u32>,
        D: Deserializer<'de>,
    {
        <u32 as Deserialize>::deserialize(deserializer).map(T::from_repr)
    }
}
