use core::num::{NonZeroU32, NonZeroU64};

use arrayvec::ArrayString;
use rend::u64_be;
use uuid::Uuid;

pub use crate::types::key_type::{KeyScheme, KeyType, key_size};
use crate::types::{
    ArmourError, Result,
    num_ops::{g4bits, g8bits, g16bits},
};

/// Composite ID like (parent id +) id + status
pub trait Cid: Sized {
    type B: Sized + AsRef<[u8]> + for<'a> TryFrom<&'a [u8]>;

    const TY: KeyScheme;
    const SIZE: usize = match Self::TY {
        KeyScheme::Typed(_) => size_of::<Self>(),
        KeyScheme::Bytes => 0,
    };
    /// should be <= 32
    const GROUP_BITS: u32;

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

    #[inline]
    fn encode_owned(self) -> Self::B {
        self.encode()
    }

    #[inline]
    fn check_bytes(bytes: &[u8]) -> Result<()> {
        debug_assert!(Self::GROUP_BITS <= 32);

        match Self::TY {
            KeyScheme::Bytes => {
                Self::from_bytes(bytes)?;
            }
            _ => {
                if bytes.len() != Self::SIZE {
                    return Err(ArmourError::BytesLenError);
                }
            }
        }

        Ok(())
    }

    fn decode(bytes: &Self::B) -> Result<Self>;

    #[inline]
    fn decode_owned(bytes: Self::B) -> Result<Self> {
        Self::decode(&bytes)
    }

    #[inline]
    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        debug_assert!(Self::GROUP_BITS <= 32);

        let bytes: Self::B = bytes.try_into().map_err(|_| ArmourError::FromBytesError)?;
        Self::decode(&bytes)
    }

    /// # Safety
    ///
    /// This is unsafe fn
    #[inline]
    unsafe fn from_bytes_unsafe(bytes: &[u8]) -> Self {
        Self::from_bytes(bytes).expect("from_bytes error")
    }

    // TODO: should be T: AsRef<[u8]>
    /// group id, where stored changed field,
    /// for recalculation of the checksum
    ///
    /// default is big endian
    ///
    /// one group is:
    /// - 256 elements for ID u32
    /// - 65536 elements for u64
    /// - 12 days for Fuid
    /// - 19 days for LowId
    fn group_id(&self) -> u32;
}

/// group_id calc
///
/// 24 bits prefix for id in big endian
///
/// each group is 256 elements for ID
pub const SEQ_BITS: u32 = 24;

/// group_id calc
///
/// 16 bits prefix for id in big endian
///
/// each group is 65536 elements for ID
pub const SEQ64_BITS: u32 = 16;

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

    #[inline]
    fn encode(&self) -> Self::B {
        self.get().to_be_bytes()
    }

    #[inline]
    fn encode_owned(self) -> Self::B {
        self.get().to_be_bytes()
    }

    #[inline]
    fn decode(bytes: &Self::B) -> Result<Self> {
        let u = u32::from_be_bytes(*bytes);
        let u = NonZeroU32::new(u).ok_or(ArmourError::NonZeroError)?;
        Ok(u)
    }

    #[inline]
    fn decode_owned(bytes: Self::B) -> Result<Self> {
        let u = u32::from_be_bytes(bytes);
        let u = NonZeroU32::new(u).ok_or(ArmourError::NonZeroError)?;
        Ok(u)
    }

    #[inline]
    fn group_id(&self) -> u32 {
        g4bits(self.get(), Self::GROUP_BITS)
    }
}

impl Cid for NonZeroU64 {
    type B = [u8; 8];
    const TY: KeyScheme = KeyScheme::Typed(&[KeyType::U64]);
    const GROUP_BITS: u32 = SEQ64_BITS;

    #[inline]
    fn encode(&self) -> Self::B {
        self.get().to_be_bytes()
    }
    #[inline]
    fn encode_owned(self) -> Self::B {
        self.get().to_be_bytes()
    }

    #[inline]
    fn decode(bytes: &Self::B) -> Result<Self> {
        let u = u64::from_be_bytes(*bytes);
        let u = NonZeroU64::new(u).ok_or(ArmourError::NonZeroError)?;
        Ok(u)
    }
    #[inline]
    fn decode_owned(bytes: Self::B) -> Result<Self> {
        let u = u64::from_be_bytes(bytes);
        let u = NonZeroU64::new(u).ok_or(ArmourError::NonZeroError)?;
        Ok(u)
    }

    #[inline]
    fn group_id(&self) -> u32 {
        let id = self.get();
        g8bits(id, Self::GROUP_BITS)
    }
}

impl Cid for u64 {
    type B = [u8; 8];
    const TY: KeyScheme = KeyScheme::Typed(&[KeyType::U64]);
    const GROUP_BITS: u32 = SEQ64_BITS;

    #[inline]
    fn encode(&self) -> Self::B {
        self.to_be_bytes()
    }
    #[inline]
    fn encode_owned(self) -> Self::B {
        self.to_be_bytes()
    }

    #[inline]
    fn decode(bytes: &Self::B) -> Result<Self> {
        let u = u64::from_be_bytes(*bytes);
        Ok(u)
    }
    #[inline]
    fn decode_owned(bytes: Self::B) -> Result<Self> {
        let u = u64::from_be_bytes(bytes);
        Ok(u)
    }

    #[inline]
    fn group_id(&self) -> u32 {
        g8bits(*self, Self::GROUP_BITS)
    }
}

