use core::{hash::Hash, str::FromStr};
use std::{fmt::Debug, marker::PhantomData, num::NonZeroU32};

use rapira::{Rapira, RapiraError};
use rend::NonZeroU32_be;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use zerocopy::{Immutable, IntoBytes, KnownLayout, TryFromBytes};

use super::{ArmourError, IdStr, enc::IdHasher, num_ops::g4bits};
use crate::{Cid, GetType, KeyScheme, KeyType, Typ, cid::SEQ_BITS, types::Result};

/// - 32bit sequence id
/// - Blowfish for encryption (64bit block)
/// - ZBASE32 for encoding into 13 length string
#[repr(transparent)]
#[cfg_attr(
    feature = "rkyv",
    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[derive(IntoBytes, FromBytes, Immutable, KnownLayout)]
pub struct ID<T>(pub NonZeroU32_be, PhantomData<T>);

impl<T> Clone for ID<T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<T> Copy for ID<T> {}

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

impl<T> Ord for ID<T> {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<T> PartialEq for ID<T> {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl<T> Eq for ID<T> {}

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

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

impl<T> ID<T> {
    #[inline]
    pub fn get(self) -> u32 {
        self.0.get()
    }

    #[inline]
    pub fn get_non_zero(self) -> NonZeroU32 {
        self.0.to_native()
    }

    #[inline]
    pub fn new(id: NonZeroU32) -> Self {
        ID(NonZeroU32_be::from_native(id), PhantomData)
    }

    #[inline]
    pub fn from_u32_le(id: u32) -> Result<Self> {
        let id = NonZeroU32::new(id).ok_or(ArmourError::NonZeroError)?;
        Ok(Self::new(id))
    }

    #[inline]
    pub fn to_le_bytes(self) -> [u8; 4] {
        self.0.get().to_le_bytes()
    }

    #[inline]
    pub fn to_be_bytes(self) -> [u8; 4] {
        zerocopy::transmute!(self)
    }

    #[inline]
    pub fn from_be_bytes(bytes: [u8; 4]) -> Result<Self> {
        zerocopy::try_transmute!(bytes).map_err(|_| ArmourError::NonZeroError)
    }

    #[inline]
    pub fn increment(self) -> Self {
        let id = self.0.to_native().saturating_add(1);
        Self::new(id)
    }
}

impl<T: IdHasher> ID<T> {
    #[inline]
    pub fn deser(s: &str) -> Result<ID<T>> {
        let v = T::deser(s)? as u32;
        zerocopy::try_transmute!(v).map_err(|_| ArmourError::NonZeroError)
    }

    #[inline]
    pub fn ser(self) -> IdStr {
        let u: u32 = zerocopy::transmute!(self);
        T::ser(u as u64)
    }
}

impl<T> TryFrom<u32> for ID<T> {
    type Error = ArmourError;
    fn try_from(val: u32) -> Result<ID<T>, Self::Error> {
        Ok(ID::new(val.try_into()?))
    }
}

impl<T: IdHasher> TryFrom<&str> for ID<T> {
    type Error = ArmourError;
    fn try_from(val: &str) -> Result<ID<T>, Self::Error> {
        Self::deser(val)
    }
}

impl<T: IdHasher> FromStr for ID<T> {
    type Err = ArmourError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::deser(s)
    }
}

impl<T> From<ID<T>> for u32 {
    #[inline(always)]
    fn from(id: ID<T>) -> Self {
        id.get()
    }
}

impl<T: IdHasher> std::fmt::Display for ID<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.ser())
    }
}

impl<T> Debug for ID<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("ID").field(&self.0).finish()
    }
}

impl<T> Hash for ID<T> {
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

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

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

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

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

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

        if bytes == [0u8; 4] {
            return Err(RapiraError::NonZero);
        }

        *slice = unsafe { slice.get_unchecked(4..) };
        Ok(())
    }

    #[inline]
    fn from_slice(slice: &mut &[u8]) -> Result<Self, rapira::RapiraError>
    where
        Self: Sized,
    {
        let bytes = <[u8; 4]>::from_slice(slice)?;
        let id = Self::decode_owned(bytes).map_err(|_| RapiraError::NonZero)?;
        Ok(id)
    }

    #[inline]
    fn convert_to_bytes(&self, slice: &mut [u8], cursor: &mut usize) {
        let bytes: &[u8; 4] = zerocopy::transmute_ref!(self);
        bytes.convert_to_bytes(slice, cursor);
    }
}

// impl<T: IdHasher> TryFromValue for ID<T> {
//     fn try_from(value: crate::Value) -> Result<Self, crate::dyn_types::FromValueError>
//     where
//         Self: Sized,
//     {
//         match value {
//             crate::Value::U32(u) => {
//                 let id = NonZeroU32::new(u)
//                     .ok_or(crate::dyn_types::FromValueError::NestedTypeFromError)?;
//                 Ok(ID::<T>::new(id))
//             }
//             _ => Err(crate::dyn_types::FromValueError::ValueMatchError),
//         }
//     }
// }

impl<T> GetType for ID<T> {
    const TYPE: Typ = Typ::Ident;
}

impl<T> Cid for ID<T> {
    type B = [u8; 4];
    const TY: KeyScheme = KeyScheme::Typed(&[KeyType::U32]);
    const GROUP_BITS: u32 = SEQ_BITS;

    #[inline]
    fn encode(&self) -> Self::B {
        zerocopy::transmute!(*self)
    }

    #[inline]
    fn encode_owned(self) -> Self::B {
        zerocopy::transmute!(self)
    }

    #[inline]
    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        let bytes: &[u8; 4] = bytes
            .get(..4)
            .ok_or(ArmourError::BytesLenError)?
            .try_into()?;

        Self::decode(bytes)
    }

    #[inline]
    fn decode(bytes: &Self::B) -> Result<Self> {
        zerocopy::try_transmute!(*bytes).map_err(|_| ArmourError::NonZeroError)
    }

    #[inline]
    fn decode_owned(bytes: Self::B) -> Result<Self> {
        zerocopy::try_transmute!(bytes).map_err(|_| ArmourError::NonZeroError)
    }

    /// 24 bits prefix for id in big endian (one group is 256 elements)
    #[inline]
    fn group_id(&self) -> u32 {
        let id = self.get();
        g4bits(id, Self::GROUP_BITS)
    }
}

#[cfg(feature = "ts-rs")]
impl<T> ts_rs::TS for ID<T> {
    type WithoutGenerics = ID<()>;
    type OptionInnerType = Self;
    fn name() -> String {
        "ID".to_owned()
    }
    fn decl_concrete() -> String {
        format!("type {} = {};", Self::name(), Self::inline())
    }
    fn decl() -> String {
        let inline = <ID<()> as ::ts_rs::TS>::inline();
        format!("type {} = {};", Self::name(), inline)
    }
    fn inline() -> String {
        "string".to_owned()
    }
    fn inline_flattened() -> String {
        panic!("{} cannot be flattened", Self::name())
    }
    fn output_path() -> Option<std::path::PathBuf> {
        Some(std::path::PathBuf::from("ID.ts"))
    }
}

#[cfg(feature = "ts")]
impl<T: 'static> TypeDef for ID<T> {
    const INFO: TypeInfo = TypeInfo::Native(NativeTypeInfo {
        r#ref: TypeExpr::ident(Ident("ID")),
    });
}
