use crate::{collections::Result, dyn_types::key_type::GetKeyType};
use core::num::NonZeroU32;

use super::ArmourError;

#[inline]
pub fn get_4bytes(slice: &[u8]) -> &[u8; 4] {
    let arr: &[u8; 4] = slice.try_into().unwrap();
    arr
}

pub fn bytes_unsafe<const N: usize>(slice: &[u8]) -> &[u8; N] {
    debug_assert!(slice.len() < N);
    unsafe {
        let bytes: &[u8] = slice.get_unchecked(..N);
        let arr: &[u8; N] = core::mem::transmute_copy(&bytes);
        arr
    }
}

pub fn as_bytes_n<const N: usize, const M: usize>(bytes: &[u8; M]) -> &[u8; N] {
    debug_assert!(M > N);
    unsafe {
        let bytes: &[u8] = bytes.get_unchecked(..N);
        let arr: &[u8; N] = core::mem::transmute_copy(&bytes);
        arr
    }
}

/**
Composite ID for multiple included ids, like vote id - user_id+vote_id
For id
*/
pub trait Cid: Sized + Copy + Eq + Ord + GetKeyType {
    type B: Sized + AsRef<[u8]>;

    fn bytes(&self) -> Self::B;

    fn from_bytes(bytes: &[u8]) -> Result<Self>;

    fn from_bytes_unsafe(bytes: &[u8]) -> Self;
}

impl Cid for u32 {
    type B = [u8; 4];

    fn bytes(&self) -> Self::B {
        self.to_be_bytes()
    }

    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        let bytes: [u8; 4] = bytes.try_into().map_err(|_| ArmourError::FromBytesError)?;
        Ok(u32::from_be_bytes(bytes))
    }

    fn from_bytes_unsafe(bytes: &[u8]) -> Self {
        let u = u32::from_be_bytes(*bytes_unsafe(bytes));
        u
    }
}

impl Cid for NonZeroU32 {
    type B = [u8; 4];

    fn bytes(&self) -> Self::B {
        self.get().to_be_bytes()
    }

    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        let bytes: [u8; 4] = bytes.try_into().map_err(|_| ArmourError::FromBytesError)?;
        let id = NonZeroU32::new(u32::from_be_bytes(bytes));
        id.ok_or(ArmourError::NonZeroError)
    }

    fn from_bytes_unsafe(bytes: &[u8]) -> Self {
        unsafe {
            let u = u32::from_be_bytes(*bytes_unsafe(bytes));
            NonZeroU32::new_unchecked(u)
        }
    }
}

// impl<const N: usize> Cid for [u8; N] {
//     const SIZE: usize = N;
//     fn as_bytes(&self) -> Box<[u8]> {
//         Box::new(self)
//     }
//     fn from_bytes(bytes: &[u8]) -> Result<Self> {
//         let bytes: [u8; N] = bytes.try_into().map_err(|_| ArmourError::FromBytesError)?;
//         Ok(bytes)
//     }
// }
// impl<const N: usize> Cid for [u32; N] {
//     const SIZE: usize = N * 4;
//     fn as_bytes(&self) -> Box<[u8]> {
//         let bytes: [[u8; 4]; N] = self.map(|u| u.to_be_bytes());
//         bytes.into_iter().flatten().collect()
//     }
//     fn from_bytes(bytes: &[u8]) -> Result<Self> {
//         if bytes.len() != <Self as Cid>::SIZE {
//             return Err(ArmourError::BytesLenError);
//         }
//         let chunks: Vec<u32> = bytes
//             .chunks_exact(<Self as Cid>::SIZE)
//             .map(|chunk| {
//                 let c: [u8; 4] = chunk.try_into().expect("cannot cid");
//                 u32::from_be_bytes(c)
//             })
//             .collect();
//         let val: [u32; N] = chunks.try_into().map_err(|_| ArmourError::FromBytesError)?;
//         Ok(val)
//     }
// }
// impl<A: Cid, B: Cid> Cid for (A, B) {
//     const SIZE: usize = A::SIZE + B::SIZE;
//     fn as_bytes(&self) -> Box<[u8]> {
//         let a = self.0.as_bytes();
//         let b = self.1.as_bytes();
//         let mut vec: Vec<u8> = Vec::with_capacity(Self::SIZE);
//         // TODO: use copy_from_slice instead
//         vec.extend_from_slice(a.as_ref());
//         vec.extend_from_slice(b.as_ref());
//         vec.into_boxed_slice()
//     }
//     fn from_bytes(bytes: &[u8]) -> Result<Self> {
//         let a = &bytes[..A::SIZE];
//         let b = &bytes[A::SIZE..];
//         let a = A::from_bytes(a)?;
//         let b = B::from_bytes(b)?;
//         Ok((a, b))
//     }
// }
// impl<A: Cid, B: Cid, C: Cid> Cid for (A, B, C) {
//     const SIZE: usize = A::SIZE + B::SIZE + C::SIZE;
//     fn as_bytes(&self) -> Box<[u8]> {
//         let a = self.0.as_bytes();
//         let b = self.1.as_bytes();
//         let c = self.2.as_bytes();
//         let mut vec: Vec<u8> = Vec::with_capacity(Self::SIZE);
//         // TODO: use copy_from_slice instead
//         vec.extend_from_slice(a.as_ref());
//         vec.extend_from_slice(b.as_ref());
//         vec.extend_from_slice(c.as_ref());
//         vec.into_boxed_slice()
//     }
//     fn from_bytes(bytes: &[u8]) -> Result<Self> {
//         let a = &bytes[..A::SIZE];
//         let ab_size: usize = A::SIZE + B::SIZE;
//         let b = &bytes[A::SIZE..ab_size];
//         let c = &bytes[ab_size..];
//         let a = A::from_bytes(a)?;
//         let b = B::from_bytes(b)?;
//         let c = C::from_bytes(c)?;
//         Ok((a, b, c))
//     }
// }
// impl<A: Cid, B: Cid, C: Cid, D: Cid> Cid for (A, B, C, D) {
//     const SIZE: usize = A::SIZE + B::SIZE + C::SIZE + D::SIZE;
//     fn as_bytes(&self) -> Box<[u8]> {
//         let a = self.0.as_bytes();
//         let b = self.1.as_bytes();
//         let c = self.2.as_bytes();
//         let d = self.3.as_bytes();
//         let mut vec: Vec<u8> = Vec::with_capacity(Self::SIZE);
//         // TODO: use copy_from_slice instead
//         vec.extend_from_slice(a.as_ref());
//         vec.extend_from_slice(b.as_ref());
//         vec.extend_from_slice(c.as_ref());
//         vec.extend_from_slice(d.as_ref());
//         vec.into_boxed_slice()
//     }
//     fn from_bytes(bytes: &[u8]) -> Result<Self> {
//         let a = &bytes[..A::SIZE];
//         let ab_size: usize = A::SIZE + B::SIZE;
//         let abc_size: usize = A::SIZE + B::SIZE + C::SIZE;
//         let b = &bytes[A::SIZE..ab_size];
//         let c = &bytes[ab_size..abc_size];
//         let d = &bytes[abc_size..];
//         let a = A::from_bytes(a)?;
//         let b = B::from_bytes(b)?;
//         let c = C::from_bytes(c)?;
//         let d = D::from_bytes(d)?;
//         Ok((a, b, c, d))
//     }
// }
