use crate::error::DatError;
use crate::signature_algorithm::SignatureAlgorithm;
use ecdsa::signature::Verifier;
use strum_macros::{Display, EnumString};

#[repr(u8)]
#[derive(Debug, Display, EnumString, Clone, Copy, Eq, PartialEq)]
pub enum SignatureKeyOutOption {
    FULL,
    SIGNING,
    VERIFYING,
}

pub enum SignatureKey {
    P256(Option<p256::ecdsa::SigningKey>, p256::ecdsa::VerifyingKey),
    P384(Option<p384::ecdsa::SigningKey>, p384::ecdsa::VerifyingKey),
    P521(Option<p521::ecdsa::SigningKey>, p521::ecdsa::VerifyingKey),
}

impl SignatureKey {
    pub fn generate(algorithm: SignatureAlgorithm) -> Self {
        match algorithm {
            SignatureAlgorithm::P256 => {
                let signing_key = <p256::ecdsa::SigningKey as p256::elliptic_curve::Generate>::generate();
                let verifying_key = *signing_key.verifying_key();
                SignatureKey::P256(Some(signing_key), verifying_key)
            },
            SignatureAlgorithm::P384 => {
                let signing_key = <p384::ecdsa::SigningKey as p384::elliptic_curve::Generate>::generate();
                let verifying_key = *signing_key.verifying_key();
                SignatureKey::P384(Some(signing_key), verifying_key)
            },
            SignatureAlgorithm::P521 => {
                let signing_key = <p521::ecdsa::SigningKey as p521::elliptic_curve::Generate>::generate();
                let verifying_key = *signing_key.verifying_key();
                SignatureKey::P521(Some(signing_key), verifying_key)
            },
        }
    }

    pub fn from_bytes(algorithm: SignatureAlgorithm, signing_key: &[u8], verifying_key: &[u8]) -> Result<SignatureKey, DatError> {
        match algorithm {
            SignatureAlgorithm::P256 => {

                let signing_key = if signing_key.is_empty() {
                    None
                } else {
                    Some(p256::ecdsa::SigningKey::from_slice(signing_key).map_err(|_| DatError::ParseDatSigningKeyError)?)
                };

                let verifying_key = if verifying_key.is_empty() {
                    *signing_key.as_ref().ok_or_else(|| DatError::ParseDatVerifyingKeyError)?.verifying_key()
                } else {
                    p256::ecdsa::VerifyingKey::from_sec1_bytes(verifying_key).map_err(|_| DatError::ParseDatVerifyingKeyError)?
                };

                Ok(SignatureKey::P256(signing_key, verifying_key))
            },
            SignatureAlgorithm::P384 => {

                let signing_key = if signing_key.is_empty() {
                    None
                } else {
                    Some(p384::ecdsa::SigningKey::from_slice(signing_key).map_err(|_| DatError::ParseDatSigningKeyError)?)
                };

                let verifying_key = if verifying_key.is_empty() {
                    *signing_key.as_ref().ok_or_else(|| DatError::ParseDatVerifyingKeyError)?.verifying_key()
                } else {
                    p384::ecdsa::VerifyingKey::from_sec1_bytes(verifying_key).map_err(|_| DatError::ParseDatVerifyingKeyError)?
                };

                Ok(SignatureKey::P384(signing_key, verifying_key))
            },
            SignatureAlgorithm::P521 => {

                let signing_key = if signing_key.is_empty() {
                    None
                } else {
                    Some(p521::ecdsa::SigningKey::from_slice(signing_key).map_err(|_| DatError::ParseDatSigningKeyError)?)
                };

                let verifying_key = if verifying_key.is_empty() {
                    *signing_key.as_ref().ok_or_else(|| DatError::ParseDatVerifyingKeyError)?.verifying_key()
                } else {
                    p521::ecdsa::VerifyingKey::from_sec1_bytes(verifying_key).map_err(|_| DatError::ParseDatVerifyingKeyError)?
                };

                Ok(SignatureKey::P521(signing_key, verifying_key))
            },
        }
    }

    pub fn algorithm(&self) -> SignatureAlgorithm {
        match self {
            SignatureKey::P256(_, _) => SignatureAlgorithm::P256,
            SignatureKey::P384(_, _) => SignatureAlgorithm::P384,
            SignatureKey::P521(_, _) => SignatureAlgorithm::P521,
        }
    }

    pub fn signature_size(&self) -> usize {
        match self {
            SignatureKey::P256(_, _) => 64,
            SignatureKey::P384(_, _) => 96,
            SignatureKey::P521(_, _) => 132,
        }
    }

    pub fn to_bytes(&self) -> (Box<[u8]>, Box<[u8]>) {
        match self {
            SignatureKey::P256(s, v) => {
                (s.as_ref().map(|e| Box::from(e.to_bytes().as_slice())).unwrap_or(Box::from([])), v.to_sec1_bytes())
            },
            SignatureKey::P384(s, v) => {
                (s.as_ref().map(|e| Box::from(e.to_bytes().as_slice())).unwrap_or(Box::from([])), v.to_sec1_bytes())
            },
            SignatureKey::P521(s, v) => {
                (s.as_ref().map(|e| Box::from(e.to_bytes().as_slice())).unwrap_or(Box::from([])), v.to_sec1_bytes())
            },
        }
    }

    pub fn sign(&self, data: &[u8]) -> Result<Box<[u8]>, DatError> {
        match self {
            SignatureKey::P256(s, _) => {
                Ok(Box::from(&*ecdsa::signature::Signer::<p256::ecdsa::Signature>::sign(s.as_ref().ok_or_else(|| DatError::VerifyOnlyKey)?, data).to_bytes()))
            },
            SignatureKey::P384(s, _) => {
                Ok(Box::from(&*ecdsa::signature::Signer::<p384::ecdsa::Signature>::sign(s.as_ref().ok_or_else(|| DatError::VerifyOnlyKey)?, data).to_bytes()))
            },
            SignatureKey::P521(s, _) => {
                Ok(Box::from(&*ecdsa::signature::Signer::<p521::ecdsa::Signature>::sign(s.as_ref().ok_or_else(|| DatError::VerifyOnlyKey)?, data).to_bytes()))
            },
        }
    }

    pub fn verify(&self, body: &[u8], sign: &[u8]) -> Result<(), DatError> {
        match self {
            SignatureKey::P256(_, v) => v.verify(body, &ecdsa::Signature::from_slice(sign).map_err(|_| DatError::InvalidDat)?),
            SignatureKey::P384(_, v) => v.verify(body, &ecdsa::Signature::from_slice(sign).map_err(|_| DatError::InvalidDat)?),
            SignatureKey::P521(_, v) => v.verify(body, &ecdsa::Signature::from_slice(sign).map_err(|_| DatError::InvalidDat)?),
        }.map_err(|_| DatError::InvalidDat)
    }

    pub fn has_signing_key(&self) -> bool {
        match self {
            SignatureKey::P256(s, _) => s.is_some(),
            SignatureKey::P384(s, _) => s.is_some(),
            SignatureKey::P521(s, _) => s.is_some(),
        }
    }
}

impl Clone for SignatureKey {
    fn clone(&self) -> Self {
        let (k, v) = self.to_bytes();
        SignatureKey::from_bytes(self.algorithm(), &*k, &*v).unwrap()
    }
}