impl Cid for u64_be {
    type B = [u8; 8];
    const TY: KeyScheme = KeyScheme::Typed(&[KeyType::U64]);
    const GROUP_BITS: u32 = SEQ64_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; 8] = bytes
            .get(..8)
            .ok_or(ArmourError::BytesLenError)?
            .try_into()?;

        Self::decode(bytes)
    }

    #[inline]
    fn decode(bytes: &Self::B) -> Result<Self> {
        Ok(zerocopy::transmute!(*bytes))
    }

    #[inline]
    fn decode_owned(bytes: Self::B) -> Result<Self> {
        Ok(zerocopy::transmute!(bytes))
    }

    #[inline]
    fn group_id(&self) -> u32 {
        let id = self.to_native();
        g8bits(id, Self::GROUP_BITS)
    }
}

const STR_GROUP_BITS: u32 = 8;

impl Cid for String {
    type B = Vec<u8>;
    const TY: KeyScheme = KeyScheme::Bytes;
    const GROUP_BITS: u32 = STR_GROUP_BITS;

    #[inline]
    fn encode(&self) -> Self::B {
        self.as_bytes().to_vec()
    }

    #[inline]
    fn encode_owned(self) -> Self::B {
        self.into_bytes()
    }

    #[inline]
    fn decode(bytes: &Self::B) -> Result<Self> {
        let s = String::from_utf8(bytes.to_vec())?;
        Ok(s)
    }

    #[inline]
    fn decode_owned(bytes: Self::B) -> Result<Self> {
        let s = String::from_utf8(bytes)?;
        Ok(s)
    }

    #[inline]
    fn group_id(&self) -> u32 {
        self.as_bytes().first().copied().unwrap_or_default() as u32
    }
}

impl<const CAP: usize> Cid for ArrayString<CAP> {
    type B = [u8; CAP];
    const TY: KeyScheme = KeyScheme::Typed(&[KeyType::Array(CAP)]);
    const GROUP_BITS: u32 = STR_GROUP_BITS;

    #[inline]
    fn encode(&self) -> Self::B {
        let mut result = [0u8; CAP];
        let bytes = self.as_bytes();
        result[..bytes.len()].copy_from_slice(bytes);
        result
    }

    #[inline]
    fn encode_owned(self) -> Self::B {
        let mut result = [0u8; CAP];
        let bytes = self.as_bytes();
        result[..bytes.len()].copy_from_slice(bytes);
        result
    }

    #[inline]
    fn decode(bytes: &Self::B) -> Result<Self> {
        let s = Self::from_byte_string(bytes)?;
        Ok(s)
    }

    #[inline]
    fn decode_owned(bytes: Self::B) -> Result<Self> {
        let s = Self::from_byte_string(&bytes)?;
        Ok(s)
    }

    #[inline]
    fn group_id(&self) -> u32 {
        self.as_bytes().first().copied().unwrap_or_default() as u32
    }
}

// 30 bit timestamp in ms represent 12 days
// другими словами, первые 10 бит - уникальные для группы. Одна группа будет содержать идентификаторы, созданные в окне из 12 дней. В году будет 31 группа.
// +- 1 bit = 12 days * 2
// 30 bit = 1_073_741_824 ms = 12 days
// 40 - 30 = 10 bit = 1024
// groups represent 32 bit numbers, поэтому:
// 32 - 10 = 22, 2^22 = 4_194_304 = 0x40_0000
pub const MILLISECOND_BITS: u32 = 10;

/// implements for V7 Uuid
impl Cid for Uuid {
    type B = [u8; 16];
    const TY: KeyScheme = KeyScheme::Typed(&[KeyType::Array(16)]);
    const GROUP_BITS: u32 = MILLISECOND_BITS;

    #[inline]
    fn encode(&self) -> Self::B {
        self.into_bytes()
    }

    #[inline]
    fn encode_owned(self) -> Self::B {
        self.into_bytes()
    }

    #[inline]
    fn decode(bytes: &Self::B) -> Result<Self> {
        Ok(Uuid::from_bytes(*bytes))
    }

    #[inline]
    fn decode_owned(bytes: Self::B) -> Result<Self> {
        Ok(Uuid::from_bytes(bytes))
    }

    #[inline]
    fn group_id(&self) -> u32 {
        g16bits(self.as_u128(), Self::GROUP_BITS)
    }
}

// # Safety
//
// This is unsafe fn
// #[inline]
// pub(crate) unsafe fn bytes_unsafe<const N: usize>(slice: &[u8]) -> &[u8; N] {
//     unsafe {
//         debug_assert!(N <= slice.len());

//         let bytes: &[u8] = slice.get_unchecked(..N);
//         let arr: &[u8; N] = core::mem::transmute_copy(&bytes);
//         arr
//     }
// }

#[cfg(feature = "solana")]
impl Cid for solana_pubkey::Pubkey {
    type B = [u8; 32];
    const TY: KeyScheme = KeyScheme::Typed(&[KeyType::Array(32)]);
    const GROUP_BITS: u32 = 8;

    #[inline]
    fn encode(&self) -> Self::B {
        self.to_bytes()
    }

    #[inline]
    fn decode(bytes: &Self::B) -> Result<Self> {
        Ok(Self::new_from_array(*bytes))
    }

    #[inline]
    fn group_id(&self) -> u32 {
        self.as_array()[0] as u32
    }
}

#[cfg(feature = "solana")]
impl Cid for solana_signature::Signature {
    type B = [u8; 64];
    const TY: KeyScheme = KeyScheme::Typed(&[KeyType::Array(64)]);
    const GROUP_BITS: u32 = 8;

    #[inline]
    fn encode(&self) -> Self::B {
        <[u8; 64]>::from(*self)
    }

    #[inline]
    fn decode(bytes: &Self::B) -> Result<Self> {
        Ok(Self::from(*bytes))
    }

    #[inline]
    fn group_id(&self) -> u32 {
        self.as_ref()[0] as u32
    }
}
