use data_encoding::HEXLOWER;
use serde::{Deserialize, Serialize};

use super::ArmourError;
use crate::types::Result;

// smallvec 24?
pub type KeyBytes = Vec<u8>;

#[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize, Debug)]
#[repr(u8)]
#[serde(tag = "type", content = "data")]
pub enum KeyType {
    U8,
    U32,
    U64,
    Fuid,
    Array(usize),
}

#[derive(Eq, PartialEq, Clone, Copy, Serialize, Debug)]
#[serde(tag = "type", content = "data")]
pub enum KeyScheme {
    Typed(&'static [KeyType]),
    // Array(usize),
    Bytes,
    // Composed(&'static [KeyScheme]),
}

impl KeyScheme {
    pub const fn is_dyn_sized(&self) -> bool {
        matches!(self, KeyScheme::Bytes)
    }

    /// - for Typed: string of values with '-' separated, e.: 1-5467-23
    /// - for Array or Bytes: hex string
    pub fn from_str(self, s: &str) -> Result<KeyBytes> {
        match self {
            KeyScheme::Typed(typ) => {
                let mut splitted = s.split('-').filter(|s| !s.is_empty());
                let size: usize = key_size(self);

                let mut key = vec![0; size];

                let mut cursor = 0;

                for t in typ {
                    let s = splitted.next().ok_or(ArmourError::FromBytesError)?;

                    match t {
                        KeyType::U8 => {
                            let u = s.parse::<u8>()?;
                            let b = key.get_mut(cursor).ok_or(ArmourError::BytesLenError)?;
                            *b = u;
                            cursor += 1;
                        }
                        KeyType::U32 => {
                            let u = s.parse::<u32>()?;
                            let slice = key
                                .get_mut(cursor..cursor + 4)
                                .ok_or(ArmourError::BytesLenError)?;
                            slice.copy_from_slice(&u.to_be_bytes());

                            cursor += 4;
                        }
                        KeyType::U64 => {
                            let u = s.parse::<u64>()?;
                            let slice = key
                                .get_mut(cursor..cursor + 8)
                                .ok_or(ArmourError::BytesLenError)?;
                            slice.copy_from_slice(&u.to_be_bytes());

                            cursor += 8;
                        }
                        KeyType::Fuid => {
                            let len = HEXLOWER
                                .decode_len(s.len())
                                .map_err(|_| ArmourError::HexDecodeError)?;

                            if len != 8 {
                                return Err(ArmourError::FromBytesError);
                            }

                            let slice = key
                                .get_mut(cursor..cursor + 8)
                                .ok_or(ArmourError::BytesLenError)?;

                            HEXLOWER
                                .decode_mut(s.as_bytes(), slice)
                                .map_err(|_| ArmourError::HexDecodeError)?;
                        }
                        KeyType::Array(size) => {
                            let len = HEXLOWER
                                .decode_len(s.len())
                                .map_err(|_| ArmourError::HexDecodeError)?;

                            if len != *size {
                                return Err(ArmourError::FromBytesError);
                            }

                            let slice = key
                                .get_mut(cursor..cursor + size)
                                .ok_or(ArmourError::BytesLenError)?;

                            HEXLOWER
                                .decode_mut(s.as_bytes(), slice)
                                .map_err(|_| ArmourError::HexDecodeError)?;
                        }
                    }
                }
                Ok(key)
            }
            KeyScheme::Bytes => {
                let len = HEXLOWER
                    .decode_len(s.len())
                    .map_err(|_| ArmourError::HexDecodeError)?;

                let mut key: KeyBytes = vec![0; len];

                HEXLOWER
                    .decode_mut(s.as_bytes(), &mut key)
                    .map_err(|_| ArmourError::HexDecodeError)?;

                Ok(key)
            }
        }
    }
}

/// cannot be 0 (mean KeyScheme::Bytes)
pub const fn key_size(ty: KeyScheme) -> usize {
    match ty {
        KeyScheme::Typed(types) => {
            let mut size = 0usize;
            let mut i = 0usize;

            while i < types.len() {
                let ty = types[i];

                match ty {
                    KeyType::U8 => size += 1,
                    KeyType::U32 => size += 4,
                    KeyType::U64 => size += 8,
                    KeyType::Fuid => size += 8,
                    KeyType::Array(s) => size += s,
                }
                i += 1;
            }
            size
        }
        KeyScheme::Bytes => panic!("KeyScheme::Bytes size unknown"),
    }
}

/// can be 0 (mean KeyScheme::Bytes)
pub const fn key_size2(ty: KeyScheme) -> usize {
    match ty {
        KeyScheme::Bytes => 0,
        _ => key_size(ty),
    }
}
