use crate::chain_crypto::bech32::Bech32;
pub use cml_core::{
    error::{DeserializeError, DeserializeFailure},
    serialization::{Deserialize, Serialize, StringEncoding},
};
use cryptoxide::blake2b::Blake2b;
pub use derivative::Derivative;
use impl_mockchain::key;
use rand::rngs::OsRng;
use std::convert::From;
pub mod emip3;
pub mod chain_core;
pub mod chain_crypto;
pub mod impl_mockchain;
pub mod typed_bytes;
#[macro_use]
extern crate cfg_if;
pub trait RawBytesEncoding {
    fn to_raw_bytes(&self) -> &[u8];
    fn from_raw_bytes(bytes: &[u8]) -> Result<Self, CryptoError>
    where
        Self: Sized;
    fn to_raw_hex(&self) -> String {
        hex::encode(self.to_raw_bytes())
    }
    fn from_raw_hex(hex_str: &str) -> Result<Self, CryptoError>
    where
        Self: Sized,
    {
        let bytes = hex::decode(hex_str)?;
        Self::from_raw_bytes(bytes.as_ref())
    }
}
#[derive(Debug, thiserror::Error)]
pub enum CryptoError {
    #[error("Bech32: {0}")]
    Bech32(#[from] chain_crypto::bech32::Error),
    #[error("ByronError: {0}")]
    Hex(#[from] hex::FromHexError),
    #[error("Deserialization: {0}")]
    Deserialization(#[from] DeserializeError),
    #[error("SecretKeyError: {0}")]
    SecretKey(#[from] chain_crypto::SecretKeyError),
    #[error("PublicKeyError: {0}")]
    PublicKey(#[from] chain_crypto::PublicKeyError),
    #[error("SignatureFromStr: {0}")]
    SignatureFromStr(#[from] chain_crypto::SignatureFromStrError),
    #[error("BootStrapCombine: {0}")]
    BootstrapCombine(#[from] ed25519_bip32::PublicKeyError),
    #[error("SignatureError: {0}")]
    SignatureError(#[from] chain_crypto::SignatureError),
}
impl From<bech32::Error> for CryptoError {
    fn from(e: bech32::Error) -> Self {
        chain_crypto::bech32::Error::Bech32Malformed(e).into()
    }
}
pub fn blake2b224(data: &[u8]) -> [u8; 28] {
    let mut out = [0; 28];
    Blake2b::blake2b(&mut out, data, &[]);
    out
}
pub fn blake2b256(data: &[u8]) -> [u8; 32] {
    let mut out = [0; 32];
    Blake2b::blake2b(&mut out, data, &[]);
    out
}
pub struct Bip32PrivateKey(chain_crypto::SecretKey<chain_crypto::Ed25519Bip32>);
impl Bip32PrivateKey {
    pub fn derive(&self, index: u32) -> Bip32PrivateKey {
        Bip32PrivateKey(chain_crypto::derive::derive_sk_ed25519(&self.0, index))
    }
    pub fn from_128_xprv(bytes: &[u8]) -> Result<Bip32PrivateKey, CryptoError> {
        let mut buf = [0; 96];
        buf[0..64].clone_from_slice(&bytes[0..64]);
        buf[64..96].clone_from_slice(&bytes[96..128]);
        Bip32PrivateKey::from_raw_bytes(&buf)
    }
    pub fn to_128_xprv(&self) -> Vec<u8> {
        let raw_key = self.to_raw_key();
        let prv_key = raw_key.to_raw_bytes();
        let raw_pub_key = self.to_public().to_raw_key();
        let pub_key = raw_pub_key.to_raw_bytes();
        let cc = self.chaincode();
        let mut buf = [0; 128];
        buf[0..64].clone_from_slice(prv_key);
        buf[64..96].clone_from_slice(pub_key);
        buf[96..128].clone_from_slice(&cc);
        buf.to_vec()
    }
    pub fn generate_ed25519_bip32() -> Bip32PrivateKey {
        Bip32PrivateKey(chain_crypto::SecretKey::<chain_crypto::Ed25519Bip32>::generate(OsRng))
    }
    pub fn to_raw_key(&self) -> PrivateKey {
        PrivateKey(key::EitherEd25519SecretKey::Extended(
            chain_crypto::derive::to_raw_sk(&self.0),
        ))
    }
    pub fn to_public(&self) -> Bip32PublicKey {
        Bip32PublicKey(self.0.to_public())
    }
    pub fn from_bech32(bech32_str: &str) -> Result<Bip32PrivateKey, CryptoError> {
        chain_crypto::SecretKey::try_from_bech32_str(bech32_str)
            .map(Bip32PrivateKey)
            .map_err(Into::into)
    }
    pub fn to_bech32(&self) -> String {
        self.0.to_bech32_str()
    }
    pub fn from_bip39_entropy(entropy: &[u8], password: &[u8]) -> Bip32PrivateKey {
        Bip32PrivateKey(chain_crypto::derive::from_bip39_entropy(entropy, password))
    }
    pub fn chaincode(&self) -> Vec<u8> {
        const ED25519_PRIVATE_KEY_LENGTH: usize = 64;
        const XPRV_SIZE: usize = 96;
        self.0.as_ref()[ED25519_PRIVATE_KEY_LENGTH..XPRV_SIZE].to_vec()
    }
}
impl RawBytesEncoding for Bip32PrivateKey {
    fn to_raw_bytes(&self) -> &[u8] {
        self.0.as_ref()
    }
    fn from_raw_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
        chain_crypto::SecretKey::<chain_crypto::Ed25519Bip32>::from_binary(bytes)
            .map_err(Into::into)
            .map(Bip32PrivateKey)
    }
}
#[derive(
    Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
)]
pub struct Bip32PublicKey(pub chain_crypto::PublicKey<chain_crypto::Ed25519Bip32>);
impl Bip32PublicKey {
    pub fn derive(&self, index: u32) -> Result<Bip32PublicKey, ed25519_bip32::DerivationError> {
        chain_crypto::derive::derive_pk_ed25519(&self.0, index).map(Bip32PublicKey)
    }
    pub fn to_raw_key(&self) -> PublicKey {
        PublicKey(chain_crypto::derive::to_raw_pk(&self.0))
    }
    pub fn from_bech32(bech32_str: &str) -> Result<Bip32PublicKey, CryptoError> {
        chain_crypto::PublicKey::try_from_bech32_str(bech32_str)
            .map(Bip32PublicKey)
            .map_err(Into::into)
    }
    pub fn to_bech32(&self) -> String {
        self.0.to_bech32_str()
    }
    pub fn chaincode(&self) -> Vec<u8> {
        const ED25519_PUBLIC_KEY_LENGTH: usize = 32;
        const XPUB_SIZE: usize = 64;
        self.0.as_ref()[ED25519_PUBLIC_KEY_LENGTH..XPUB_SIZE].to_vec()
    }
}
impl RawBytesEncoding for Bip32PublicKey {
    fn to_raw_bytes(&self) -> &[u8] {
        self.0.as_ref()
    }
    fn from_raw_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
        chain_crypto::PublicKey::<chain_crypto::Ed25519Bip32>::from_binary(bytes)
            .map_err(Into::into)
            .map(Bip32PublicKey)
    }
}
impl From<chain_crypto::PublicKey<chain_crypto::Ed25519Bip32>> for Bip32PublicKey {
    fn from(key: chain_crypto::PublicKey<chain_crypto::Ed25519Bip32>) -> Self {
        Self(key)
    }
}
pub struct PrivateKey(key::EitherEd25519SecretKey);
impl From<key::EitherEd25519SecretKey> for PrivateKey {
    fn from(secret_key: key::EitherEd25519SecretKey) -> PrivateKey {
        PrivateKey(secret_key)
    }
}
impl PrivateKey {
    pub fn to_public(&self) -> PublicKey {
        self.0.to_public().into()
    }
    pub fn generate_ed25519() -> PrivateKey {
        let keypair = chain_crypto::SecretKey::<chain_crypto::Ed25519>::generate(OsRng);
        PrivateKey(key::EitherEd25519SecretKey::Normal(keypair))
    }
    pub fn generate_ed25519extended() -> PrivateKey {
        let keypair = chain_crypto::SecretKey::<chain_crypto::Ed25519Extended>::generate(OsRng);
        PrivateKey(key::EitherEd25519SecretKey::Extended(keypair))
    }
    pub fn from_bech32(bech32_str: &str) -> Result<PrivateKey, CryptoError> {
        chain_crypto::SecretKey::try_from_bech32_str(bech32_str)
            .map(key::EitherEd25519SecretKey::Extended)
            .or_else(|_| {
                chain_crypto::SecretKey::try_from_bech32_str(bech32_str)
                    .map(key::EitherEd25519SecretKey::Normal)
            })
            .map(PrivateKey)
            .map_err(Into::into)
    }
    pub fn to_bech32(&self) -> String {
        match self.0 {
            key::EitherEd25519SecretKey::Normal(ref secret) => secret.to_bech32_str(),
            key::EitherEd25519SecretKey::Extended(ref secret) => secret.to_bech32_str(),
        }
    }
    pub fn from_extended_bytes(bytes: &[u8]) -> Result<PrivateKey, CryptoError> {
        chain_crypto::SecretKey::from_binary(bytes)
            .map(key::EitherEd25519SecretKey::Extended)
            .map(PrivateKey)
            .map_err(Into::into)
    }
    pub fn from_normal_bytes(bytes: &[u8]) -> Result<PrivateKey, CryptoError> {
        chain_crypto::SecretKey::from_binary(bytes)
            .map(key::EitherEd25519SecretKey::Normal)
            .map(PrivateKey)
            .map_err(Into::into)
    }
    pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
        Ed25519Signature::from(self.0.sign(&message.to_vec()))
    }
}
impl RawBytesEncoding for PrivateKey {
    fn to_raw_bytes(&self) -> &[u8] {
        match self.0 {
            key::EitherEd25519SecretKey::Normal(ref secret) => secret.as_ref(),
            key::EitherEd25519SecretKey::Extended(ref secret) => secret.as_ref(),
        }
    }
    fn from_raw_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
        Self::from_normal_bytes(bytes).or_else(|_| Self::from_extended_bytes(bytes))
    }
}
#[derive(
    Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
)]
pub struct PublicKey(pub chain_crypto::PublicKey<chain_crypto::Ed25519>);
impl From<chain_crypto::PublicKey<chain_crypto::Ed25519>> for PublicKey {
    fn from(key: chain_crypto::PublicKey<chain_crypto::Ed25519>) -> PublicKey {
        PublicKey(key)
    }
}
impl PublicKey {
    pub fn from_bech32(bech32_str: &str) -> Result<PublicKey, CryptoError> {
        chain_crypto::PublicKey::try_from_bech32_str(bech32_str)
            .map(PublicKey)
            .map_err(Into::into)
    }
    pub fn to_bech32(&self) -> String {
        self.0.to_bech32_str()
    }
    pub fn verify(&self, data: &[u8], signature: &Ed25519Signature) -> bool {
        signature.0.verify_slice(&self.0, data) == chain_crypto::Verification::Success
    }
    pub fn hash(&self) -> Ed25519KeyHash {
        Ed25519KeyHash::from(blake2b224(self.to_raw_bytes()))
    }
}
impl RawBytesEncoding for PublicKey {
    fn to_raw_bytes(&self) -> &[u8] {
        self.0.as_ref()
    }
    fn from_raw_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
        chain_crypto::PublicKey::from_binary(bytes)
            .map_err(Into::into)
            .map(PublicKey)
    }
}
macro_rules! impl_signature {
    ($name:ident, $signee_type:ty, $verifier_type:ty) => {
        #[derive(Debug, Clone, Eq, PartialEq)]
        pub struct $name(chain_crypto::Signature<$signee_type, $verifier_type>);
        impl $name {
            pub fn to_bech32(&self) -> String {
                use crate::chain_crypto::bech32::Bech32;
                self.0.to_bech32_str()
            }
            pub fn to_hex(&self) -> String {
                hex::encode(&self.0.as_ref())
            }
            pub fn from_bech32(bech32_str: &str) -> Result<Self, CryptoError> {
                use crate::chain_crypto::bech32::Bech32;
                chain_crypto::Signature::try_from_bech32_str(&bech32_str)
                    .map(Self)
                    .map_err(Into::into)
            }
            pub fn from_hex(input: &str) -> Result<Self, CryptoError> {
                use crate::chain_core::property::FromStr;
                chain_crypto::Signature::from_str(input)
                    .map(Self)
                    .map_err(Into::into)
            }
        }
        impl RawBytesEncoding for $name {
            fn to_raw_bytes(&self) -> &[u8] {
                self.0.as_ref()
            }
            fn from_raw_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
                chain_crypto::Signature::from_binary(bytes.as_ref())
                    .map(Self)
                    .map_err(Into::into)
            }
        }
        impl serde::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: serde::Serializer,
            {
                serializer.serialize_str(&self.to_hex())
            }
        }
        impl<'de> serde::de::Deserialize<'de> for $name {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where
                D: serde::de::Deserializer<'de>,
            {
                let s = <String as serde::de::Deserialize>::deserialize(deserializer)?;
                $name::from_hex(&s).map_err(|_e| {
                    serde::de::Error::invalid_value(
                        serde::de::Unexpected::Str(&s),
                        &"hex bytes for signature",
                    )
                })
            }
        }
        impl schemars::JsonSchema for $name {
            fn schema_name() -> String {
                String::from(stringify!($name))
            }
            fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
                String::json_schema(gen)
            }
            fn is_referenceable() -> bool {
                String::is_referenceable()
            }
        }
        impl Ord for $name {
            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
                self.0.as_ref().cmp(other.0.as_ref())
            }
        }
        impl PartialOrd for $name {
            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
                Some(self.cmp(other))
            }
        }
        #[allow(clippy::derived_hash_with_manual_eq)]
        impl std::hash::Hash for $name {
            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
                self.0.as_ref().hash(state)
            }
        }
        impl From<chain_crypto::Signature<$signee_type, $verifier_type>> for $name {
            fn from(sig: chain_crypto::Signature<$signee_type, $verifier_type>) -> Self {
                Self(sig)
            }
        }
    };
}
impl_signature!(Ed25519Signature, Vec<u8>, chain_crypto::Ed25519);
#[macro_export]
macro_rules! impl_hash_type {
    ($name:ident, $byte_count:expr) => {
        #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
        pub struct $name([u8; $byte_count]);
        impl $name {
            pub const BYTE_COUNT: usize = $byte_count;
            pub fn to_bech32(&self, prefix: &str) -> Result<String, CryptoError> {
                use bech32::ToBase32;
                bech32::encode(&prefix, self.0.as_ref().to_base32()).map_err(Into::into)
            }
            pub fn from_bech32(bech_str: &str) -> Result<$name, CryptoError> {
                let (_hrp, u5data) = bech32::decode(bech_str)
                    .map_err(chain_crypto::bech32::Error::Bech32Malformed)?;
                let data: Vec<u8> = bech32::FromBase32::from_base32(&u5data)
                    .map_err(chain_crypto::bech32::Error::Bech32Malformed)?;
                Self::from_raw_bytes(&data).map_err(Into::into)
            }
            pub fn to_hex(&self) -> String {
                hex::encode(&self.0.as_ref())
            }
            pub fn from_hex(input: &str) -> Result<Self, CryptoError> {
                let hex_bytes = hex::decode(input)?;
                Self::from_raw_bytes(&hex_bytes)
            }
        }
        impl std::fmt::Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                write!(f, "{}", self.to_raw_hex())
            }
        }
        impl From<[u8; $byte_count]> for $name {
            fn from(bytes: [u8; $byte_count]) -> Self {
                Self(bytes)
            }
        }
        impl RawBytesEncoding for $name {
            fn to_raw_bytes(&self) -> &[u8] {
                self.0.as_ref()
            }
            fn from_raw_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
                use std::convert::TryInto;
                match bytes.len() {
                    $byte_count => Ok($name(bytes[..$byte_count].try_into().unwrap())),
                    other_len => {
                        let cbor_error = cbor_event::Error::WrongLen(
                            $byte_count,
                            cbor_event::Len::Len(other_len as u64),
                            "hash length",
                        );
                        Err(DeserializeError::new(
                            stringify!($name),
                            DeserializeFailure::CBOR(cbor_error),
                        )
                        .into())
                    }
                }
            }
        }
        impl serde::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: serde::Serializer,
            {
                serializer.serialize_str(&self.to_hex())
            }
        }
        impl<'de> serde::de::Deserialize<'de> for $name {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where
                D: serde::de::Deserializer<'de>,
            {
                let s = <String as serde::de::Deserialize>::deserialize(deserializer)?;
                $name::from_hex(&s).map_err(|_e| {
                    serde::de::Error::invalid_value(
                        serde::de::Unexpected::Str(&s),
                        &"hex bytes for hash",
                    )
                })
            }
        }
        impl schemars::JsonSchema for $name {
            fn schema_name() -> String {
                String::from(stringify!($name))
            }
            fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
                String::json_schema(gen)
            }
            fn is_referenceable() -> bool {
                String::is_referenceable()
            }
        }
    };
}
impl_hash_type!(Ed25519KeyHash, 28);
impl_hash_type!(ScriptHash, 28);
impl_hash_type!(TransactionHash, 32);
impl_hash_type!(GenesisDelegateHash, 28);
impl_hash_type!(GenesisHash, 28);
impl_hash_type!(AuxiliaryDataHash, 32);
impl_hash_type!(PoolMetadataHash, 32);
impl_hash_type!(VRFKeyHash, 32);
impl_hash_type!(BlockBodyHash, 32);
impl_hash_type!(BlockHeaderHash, 32);
impl_hash_type!(DatumHash, 32);
impl_hash_type!(ScriptDataHash, 32);
impl_hash_type!(VRFVkey, 32);
impl_hash_type!(KESVkey, 32);
impl_hash_type!(NonceHash, 32);
impl_hash_type!(AnchorDocHash, 32);
#[derive(Clone)]
pub struct LegacyDaedalusPrivateKey(chain_crypto::SecretKey<chain_crypto::LegacyDaedalus>);
impl LegacyDaedalusPrivateKey {
    pub fn chaincode(&self) -> Vec<u8> {
        const ED25519_PRIVATE_KEY_LENGTH: usize = 64;
        const XPRV_SIZE: usize = 96;
        self.0.as_ref()[ED25519_PRIVATE_KEY_LENGTH..XPRV_SIZE].to_vec()
    }
}
impl RawBytesEncoding for LegacyDaedalusPrivateKey {
    fn to_raw_bytes(&self) -> &[u8] {
        self.0.as_ref()
    }
    fn from_raw_bytes(bytes: &[u8]) -> Result<LegacyDaedalusPrivateKey, CryptoError> {
        chain_crypto::SecretKey::<chain_crypto::LegacyDaedalus>::from_binary(bytes)
            .map(LegacyDaedalusPrivateKey)
            .map_err(|e| e.into())
    }
}
impl AsRef<chain_crypto::SecretKey<chain_crypto::LegacyDaedalus>> for LegacyDaedalusPrivateKey {
    fn as_ref(&self) -> &chain_crypto::SecretKey<chain_crypto::LegacyDaedalus> {
        &self.0
    }
}