use serde_json::Value;

use crate::bridge;
use crate::certificate::Certificate;
use crate::error::Result;
use crate::key::PrivateKey;

#[derive(Debug)]
/// Wraps `SecIdentityRef`.
pub struct Identity {
    handle: bridge::Handle,
}

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

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

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn type_id() -> usize {
        unsafe { bridge::security_identity_get_type_id() }
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn import_pkcs12_first(data: &[u8], password: &str) -> Result<Self> {
        let password = bridge::cstring(password)?;
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_identity_import_pkcs12_first(
                data.as_ptr().cast(),
                bridge::len_to_isize(data.len())?,
                password.as_ptr(),
                &mut status,
                &mut error,
            )
        };
        bridge::required_handle("security_identity_import_pkcs12_first", raw, status, error)
            .map(Self::from_handle)
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn from_certificate_and_private_key(
        certificate: &Certificate,
        private_key: &PrivateKey,
    ) -> Result<Self> {
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_identity_create(
                certificate.handle().as_ptr(),
                private_key.handle().as_ptr(),
                &mut status,
                &mut error,
            )
        };
        bridge::required_handle("security_identity_create", raw, status, error)
            .map(Self::from_handle)
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn with_certificate(certificate: &Certificate) -> Result<Self> {
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_identity_create_with_certificate(
                certificate.handle().as_ptr(),
                &mut status,
                &mut error,
            )
        };
        bridge::required_handle(
            "security_identity_create_with_certificate",
            raw,
            status,
            error,
        )
        .map(Self::from_handle)
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn preferred(
        name: &str,
        key_usage: &[&str],
        valid_issuers: &[Vec<u8>],
    ) -> Result<Option<Self>> {
        let name = bridge::cstring(name)?;
        let key_usage = (!key_usage.is_empty())
            .then(|| bridge::json_cstring(&key_usage))
            .transpose()?;
        let valid_issuers = (!valid_issuers.is_empty())
            .then(|| bridge::json_cstring(&valid_issuers))
            .transpose()?;
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_identity_copy_preferred(
                name.as_ptr(),
                key_usage
                    .as_ref()
                    .map_or(std::ptr::null(), |value| value.as_ptr()),
                valid_issuers
                    .as_ref()
                    .map_or(std::ptr::null(), |value| value.as_ptr()),
                &mut status,
                &mut error,
            )
        };
        if raw.is_null() && status == 0 {
            Ok(None)
        } else {
            bridge::required_handle("security_identity_copy_preferred", raw, status, error)
                .map(Self::from_handle)
                .map(Some)
        }
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn set_preferred(identity: Option<&Self>, name: &str, key_usage: &[&str]) -> Result<()> {
        let name = bridge::cstring(name)?;
        let key_usage = (!key_usage.is_empty())
            .then(|| bridge::json_cstring(&key_usage))
            .transpose()?;
        let mut error = std::ptr::null_mut();
        let status = unsafe {
            bridge::security_identity_set_preferred(
                identity.map_or(std::ptr::null_mut(), |value| value.handle.as_ptr()),
                name.as_ptr(),
                key_usage
                    .as_ref()
                    .map_or(std::ptr::null(), |value| value.as_ptr()),
                &mut error,
            )
        };
        bridge::status_result("security_identity_set_preferred", status, error)
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn copy_system_identity(domain: &str) -> Result<Self> {
        let domain = bridge::cstring(domain)?;
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_identity_copy_system_identity(domain.as_ptr(), &mut status, &mut error)
        };
        bridge::required_handle("security_identity_copy_system_identity", raw, status, error)
            .map(Self::from_handle)
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn set_system_identity(domain: &str, identity: Option<&Self>) -> Result<()> {
        let domain = bridge::cstring(domain)?;
        let mut error = std::ptr::null_mut();
        let status = unsafe {
            bridge::security_identity_set_system_identity(
                domain.as_ptr(),
                identity.map_or(std::ptr::null_mut(), |value| value.handle.as_ptr()),
                &mut error,
            )
        };
        bridge::status_result("security_identity_set_system_identity", status, error)
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn actual_domain(&self) -> Result<Option<String>> {
        let raw = unsafe { bridge::security_identity_copy_actual_domain(self.handle.as_ptr()) };
        bridge::optional_string(raw)
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn label(&self) -> Result<Option<String>> {
        let raw = unsafe { bridge::security_identity_copy_label(self.handle.as_ptr()) };
        bridge::optional_string(raw)
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn chain_count(&self) -> usize {
        usize::try_from(unsafe { bridge::security_identity_get_chain_count(self.handle.as_ptr()) })
            .unwrap_or_default()
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn certificate(&self) -> Result<Certificate> {
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_identity_copy_certificate(
                self.handle.as_ptr(),
                &mut status,
                &mut error,
            )
        };
        bridge::required_handle("security_identity_copy_certificate", raw, status, error)
            .map(Certificate::from_handle)
    }

    /// Wraps the corresponding `SecIdentityRef` operation.
    pub fn private_key_attributes(&self) -> Result<Value> {
        let mut status = 0;
        let mut error = std::ptr::null_mut();
        let raw = unsafe {
            bridge::security_identity_copy_private_key_attributes(
                self.handle.as_ptr(),
                &mut status,
                &mut error,
            )
        };
        bridge::required_json(
            "security_identity_copy_private_key_attributes",
            raw,
            status,
            error,
        )
    }
}
