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

use harsh::Harsh;
use rapira::{Rapira, RapiraError};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use super::ArmourError;
use crate::{collections::Result, GetType, Typ};

// default: b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
#[cfg(feature = "std")]
const HARSH_ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz1234567890";
// default: b"cfhistuCFHISTU"
#[cfg(feature = "std")]
const SEPARATORS: &[u8] = b"cfhistu";

#[cfg(feature = "std")]
pub fn create_harsh(salt: &str) -> Harsh {
    Harsh::builder()
        .salt(salt)
        .length(2)
        .alphabet(HARSH_ALPHABET)
        .separators(SEPARATORS)
        .build()
        .unwrap()
}

pub trait HarshIdent {
    #[cfg(feature = "std")]
    fn get_hasher() -> &'static Harsh;

    #[cfg(feature = "std")]
    fn get_name() -> &'static str;
}

#[repr(transparent)]
pub struct Hashid<T>(NonZeroU32, PhantomData<T>);

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

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

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

    #[inline]
    pub fn new_unsafe(id: u32) -> Self {
        let id = NonZeroU32::new(id).unwrap();
        Hashid(id, PhantomData)
    }

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

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

    #[inline]
    pub fn from_le_bytes(b: [u8; 4]) -> Self {
        let id = u32::from_le_bytes(b);
        Self::new_unsafe(id)
    }

    #[inline]
    pub fn from_be_bytes(b: [u8; 4]) -> Self {
        let id = u32::from_be_bytes(b);
        Self::new_unsafe(id)
    }

    pub fn increment(self) -> Self {
        Self::new_unsafe(self.get() + 1)
    }
}

impl<T: HarshIdent> Hashid<T> {
    #[cfg(feature = "std")]
    #[inline]
    pub fn get_name() -> &'static str {
        T::get_name()
    }

    #[inline]
    pub fn deser(s: &str) -> Result<Hashid<T>> {
        let v = T::get_hasher()
            .decode(s)
            .map_err(|_| ArmourError::UnknownError)
            .and_then(|vec| {
                vec.first()
                    .map(|u| *u as u32)
                    .ok_or(ArmourError::UnknownError)
            })?;
        // let v = T::deser(s)?;
        let v = NonZeroU32::new(v).ok_or(ArmourError::NonZeroError)?;
        Ok(Self::new(v))
    }

    #[inline]
    pub fn ser(self) -> String {
        T::get_hasher().encode(&[self.get() as u64])
    }
}

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

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

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

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

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

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

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

impl<T> From<Hashid<T>> for u32 {
    fn from(id: Hashid<T>) -> Self {
        id.get()
    }
}

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

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

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

#[cfg(feature = "std")]
impl<T: HarshIdent> Serialize for Hashid<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: HarshIdent> Deserialize<'de> for Hashid<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 = Hashid::<T>::deser(s).map_err(|_| D::Error::custom("id value error"))?;
        Ok(a)
    }
}

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

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

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

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

    #[inline]
    fn from_slice(slice: &mut &[u8]) -> Result<Self, rapira::RapiraError>
    where
        Self: Sized,
    {
        let u = u32::from_slice(slice)?;
        let id = NonZeroU32::new(u).ok_or(RapiraError::NonZeroError)?;
        Ok(Hashid::<T>::new(id))
    }

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

        if bytes == [0, 0, 0, 0] {
            return Err(RapiraError::NonZeroError);
        }

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

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

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

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

#[cfg(feature = "wa_proto")]
impl<T: HarshIdent> Incoming for Hashid<T> {
    #[cfg(not(feature = "std"))]
    fn init(args: &mut IterMut<u32>) -> Result<(u32, Self), ProtocolError>
    where
        Self: Sized,
    {
        let el: u32 = *args.next().ok_or(ProtocolError(ARGS_NEXT_ERROR))?;
        let id = Hashid::<T>::new_unsafe(el);
        Ok((id, el))
    }
    #[cfg(feature = "std")]
    fn args(&self, args: &mut Vec<u32>) -> Result<(), wa_proto::ProtocolError> {
        args.push(self.get());
        Ok(())
    }

    fn fill(
        &self,
        _: &mut std::cell::RefMut<[u8]>,
        args: &mut std::slice::Iter<u32>,
    ) -> Result<(), wa_proto::ProtocolError> {
        args.next()
            .ok_or_else(|| wa_proto::ProtocolError("args is end".to_owned()))?;
        Ok(())
    }
}

#[cfg(feature = "wa_proto")]
impl<T: HarshIdent> Outcoming for Hashid<T> {
    #[cfg(not(feature = "std"))]
    fn args(&self, args: &mut Vec<u32>) -> Result<(), ProtocolError> {
        args.push(self.get());
        Ok(())
    }

    #[cfg(feature = "std")]
    fn read(_: &[u8], args: &mut Iter<u32>) -> Result<Self, wa_proto::ProtocolError> {
        let u = *args
            .next()
            .ok_or_else(|| wa_proto::ProtocolError("args is end".to_owned()))?;
        let id = Hashid::<T>::new_unsafe(u);
        Ok(id)
    }
}

// impl<T: HarshIdent> From<Hashid<T>> for crate::Value {
//     fn from(val: Hashid<T>) -> Self {
//         crate::Value::U32(val.get())
//     }
// }

// impl<T: HarshIdent> TryFromValue for Hashid<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(Hashid::<T>::new(id))
//             }
//             _ => Err(crate::dyn_types::FromValueError::ValueMatchError),
//         }
//     }
// }

impl<T> GetType for Hashid<T> {
    const TYPE: Typ = Typ::U32;
}

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

// #[macro_export]
// macro_rules! hashid {
//     ($m_name: ident, $key: expr) => {
//         pub mod $m_name {
//             use std::sync::OnceLock;
//             use armour::collections::hashid::{create_harsh, HarshIdent, Hashid};
//             use harsh::Harsh;
//             static HARSH: OnceLock<Harsh> = OnceLock::new();
//             #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)]
//             pub struct Hasher;
//             impl HarshIdent for Hasher {
//                 #[inline]
//                 fn get_hasher() -> &'static Harsh {
//                     HARSH.get_or_init(|| create_harsh($key))
//                 }
//                 fn get_name() -> &'static str {
//                     stringify!($name)
//                 }
//             }
//         }
//     };
// }
