use crate::{GetType, Typ};

use super::ident::{IdHasher, ID};
use arrayvec::ArrayString;
#[cfg(feature = "std")]
use bincode::{Decode, Encode};
use core::{num::NonZeroU32, ops::Deref};
use rapira::Rapira;
use serde::{Deserialize, Deserializer, Serialize, Serializer as SerdeSerializer};

#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Default)]
#[repr(transparent)]
pub struct OptionalId<T>(Option<ID<T>>)
where
    T: IdHasher;

impl<T: IdHasher> OptionalId<T> {
    pub fn get(self) -> Option<ID<T>> {
        self.0
    }

    pub fn get_u32(self) -> u32 {
        match self.0 {
            Some(v) => v.get(),
            None => 0,
        }
    }

    pub fn from_u32(u: u32) -> Self {
        let u = NonZeroU32::new(u).map(ID::new);
        Self(u)
    }

    pub fn ser(self) -> Option<ArrayString<13>> {
        self.0.map(|u| T::hash(u.get()))
    }
}

impl<T: IdHasher> Deref for OptionalId<T> {
    type Target = Option<ID<T>>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T: IdHasher> PartialEq<ID<T>> for OptionalId<T> {
    fn eq(&self, other: &ID<T>) -> bool {
        match self.0 {
            Some(id) => id.eq(other),
            None => false,
        }
    }
}

impl<T: IdHasher> From<Option<ID<T>>> for OptionalId<T> {
    fn from(val: Option<ID<T>>) -> Self {
        Self(val)
    }
}

#[cfg(feature = "std")]
impl<T: IdHasher> Serialize for OptionalId<T> {
    fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let s = self.ser();
        match s {
            None => serializer.serialize_none(),
            Some(s) => serializer.serialize_str(&s),
        }
    }
}

#[cfg(feature = "std")]
impl<'de, T: IdHasher> Deserialize<'de> for OptionalId<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        use serde::de::Error;
        let s: Option<&str> = Deserialize::deserialize(deserializer)?;
        match s {
            None => Ok(OptionalId(None)),
            Some(s) => {
                let a = ID::<T>::deser(s)
                    .map_err(|e| D::Error::custom(format!("id value error: {e}")))?;
                Ok(OptionalId(Some(a)))
            }
        }
    }
}

#[cfg(feature = "std")]
impl<T: IdHasher> Encode for OptionalId<T> {
    fn encode<E: bincode::enc::Encoder>(
        &self,
        encoder: &mut E,
    ) -> core::result::Result<(), bincode::error::EncodeError> {
        let u = self.get_u32();
        bincode::Encode::encode(&u, encoder)
    }
}

#[cfg(feature = "std")]
impl<T: IdHasher> Decode for OptionalId<T> {
    fn decode<D: bincode::de::Decoder>(
        decoder: &mut D,
    ) -> Result<Self, bincode::error::DecodeError> {
        let id: u32 = bincode::de::Decode::decode(decoder)?;
        Ok(Self::from_u32(id))
    }
}

impl<T: IdHasher> Rapira for OptionalId<T> {
    const STATIC_SIZE: Option<usize> = Some(4);

    #[inline]
    fn from_slice(slice: &mut &[u8]) -> Result<Self, rapira::RapiraError>
    where
        Self: Sized,
    {
        let u = u32::from_slice(slice)?;
        Ok(Self::from_u32(u))
    }

    #[inline]
    fn check_bytes(slice: &mut &[u8]) -> Result<(), rapira::RapiraError>
    where
        Self: Sized,
    {
        *slice = slice.get(4..).ok_or(rapira::RapiraError::SliceLenError)?;
        Ok(())
    }

    #[inline]
    unsafe fn from_slice_unsafe(slice: &mut &[u8]) -> Result<Self, rapira::RapiraError>
    where
        Self: Sized,
    {
        let u = u32::from_slice_unsafe(slice)?;
        Ok(Self::from_u32(u))
    }

    #[inline]
    fn convert_to_bytes(&self, slice: &mut [u8], cursor: &mut usize) {
        let u = self.get_u32();
        u.convert_to_bytes(slice, cursor);
    }

    #[inline]
    fn size(&self) -> usize {
        4
    }
}

impl<T: IdHasher> GetType for OptionalId<T> {
    const TYPE: Option<Typ> = Some(Typ::U32);
}
