use super::ArmourError;
use crate::{collections::Result, GetType, Id};
use arrayvec::ArrayString;
#[cfg(feature = "std")]
use bincode::{Decode, Encode};
use blowfish::{
    cipher::{generic_array::GenericArray, BlockDecrypt, BlockEncrypt, KeyInit},
    BlowfishLE,
};
use core::hash::Hash;
#[cfg(feature = "wa_proto")]
use core::slice::Iter;
use data_encoding::{Encoding, Specification, BASE32_DNSSEC};
use once_cell::sync::Lazy;
use rapira::{Rapira, RapiraError};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{fmt::Debug, marker::PhantomData, num::NonZeroU32};
#[cfg(feature = "wa_proto")]
use wa_proto::{Incoming, Outcoming};

pub const ALPHABET: &str = "ybndrfg8ejkmcpqxot1uwisza345h769";

static ZBASE32: Lazy<Encoding> = Lazy::new(|| {
    let mut spec = Specification::new();
    spec.symbols.push_str(ALPHABET);
    spec.translate.from.push_str("YBNDRFGEJKMCPQXOTUWISZAH");
    spec.translate.to.push_str("ybndrfgejkmcpqxotuwiszah");
    spec.encoding().expect("cannot build ZBASE32")
});

#[inline]
pub fn serialize_id(id: u32, bf: &BlowfishLE) -> ArrayString<13> {
    let id = id as u64;
    let bytes = id.to_le_bytes();
    let mut generic_arr = GenericArray::from(bytes);
    bf.encrypt_block(&mut generic_arr);
    let mut buf: ArrayString<13> = ArrayString::zero_filled();
    ZBASE32.encode_mut(&generic_arr, unsafe { buf.as_bytes_mut() });
    buf
}

#[inline]
pub fn deserialize_id(id: &str, bf: &BlowfishLE) -> Result<u32> {
    if id.len() != 13 {
        return Err(ArmourError::ZBaseDecodeError);
    }
    let mut buf: [u8; 8] = [0; 8];
    ZBASE32
        .decode_mut(id.as_bytes(), &mut buf)
        .map_err(|_| ArmourError::ZBaseDecodeError)?;
    let mut generic_arr = GenericArray::from(buf);
    bf.decrypt_block(&mut generic_arr);
    let id = u64::from_le_bytes(generic_arr.into());
    Ok(id as u32)
}
pub trait IdHasher: Copy + Ord + Eq + Sized + Send + Sync + Debug {
    // #[cfg(feature = "std")]
    // fn get_id(self) -> u32;

    #[cfg(feature = "std")]
    fn get_hasher() -> &'static BlowfishLE;

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

    // #[cfg(feature = "std")]
    // #[inline]
    // fn hash(self) -> ArrayString<13> {
    //     serialize_id(self.get_id(), Self::get_hasher())
    // }
    #[cfg(feature = "std")]
    #[inline]
    fn hash(id: u32) -> ArrayString<13> {
        serialize_id(id, Self::get_hasher())
    }
    #[cfg(feature = "std")]
    #[inline]
    fn deser(s: &str) -> Result<u32> {
        deserialize_id(s, Self::get_hasher())
    }
}

#[cfg(feature = "std")]
pub fn create_bf(base32_dns_sec: &ArrayString<26>) -> BlowfishLE {
    let mut buf: [u8; 16] = [0; 16];
    BASE32_DNSSEC
        .decode_mut(base32_dns_sec.as_bytes(), &mut buf)
        .expect("cannot build blowfish key");
    BlowfishLE::new_from_slice(&buf).expect("cannot create bf")
}

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

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

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

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

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

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

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

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

impl<T: IdHasher> 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: IdHasher> ID<T> {
    #[cfg(feature = "std")]
    #[inline]
    pub fn get_name() -> &'static str {
        T::get_name()
    }

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

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

    pub fn into_id(self) -> Id<T> {
        Id::new(self)
    }

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

    #[inline]
    pub fn new_unsafe(id: u32) -> Self {
        let id = unsafe { NonZeroU32::new_unchecked(id) };
        ID(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 deser(s: &str) -> Result<ID<T>> {
        let v = T::deser(s)?;
        let v = NonZeroU32::new(v).ok_or(ArmourError::NonZeroError)?;
        Ok(Self::new(v))
    }

    pub fn ser(self) -> ArrayString<13> {
        T::hash(self.get())
    }
}

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

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

impl<T: IdHasher> std::fmt::Display for ID<T> {
    // TODO: display for only console?
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl<T: IdHasher> 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: IdHasher> 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)
    }
}

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

impl<T: IdHasher> Rapira for ID<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(ID::<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(ID::<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(ID::<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: IdHasher> Incoming for ID<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 = ID::<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_string()))?;
        Ok(())
    }
}

#[cfg(feature = "wa_proto")]
impl<T: IdHasher> Outcoming for ID<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_string()))?;
        let id = ID::<T>::new_unsafe(u);
        Ok(id)
    }
}

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

// 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: IdHasher> GetType for ID<T> {
    const TYPE: Option<crate::Typ> = Some(crate::Typ::U32);
}
