use crate::impl_from_uint_for;
use num_bigint::BigUint;
use num_traits::ToPrimitive;
use serde::{Deserialize, Serialize};
use std::fmt::Display;

/// Signature schema
///
/// Note: u8 is enough for the signature schema
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, Ord, PartialOrd, Copy)]
pub enum SignatureSchema {
    BtcEcdsa = 1000,
    EvmEcdsa = 1001,
    TronEcdsa = 1002,

    TonEddsaOpenMask = 2000,
    SolanaEddsa = 2010,
    IcpEddsa = 2020,
}

impl SignatureSchema {
    /// Get the value of the signature schema
    pub fn value(&self) -> u16 {
        match self {
            SignatureSchema::BtcEcdsa => 1000,
            SignatureSchema::EvmEcdsa => 1001,
            SignatureSchema::TronEcdsa => 1002,
            SignatureSchema::TonEddsaOpenMask => 2000,
            SignatureSchema::SolanaEddsa => 2010,
            SignatureSchema::IcpEddsa => 2020,
        }
    }
}

impl Display for SignatureSchema {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SignatureSchema::BtcEcdsa => write!(f, "BtcEcdsa"),
            SignatureSchema::EvmEcdsa => write!(f, "EvmEcdsa"),
            SignatureSchema::TronEcdsa => write!(f, "TronEcdsa"),
            SignatureSchema::TonEddsaOpenMask => write!(f, "TonEddsaOpenMask"),
            SignatureSchema::SolanaEddsa => write!(f, "SolanaEddsa"),
            SignatureSchema::IcpEddsa => write!(f, "IcpEddsa"),
        }
    }
}

impl From<BigUint> for SignatureSchema {
    fn from(value: BigUint) -> Self {
        match value.to_u16().unwrap() {
            1000 => SignatureSchema::BtcEcdsa,
            1001 => SignatureSchema::EvmEcdsa,
            1002 => SignatureSchema::TronEcdsa,
            2000 => SignatureSchema::TonEddsaOpenMask,
            2010 => SignatureSchema::SolanaEddsa,
            2020 => SignatureSchema::IcpEddsa,
            _ => panic!("Invalid value for SignatureSchema"),
        }
    }
}

impl_from_uint_for!(SignatureSchema, u16, u32, u64, u128);

#[cfg(test)]
mod tests {
    use super::*;
    use ex3_serde::{bincode, cbor};
    #[test]
    fn test_signature_schema_serde() {
        let schema = SignatureSchema::BtcEcdsa;

        let bytes = bincode::serialize(&schema).unwrap();
        let schema2: SignatureSchema = bincode::deserialize(&bytes).unwrap();

        assert_eq!(schema, schema2);

        let schema = SignatureSchema::BtcEcdsa;
        let bytes = cbor::serialize(&schema).unwrap();
        let schema2: SignatureSchema = cbor::deserialize(&bytes).unwrap();

        assert_eq!(schema, schema2);
    }

    #[test]
    fn test_signature_schema_hex() {
        assert_eq!(format!("{:x}", SignatureSchema::BtcEcdsa.value()), "3e8");
        assert_eq!(format!("{:x}", SignatureSchema::EvmEcdsa.value()), "3e9");
    }
}
