use crate::error::DatError;
use ecdsa::signature::Verifier;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SignAlgorithm {
    P256,
    P384,
    P521,
}

pub enum SignKey {
    P256(p256::ecdsa::SigningKey),
    P384(p384::ecdsa::SigningKey),
    P521(p521::ecdsa::SigningKey),
}

pub enum VerifyKey {
    P256(p256::ecdsa::VerifyingKey),
    P384(p384::ecdsa::VerifyingKey),
    P521(p521::ecdsa::VerifyingKey),
}

impl SignAlgorithm {
    pub fn to_str(&self) -> &'static str {
        match self {
            SignAlgorithm::P256 => "P256",
            SignAlgorithm::P384 => "P384",
            SignAlgorithm::P521 => "P521",
        }
    }
    pub fn from_str(s: &str) -> Result<SignAlgorithm, DatError> {
        match s {
            "P256" => Ok(SignAlgorithm::P256),
            "P384" => Ok(SignAlgorithm::P384),
            "P521" => Ok(SignAlgorithm::P521),
            _ => Err(DatError::SignError(format!("UnknownSignAlgorithm {}", s))),
        }
    }
}

impl SignKey {
    pub fn generate(alg: SignAlgorithm) -> Self {
        match alg {
            SignAlgorithm::P256 => SignKey::P256(<p256::ecdsa::SigningKey as p256::elliptic_curve::Generate>::generate()),
            SignAlgorithm::P384 => SignKey::P384(<p384::ecdsa::SigningKey as p384::elliptic_curve::Generate>::generate()),
            SignAlgorithm::P521 => SignKey::P521(<p521::ecdsa::SigningKey as p521::elliptic_curve::Generate>::generate()),
        }
    }

    pub fn from_bytes(alg: SignAlgorithm, data: &[u8]) -> Result<SignKey, DatError> {
        Ok(match alg {
            SignAlgorithm::P256 => p256::ecdsa::SigningKey::from_slice(data).map(SignKey::P256),
            SignAlgorithm::P384 => p384::ecdsa::SigningKey::from_slice(data).map(SignKey::P384),
            SignAlgorithm::P521 => p521::ecdsa::SigningKey::from_slice(data).map(SignKey::P521),
        }?)
    }

    pub fn to_bytes(&self) -> Box<[u8]> {
        match self {
            SignKey::P256(sk) => Box::from(sk.to_bytes().as_slice()),
            SignKey::P384(sk) => Box::from(sk.to_bytes().as_slice()),
            SignKey::P521(sk) => Box::from(sk.to_bytes().as_slice()),
        }
    }

    pub fn to_verify_key(&self) -> VerifyKey {
        match self {
            SignKey::P256(key) => VerifyKey::P256(*key.verifying_key()),
            SignKey::P384(key) => VerifyKey::P384(*key.verifying_key()),
            SignKey::P521(key) => VerifyKey::P521(*key.verifying_key()),
        }
    }

    pub fn sign(&self, data: &[u8]) -> Box<[u8]> {
        match self {
            SignKey::P256(key) => Box::from(&*p256::ecdsa::signature::Signer::<p256::ecdsa::Signature>::sign(key, data).to_bytes()),
            SignKey::P384(key) => Box::from(&*p384::ecdsa::signature::Signer::<p384::ecdsa::Signature>::sign(key, data).to_bytes()),
            SignKey::P521(key) => Box::from(&*p521::ecdsa::signature::Signer::<p521::ecdsa::Signature>::sign(key, data).to_bytes()),
        }
    }
}

impl VerifyKey {
    pub fn to_alg(&self) -> SignAlgorithm {
        match self {
            VerifyKey::P256(_) => SignAlgorithm::P256,
            VerifyKey::P384(_) => SignAlgorithm::P384,
            VerifyKey::P521(_) => SignAlgorithm::P521,
        }
    }

    pub fn verify(&self, body: &[u8], signature: &[u8]) -> Result<(), DatError> {
        Ok(match self {
            VerifyKey::P256(key) => key.verify(body, &ecdsa::Signature::from_slice(signature)?),
            VerifyKey::P384(key) => key.verify(body, &ecdsa::Signature::from_slice(signature)?),
            VerifyKey::P521(key) => key.verify(body, &ecdsa::Signature::from_slice(signature)?),
        }?)
    }
}
