use std::ops::Range;

use primitive_enum::UnsafeFromU8;
use serde::{ser::SerializeStruct, Serialize, Serializer};

use super::{
    cid::Cid,
    ident::{IdHasher, ID},
    ArmourError,
};
use crate::{
    bytes_unsafe,
    collections::Result,
    dyn_types::key_type::{GetKeyType, KeyBytes, KeyType},
};

pub const DELETED: u8 = 255;
pub const UID_SIZE: usize = 5;

pub trait UidStatus
where
    Self: Default + Sized + Copy + Eq + Ord + UnsafeFromU8 + PartialEq<u8>,
{
    fn as_byte(self) -> u8;
}

/// (deleted-disabled, sequence_id)
/// deleted - навсегда, disabled - можно потом восстановить.
/// сохраняем в одном байте информацию о удаленном или отключенном id
/// так так сущность может быть либо удаленной, либо отключенной.
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)]
#[repr(C)]
pub struct Uid<T: UidStatus, I: IdHasher> {
    pub status: T,
    id: ID<I>,
}

impl<T: UidStatus, I: IdHasher> Uid<T, I> {
    #[inline]
    pub fn get_u32(self) -> u32 {
        self.id.get()
    }

    pub fn new(id: ID<I>) -> Uid<T, I> {
        Uid {
            status: Default::default(),
            id,
        }
    }

    pub fn create_with_status(id: ID<I>, status: T) -> Uid<T, I> {
        Uid { status, id }
    }

    #[inline]
    pub fn get_bytes(self) -> [u8; UID_SIZE] {
        let id_vec: [u8; 4] = self.id.to_le_bytes();
        let status = self.status.as_byte();
        [status, id_vec[3], id_vec[2], id_vec[1], id_vec[0]]
    }

    #[inline]
    pub fn from_bytes(bytes: [u8; UID_SIZE]) -> Self {
        let first_byte = bytes[0];
        let status: T = UnsafeFromU8::from_unsafe(first_byte);
        let id_bytes: [u8; 4] = [bytes[4], bytes[3], bytes[2], bytes[1]];

        Uid {
            status,
            id: ID::new_unsafe(u32::from_le_bytes(id_bytes)),
        }
    }

    #[inline]
    pub fn deleted_prefix() -> [u8; 1] {
        [DELETED]
    }

    /// ( [0, 0, 0, 0, 0] .. [255, 0, 0, 0, 0] )
    #[inline]
    pub fn not_deleted_range() -> Range<[u8; UID_SIZE]> {
        let from: [u8; UID_SIZE] = [0, 0, 0, 0, 0];
        let to: [u8; UID_SIZE] = [DELETED, 0, 0, 0, 0];
        from..to
    }

    #[inline]
    pub fn get_status(self) -> T {
        self.status
    }

    pub fn set_status(&mut self, status: T) {
        self.status = status;
    }

    #[inline]
    pub fn is_deleted(self) -> bool {
        self.status.as_byte() == DELETED
    }

    #[inline]
    pub fn copy_with_status(mut self, status: T) -> Self {
        self.status = status;
        self
    }

    // #[inline]
    // pub fn copy_with_deleted(&self) -> Self {
    //     let mut new = *self;
    //     new.status = T::from(DELETED);
    //     new
    // }
}

// impl<T: UidStatus, I: IdHasher> Default for Uid<T, I> {
//     fn default() -> Uid<T, I> {
//         Uid {
//             status: T::default(),
//             id: 0.into(),
//         }
//     }
// }

// impl<T: UidStatus, I: IdHasher> From<u32> for Uid<T, I> {
//     fn from(val: u32) -> Uid<T, I> {
//         Uid::new(val)
//     }
// }

// impl<T: UidStatus, I: IdHasher> From<u64> for Uid<T, I> {
//     fn from(val: u64) -> Uid<T, I> {
//         Uid::new(val as u32)
//     }
// }

impl<T: UidStatus, I: IdHasher> From<Uid<T, I>> for u32 {
    fn from(val: Uid<T, I>) -> u32 {
        val.get_u32()
    }
}

impl<T: UidStatus + Serialize, I: IdHasher> Serialize for Uid<T, I> {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let id = self.id.ser();
        let mut s = serializer.serialize_struct("Id", 2)?;
        s.serialize_field("status", &self.status)?;
        s.serialize_field("id", &id)?;
        s.end()
    }
}

impl<T: UidStatus, I: IdHasher> Cid for Uid<T, I> {
    type B = [u8; UID_SIZE];

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

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

    fn from_bytes_unsafe(bytes: &[u8]) -> Self {
        let bytes: [u8; UID_SIZE] = *bytes_unsafe(bytes);
        Uid::from_bytes(bytes)
    }
}

impl<T: UidStatus, I: IdHasher> GetKeyType for Uid<T, I> {
    /// 5
    const SIZE: u8 = UID_SIZE as u8;

    #[inline]
    fn typ() -> &'static [KeyType] {
        &[KeyType::U8, KeyType::U32]
    }

    fn as_bytes(&self) -> KeyBytes {
        let mut key = KeyBytes::new_const::<{ UID_SIZE as u8 }>();

        let first_byte = self.status.as_byte();
        let bytes: [u8; 4] = self.id.to_be_bytes();

        unsafe {
            *key.get_unchecked_mut(0) = first_byte;
            key.get_unchecked_mut(1..5).copy_from_slice(&bytes)
        }

        // key.copy_from_slice(&self.get_bytes());
        key
    }
}

// #[Scalar]
// impl<T: UidStatus + Send + Sync> ScalarType for Uid<T> {
//     fn parse(_value: async_graphql::Value) -> InputValueResult<Self> {
//         tod!()
//     }
//
//     fn to_value(&self) -> async_graphql::Value {
//         tod!()
//     }
// }
