use serde_json::Value;

use crate::bridge::{self, Handle};
use crate::certificate::PublicKey;
use crate::error::Result;

#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ExternalFormat {
    Unknown = 0,
    OpenSsl = 1,
    Ssh = 2,
    Bsafe = 3,
    RawKey = 4,
    WrappedPkcs8 = 5,
    WrappedOpenSsl = 6,
    WrappedSsh = 7,
    WrappedLsh = 8,
    X509Certificate = 9,
    PemSequence = 10,
    Pkcs7 = 11,
    Pkcs12 = 12,
    NetscapeCertificateSequence = 13,
    SshV2 = 14,
}

#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ExternalItemType {
    Unknown = 0,
    PrivateKey = 1,
    PublicKey = 2,
    SessionKey = 3,
    Certificate = 4,
    Aggregate = 5,
}

#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KeyType {
    Rsa = 0,
    EcSecPrimeRandom = 1,
}

#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SignatureAlgorithm {
    RsaSignatureMessagePkcs1v15Sha256 = 0,
    RsaSignatureDigestPkcs1v15Sha256 = 1,
    RsaSignatureMessagePssSha256 = 2,
    EcdsaSignatureMessageX962Sha256 = 3,
    EcdsaSignatureDigestX962Sha256 = 4,
}

#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EncryptionAlgorithm {
    RsaEncryptionRaw = 0,
    RsaEncryptionPkcs1 = 1,
    RsaEncryptionOaepSha1 = 2,
    RsaEncryptionOaepSha224 = 3,
    RsaEncryptionOaepSha256 = 4,
    RsaEncryptionOaepSha384 = 5,
    RsaEncryptionOaepSha512 = 6,
    RsaEncryptionOaepSha1AesGcm = 7,
    RsaEncryptionOaepSha224AesGcm = 8,
    RsaEncryptionOaepSha256AesGcm = 9,
    RsaEncryptionOaepSha384AesGcm = 10,
    RsaEncryptionOaepSha512AesGcm = 11,
}

pub(crate) fn key_type_id() -> usize {
    unsafe { bridge::security_key_get_type_id() }
}

pub(crate) fn key_block_size(handle: &Handle) -> usize {
    usize::try_from(unsafe { bridge::security_key_get_block_size(handle.as_ptr()) })
        .unwrap_or_default()
}

pub(crate) fn key_external_representation(handle: &Handle) -> Result<Vec<u8>> {
    let mut status = 0;
    let mut error = std::ptr::null_mut();
    let raw = unsafe {
        bridge::security_key_copy_external_representation(handle.as_ptr(), &mut status, &mut error)
    };
    bridge::required_data(
        "security_key_copy_external_representation",
        raw,
        status,
        error,
    )
}

pub(crate) fn encrypt_with_public_key(
    handle: &Handle,
    algorithm: EncryptionAlgorithm,
    plaintext: &[u8],
) -> Result<Vec<u8>> {
    let mut status = 0;
    let mut error = std::ptr::null_mut();
    let raw = unsafe {
        bridge::security_public_key_create_encrypted_data(
            handle.as_ptr(),
            algorithm as u32,
            plaintext.as_ptr().cast(),
            bridge::len_to_isize(plaintext.len())?,
            &mut status,
            &mut error,
        )
    };
    bridge::required_data(
        "security_public_key_create_encrypted_data",
        raw,
        status,
        error,
    )
}

pub(crate) fn decrypt_with_private_key(
    handle: &Handle,
    algorithm: EncryptionAlgorithm,
    ciphertext: &[u8],
) -> Result<Vec<u8>> {
    let mut status = 0;
    let mut error = std::ptr::null_mut();
    let raw = unsafe {
        bridge::security_private_key_create_decrypted_data(
            handle.as_ptr(),
            algorithm as u32,
            ciphertext.as_ptr().cast(),
            bridge::len_to_isize(ciphertext.len())?,
            &mut status,
            &mut error,
        )
    };
    bridge::required_data(
        "security_private_key_create_decrypted_data",
        raw,
        status,
        error,
    )
}

#[derive(Debug)]
pub struct PrivateKey {
    handle: Handle,
}

impl PrivateKey {
    pub fn type_id() -> usize {
        key_type_id()
    }

    pub(crate) fn from_handle(handle: Handle) -> Self {
        Self { handle }
    }

    pub(crate) fn handle(&self) -> &Handle {
        &self.handle
    }

    pub fn from_data(data: &[u8], key_type: KeyType, key_size_bits: usize) -> Result<Self> {
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_private_key_create_with_data(
                data.as_ptr().cast(),
                bridge::len_to_isize(data.len())?,
                key_type as u32,
                bridge::len_to_isize(key_size_bits)?,
                &mut status,
                &mut error,
            )
        };
        bridge::required_handle("security_private_key_create_with_data", raw, status, error)
            .map(Self::from_handle)
    }

    pub fn import_item(
        data: &[u8],
        file_name_or_extension: Option<&str>,
        format: ExternalFormat,
        item_type: ExternalItemType,
    ) -> Result<Self> {
        let file_name_or_extension = file_name_or_extension.map(bridge::cstring).transpose()?;
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_private_key_import_item(
                data.as_ptr().cast(),
                bridge::len_to_isize(data.len())?,
                file_name_or_extension
                    .as_ref()
                    .map_or(std::ptr::null(), |value| value.as_ptr()),
                format as u32,
                item_type as u32,
                &mut status,
                &mut error,
            )
        };
        bridge::required_handle("security_private_key_import_item", raw, status, error)
            .map(Self::from_handle)
    }

    pub fn import_pem(pem: &[u8]) -> Result<Self> {
        Self::import_item(
            pem,
            Some(".pem"),
            ExternalFormat::Unknown,
            ExternalItemType::PrivateKey,
        )
    }

    pub fn public_key(&self) -> Result<PublicKey> {
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_key_copy_public_key(self.handle.as_ptr(), &mut status, &mut error)
        };
        bridge::required_handle("security_key_copy_public_key", raw, status, error)
            .map(PublicKey::from_handle)
    }

    pub fn attributes(&self) -> Result<Value> {
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_key_copy_attributes(self.handle.as_ptr(), &mut status, &mut error)
        };
        bridge::required_json("security_key_copy_attributes", raw, status, error)
    }

    pub fn block_size(&self) -> usize {
        key_block_size(&self.handle)
    }

    pub fn external_representation(&self) -> Result<Vec<u8>> {
        key_external_representation(&self.handle)
    }

    pub fn sign(&self, algorithm: SignatureAlgorithm, data: &[u8]) -> Result<Vec<u8>> {
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_private_key_create_signature(
                self.handle.as_ptr(),
                algorithm as u32,
                data.as_ptr().cast(),
                bridge::len_to_isize(data.len())?,
                &mut status,
                &mut error,
            )
        };
        bridge::required_data("security_private_key_create_signature", raw, status, error)
    }

    pub fn decrypt(&self, algorithm: EncryptionAlgorithm, ciphertext: &[u8]) -> Result<Vec<u8>> {
        decrypt_with_private_key(&self.handle, algorithm, ciphertext)
    }
}
