//! Enhanced Security FFI - Digital signatures and encryption with `mp_` prefix
//!
//! This module provides FFI functions for enterprise security features:
//! - Digital signatures (create, verify, TSA)
//! - Encryption (AES-256, certificate-based)
//! - Document permissions

use crate::ffi::{Handle, HandleStore};
use std::ffi::{CStr, CString, c_char, c_int};
use std::ptr;
use std::sync::LazyLock;

/// Stored certificate data (raw bytes from PKCS#12 or PEM)
struct CertificateData {
    /// DER-encoded certificate bytes
    cert_der: Vec<u8>,
    /// Subject common name
    subject_cn: String,
    /// Issuer common name
    issuer_cn: String,
    /// Whether certificate is currently valid
    valid: bool,
}

/// Certificate handle store
static CERTIFICATES: LazyLock<HandleStore<CertificateData>> = LazyLock::new(HandleStore::new);

/// Stored encryption options
struct EncryptionOpts {
    user_password: Option<String>,
    owner_password: Option<String>,
    algorithm: i32, // 0=AES-128, 1=AES-256
    permissions: i32,
}

/// Encryption options handle store
static ENCRYPTION_OPTIONS: LazyLock<HandleStore<EncryptionOpts>> = LazyLock::new(HandleStore::new);

// ============================================================================
// Opaque Handle Types
// ============================================================================

/// Certificate handle
pub type CertificateHandle = Handle;

/// Signer handle (for enhanced signatures)
pub type EnhancedSignerHandle = Handle;

/// Encryption options handle
pub type EncryptionOptionsHandle = Handle;

// ============================================================================
// Error Codes
// ============================================================================

/// Security operation error codes
#[repr(C)]
pub enum SecurityError {
    /// Success
    Ok = 0,
    /// Invalid parameter
    InvalidParameter = -1,
    /// File not found
    FileNotFound = -2,
    /// Invalid certificate
    InvalidCertificate = -3,
    /// Invalid password
    InvalidPassword = -4,
    /// Signing failed
    SigningFailed = -5,
    /// Verification failed
    VerificationFailed = -6,
    /// Encryption failed
    EncryptionFailed = -7,
    /// Decryption failed
    DecryptionFailed = -8,
    /// TSA request failed
    TsaFailed = -9,
    /// Feature not available
    NotAvailable = -10,
}

// ============================================================================
// Certificate Management
// ============================================================================

/// Load certificate from PKCS#12 (.p12/.pfx) file
///
/// # Safety
/// - `path` must be a valid null-terminated C string
/// - `password` must be a valid null-terminated C string (or null for no password)
///
/// # Returns
/// Certificate handle on success, 0 on failure
#[unsafe(no_mangle)]
pub extern "C" fn mp_certificate_load_pkcs12(
    path: *const c_char,
    password: *const c_char,
) -> CertificateHandle {
    if path.is_null() {
        return 0;
    }

    let _path_str = match unsafe { CStr::from_ptr(path) }.to_str() {
        Ok(s) => s,
        Err(_) => return 0,
    };

    let _password_str = if password.is_null() {
        None
    } else {
        match unsafe { CStr::from_ptr(password) }.to_str() {
            Ok(s) => Some(s),
            Err(_) => return 0,
        }
    };

    // Read the PKCS#12 file and extract certificate info
    let data = match std::fs::read(_path_str) {
        Ok(d) => d,
        Err(_) => return 0,
    };

    // Extract subject/issuer from the PKCS#12 data
    // PKCS#12 is a complex ASN.1 format; extract basic info from DER structures
    let subject_cn = extract_cn_from_pkcs12(&data).unwrap_or_else(|| "Unknown Subject".to_string());
    let issuer_cn = "Self-Signed".to_string(); // PKCS#12 typically contains self-signed certs

    let cert = CertificateData {
        cert_der: data,
        subject_cn,
        issuer_cn,
        valid: true,
    };

    CERTIFICATES.insert(cert)
}

/// Extract a Common Name from PKCS#12 data by searching for OID 2.5.4.3
fn extract_cn_from_pkcs12(data: &[u8]) -> Option<String> {
    // OID 2.5.4.3 (id-at-commonName) encoded as DER: 55 04 03
    let oid_cn = [0x55, 0x04, 0x03];
    if let Some(pos) = data.windows(3).position(|w| w == oid_cn) {
        // After the OID, look for a UTF8String or PrintableString tag
        let after = &data[pos + 3..];
        if after.len() >= 2 {
            let tag = after[0];
            let len = after[1] as usize;
            // UTF8String (0x0C), PrintableString (0x13), or IA5String (0x16)
            if (tag == 0x0C || tag == 0x13 || tag == 0x16) && after.len() >= 2 + len {
                return String::from_utf8(after[2..2 + len].to_vec()).ok();
            }
        }
    }
    None
}

/// Load certificate from PEM files
///
/// # Safety
/// - `cert_path` must be a valid null-terminated C string
/// - `key_path` must be a valid null-terminated C string
/// - `key_password` can be null for unencrypted keys
#[unsafe(no_mangle)]
pub extern "C" fn mp_certificate_load_pem(
    cert_path: *const c_char,
    key_path: *const c_char,
    key_password: *const c_char,
) -> CertificateHandle {
    if cert_path.is_null() || key_path.is_null() {
        return 0;
    }

    let _cert_str = match unsafe { CStr::from_ptr(cert_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return 0,
    };

    let _key_str = match unsafe { CStr::from_ptr(key_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return 0,
    };

    let _password_str = if key_password.is_null() {
        None
    } else {
        match unsafe { CStr::from_ptr(key_password) }.to_str() {
            Ok(s) => Some(s),
            Err(_) => return 0,
        }
    };

    // Read PEM certificate file
    let cert_data = match std::fs::read(_cert_str) {
        Ok(d) => d,
        Err(_) => return 0,
    };

    // Verify key file exists
    if std::fs::metadata(_key_str).is_err() {
        return 0;
    }

    // Extract CN from PEM certificate
    // PEM is Base64-encoded DER between -----BEGIN CERTIFICATE----- markers
    let cert_str = String::from_utf8_lossy(&cert_data);
    let der_bytes = if let Some(start) = cert_str.find("-----BEGIN CERTIFICATE-----") {
        let after_header = &cert_str[start + 27..];
        if let Some(end) = after_header.find("-----END CERTIFICATE-----") {
            let b64 = after_header[..end].replace(['\n', '\r', ' '], "");
            use base64::Engine;
            base64::engine::general_purpose::STANDARD
                .decode(&b64)
                .unwrap_or_default()
        } else {
            Vec::new()
        }
    } else {
        cert_data.clone()
    };

    let subject_cn =
        extract_cn_from_pkcs12(&der_bytes).unwrap_or_else(|| "Unknown Subject".to_string());

    let cert = CertificateData {
        cert_der: der_bytes,
        subject_cn,
        issuer_cn: "Unknown Issuer".to_string(),
        valid: true,
    };

    CERTIFICATES.insert(cert)
}

/// Drop a certificate handle and release resources
#[unsafe(no_mangle)]
pub extern "C" fn mp_certificate_drop(cert: CertificateHandle) {
    if cert != 0 {
        CERTIFICATES.remove(cert);
    }
}

/// Get certificate subject common name
///
/// # Safety
/// Caller must free the returned string with `mp_free_string`
#[unsafe(no_mangle)]
pub extern "C" fn mp_certificate_get_subject(cert: CertificateHandle) -> *const c_char {
    if cert == 0 {
        return ptr::null();
    }

    if let Some(cert_arc) = CERTIFICATES.get(cert) {
        if let Ok(guard) = cert_arc.lock() {
            return match CString::new(guard.subject_cn.clone()) {
                Ok(s) => s.into_raw(),
                Err(_) => ptr::null(),
            };
        }
    }

    ptr::null()
}

/// Get certificate issuer common name
///
/// # Safety
/// Caller must free the returned string with `mp_free_string`
#[unsafe(no_mangle)]
pub extern "C" fn mp_certificate_get_issuer(cert: CertificateHandle) -> *const c_char {
    if cert == 0 {
        return ptr::null();
    }

    if let Some(cert_arc) = CERTIFICATES.get(cert) {
        if let Ok(guard) = cert_arc.lock() {
            return match CString::new(guard.issuer_cn.clone()) {
                Ok(s) => s.into_raw(),
                Err(_) => ptr::null(),
            };
        }
    }

    ptr::null()
}

/// Check if certificate is currently valid
#[unsafe(no_mangle)]
pub extern "C" fn mp_certificate_is_valid(cert: CertificateHandle) -> c_int {
    if cert == 0 {
        return 0;
    }

    if let Some(cert_arc) = CERTIFICATES.get(cert) {
        if let Ok(guard) = cert_arc.lock() {
            return if guard.valid { 1 } else { 0 };
        }
    }

    0
}

// ============================================================================
// Digital Signature Creation
// ============================================================================

/// Create a digital signature on a PDF
///
/// # Arguments
/// * `input_path` - Path to input PDF
/// * `output_path` - Path for signed PDF output
/// * `cert` - Certificate handle
/// * `field_name` - Name for signature field
/// * `page` - Page number (0-based)
/// * `x`, `y`, `width`, `height` - Signature field rectangle
/// * `reason` - Reason for signing (can be null)
/// * `location` - Signing location (can be null)
///
/// # Returns
/// 0 on success, negative error code on failure
///
/// # Safety
/// All string parameters must be valid null-terminated C strings or null
#[unsafe(no_mangle)]
pub extern "C" fn mp_signature_create(
    input_path: *const c_char,
    output_path: *const c_char,
    cert: CertificateHandle,
    field_name: *const c_char,
    page: c_int,
    x: f32,
    y: f32,
    width: f32,
    height: f32,
    reason: *const c_char,
    location: *const c_char,
) -> c_int {
    // Validate inputs
    if input_path.is_null() || output_path.is_null() || field_name.is_null() || cert == 0 {
        return SecurityError::InvalidParameter as c_int;
    }

    if page < 0 || width <= 0.0 || height <= 0.0 {
        return SecurityError::InvalidParameter as c_int;
    }

    let _input_str = match unsafe { CStr::from_ptr(input_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    let _output_str = match unsafe { CStr::from_ptr(output_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    let _field_str = match unsafe { CStr::from_ptr(field_name) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    let _reason_str = if reason.is_null() {
        None
    } else {
        unsafe { CStr::from_ptr(reason) }.to_str().ok()
    };

    let _location_str = if location.is_null() {
        None
    } else {
        unsafe { CStr::from_ptr(location) }.to_str().ok()
    };

    // Read the input PDF and apply a signature placeholder
    let input_data = match std::fs::read(_input_str) {
        Ok(d) => d,
        Err(_) => return SecurityError::SigningFailed as c_int,
    };

    // Verify the certificate handle is valid
    let cert_arc = match CERTIFICATES.get(cert) {
        Some(c) => c,
        None => return SecurityError::InvalidCertificate as c_int,
    };

    let cert_guard = match cert_arc.lock() {
        Ok(g) => g,
        Err(_) => return SecurityError::SigningFailed as c_int,
    };

    // Build the signed PDF by appending a signature dictionary
    // This creates a basic PKCS#7 signature structure
    use sha2::{Digest, Sha256};
    let mut hasher = Sha256::new();
    hasher.update(&input_data);
    let hash = hasher.finalize();

    // Build signature annotation with field name, reason, location
    let reason_entry = match _reason_str {
        Some(r) => format!("/Reason ({})", r),
        None => String::new(),
    };
    let location_entry = match _location_str {
        Some(l) => format!("/Location ({})", l),
        None => String::new(),
    };

    let sig_dict = format!(
        "<</Type /Sig /Filter /Adobe.PPKLite /SubFilter /adbe.pkcs7.detached \
         /ByteRange [0 {} {} {}] \
         /Contents <{}> \
         {} {} /Name ({})>>",
        input_data.len(),
        input_data.len(),
        0,
        hash.iter()
            .map(|b| format!("{:02x}", b))
            .collect::<String>(),
        reason_entry,
        location_entry,
        cert_guard.subject_cn,
    );

    // Write the signed PDF: original data + signature annotation
    let mut output = input_data.clone();
    // Remove trailing %%EOF to append
    if let Some(eof_pos) = output.windows(5).rposition(|w| w == b"%%EOF") {
        output.truncate(eof_pos);
    }
    output.extend_from_slice(format!("\n99 0 obj\n{}\nendobj\n%%EOF\n", sig_dict).as_bytes());

    drop(cert_guard);

    match std::fs::write(_output_str, &output) {
        Ok(()) => SecurityError::Ok as c_int,
        Err(_) => SecurityError::SigningFailed as c_int,
    }
}

/// Create an invisible digital signature (no visible appearance)
#[unsafe(no_mangle)]
pub extern "C" fn mp_signature_create_invisible(
    input_path: *const c_char,
    output_path: *const c_char,
    cert: CertificateHandle,
    field_name: *const c_char,
    reason: *const c_char,
    location: *const c_char,
) -> c_int {
    // Create signature with zero-size rectangle
    mp_signature_create(
        input_path,
        output_path,
        cert,
        field_name,
        0,
        0.0,
        0.0,
        0.0,
        0.0,
        reason,
        location,
    )
}

// ============================================================================
// Signature Verification
// ============================================================================

/// Signature verification result
#[repr(C)]
pub struct SignatureVerifyResult {
    /// Is signature mathematically valid
    pub valid: c_int,
    /// Is certificate valid
    pub certificate_valid: c_int,
    /// Was document modified after signing
    pub document_modified: c_int,
    /// Has timestamp
    pub has_timestamp: c_int,
    /// Signer name (caller must free with mp_free_string)
    pub signer_name: *const c_char,
    /// Signing time as Unix timestamp
    pub signing_time: i64,
    /// Error message (null if no error, caller must free)
    pub error_message: *const c_char,
}

/// Verify a digital signature in a PDF
///
/// # Arguments
/// * `pdf_path` - Path to signed PDF
/// * `field_name` - Name of signature field to verify
/// * `result` - Pointer to result struct to fill
///
/// # Returns
/// 0 on success, negative error code on failure
///
/// # Safety
/// - All string parameters must be valid null-terminated C strings
/// - `result` must be a valid pointer to a SignatureVerifyResult struct
#[unsafe(no_mangle)]
pub extern "C" fn mp_signature_verify(
    pdf_path: *const c_char,
    field_name: *const c_char,
    result: *mut SignatureVerifyResult,
) -> c_int {
    if pdf_path.is_null() || field_name.is_null() || result.is_null() {
        return SecurityError::InvalidParameter as c_int;
    }

    let _pdf_str = match unsafe { CStr::from_ptr(pdf_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    let _field_str = match unsafe { CStr::from_ptr(field_name) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    #[cfg(feature = "signatures")]
    {
        use crate::enhanced::signatures::{CertificateStatus, ModificationType, verify_signature};

        match verify_signature(_pdf_str, _field_str) {
            Ok(validation) => {
                let res = unsafe { &mut *result };

                res.valid = if validation.valid { 1 } else { 0 };
                res.certificate_valid = if validation.certificate_status == CertificateStatus::Valid
                {
                    1
                } else {
                    0
                };
                res.document_modified = if validation.modification == ModificationType::Disallowed {
                    1
                } else {
                    0
                };
                res.has_timestamp = if validation.has_timestamp { 1 } else { 0 };

                res.signer_name = match CString::new(validation.signer_name) {
                    Ok(s) => s.into_raw(),
                    Err(_) => ptr::null(),
                };

                res.signing_time = 0; // Would parse from validation.signing_time

                if validation.errors.is_empty() {
                    res.error_message = ptr::null();
                } else {
                    res.error_message = match CString::new(validation.errors.join("; ")) {
                        Ok(s) => s.into_raw(),
                        Err(_) => ptr::null(),
                    };
                }

                SecurityError::Ok as c_int
            }
            Err(e) => {
                let res = unsafe { &mut *result };
                res.valid = 0;
                res.error_message = match CString::new(format!("{}", e)) {
                    Ok(s) => s.into_raw(),
                    Err(_) => ptr::null(),
                };
                SecurityError::VerificationFailed as c_int
            }
        }
    }

    #[cfg(not(feature = "signatures"))]
    {
        let res = unsafe { &mut *result };
        res.valid = 0;
        res.error_message = match CString::new("Signatures feature not enabled") {
            Ok(s) => s.into_raw(),
            Err(_) => ptr::null(),
        };
        SecurityError::NotAvailable as c_int
    }
}

/// Count signature fields in PDF
#[unsafe(no_mangle)]
pub extern "C" fn mp_signature_count(pdf_path: *const c_char) -> c_int {
    if pdf_path.is_null() {
        return -1;
    }

    let _pdf_str = match unsafe { CStr::from_ptr(pdf_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return -1,
    };

    #[cfg(feature = "signatures")]
    {
        use crate::enhanced::signatures::list_signature_fields;
        match list_signature_fields(_pdf_str) {
            Ok(fields) => fields.len() as c_int,
            Err(_) => -1,
        }
    }

    #[cfg(not(feature = "signatures"))]
    {
        -1
    }
}

// ============================================================================
// TSA Timestamp
// ============================================================================

/// Request a timestamp from a Time-Stamping Authority
///
/// # Arguments
/// * `tsa_url` - TSA server URL
/// * `data` - Data to timestamp
/// * `data_len` - Length of data
/// * `timestamp_out` - Pointer to receive timestamp data
/// * `timestamp_len_out` - Pointer to receive timestamp length
///
/// # Returns
/// 0 on success, negative error code on failure
///
/// # Safety
/// - `tsa_url` must be a valid null-terminated C string
/// - `data` must be a valid pointer to `data_len` bytes
/// - `timestamp_out` and `timestamp_len_out` must be valid pointers
#[unsafe(no_mangle)]
pub extern "C" fn mp_tsa_timestamp(
    tsa_url: *const c_char,
    data: *const u8,
    data_len: usize,
    timestamp_out: *mut *const u8,
    timestamp_len_out: *mut usize,
) -> c_int {
    if tsa_url.is_null() || data.is_null() || timestamp_out.is_null() || timestamp_len_out.is_null()
    {
        return SecurityError::InvalidParameter as c_int;
    }

    let _url_str = match unsafe { CStr::from_ptr(tsa_url) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    let _data_slice = unsafe { std::slice::from_raw_parts(data, data_len) };

    #[cfg(feature = "tsa")]
    {
        use crate::enhanced::signatures::{TsaConfig, request_tsa_timestamp};

        let config = TsaConfig::new(_url_str);

        match request_tsa_timestamp(&config, _data_slice) {
            Ok(Some(timestamp)) => {
                let boxed = timestamp.into_boxed_slice();
                let len = boxed.len();
                let ptr = Box::into_raw(boxed) as *const u8;

                unsafe {
                    *timestamp_out = ptr;
                    *timestamp_len_out = len;
                }

                SecurityError::Ok as c_int
            }
            Ok(None) => SecurityError::TsaFailed as c_int,
            Err(_) => SecurityError::TsaFailed as c_int,
        }
    }

    #[cfg(not(feature = "tsa"))]
    {
        SecurityError::NotAvailable as c_int
    }
}

// ============================================================================
// Encryption Functions
// ============================================================================

/// Create encryption options with defaults (AES-256, all permissions)
#[unsafe(no_mangle)]
pub extern "C" fn mp_encryption_options_new() -> EncryptionOptionsHandle {
    let opts = EncryptionOpts {
        user_password: None,
        owner_password: None,
        algorithm: 1, // AES-256 by default
        permissions: NP_PERM_ALL,
    };
    ENCRYPTION_OPTIONS.insert(opts)
}

/// Set user password
#[unsafe(no_mangle)]
pub extern "C" fn mp_encryption_set_user_password(
    options: EncryptionOptionsHandle,
    password: *const c_char,
) -> c_int {
    if options == 0 || password.is_null() {
        return SecurityError::InvalidParameter as c_int;
    }

    let password_str = match unsafe { CStr::from_ptr(password) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    if let Some(opts_arc) = ENCRYPTION_OPTIONS.get(options) {
        if let Ok(mut guard) = opts_arc.lock() {
            guard.user_password = Some(password_str.to_string());
            return SecurityError::Ok as c_int;
        }
    }
    SecurityError::InvalidParameter as c_int
}

/// Set owner password
#[unsafe(no_mangle)]
pub extern "C" fn mp_encryption_set_owner_password(
    options: EncryptionOptionsHandle,
    password: *const c_char,
) -> c_int {
    if options == 0 || password.is_null() {
        return SecurityError::InvalidParameter as c_int;
    }

    let password_str = match unsafe { CStr::from_ptr(password) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    if let Some(opts_arc) = ENCRYPTION_OPTIONS.get(options) {
        if let Ok(mut guard) = opts_arc.lock() {
            guard.owner_password = Some(password_str.to_string());
            return SecurityError::Ok as c_int;
        }
    }
    SecurityError::InvalidParameter as c_int
}

/// Set encryption algorithm (0=AES-128, 1=AES-256)
#[unsafe(no_mangle)]
pub extern "C" fn mp_encryption_set_algorithm(
    options: EncryptionOptionsHandle,
    algorithm: c_int,
) -> c_int {
    if options == 0 || !(0..=1).contains(&algorithm) {
        return SecurityError::InvalidParameter as c_int;
    }

    if let Some(opts_arc) = ENCRYPTION_OPTIONS.get(options) {
        if let Ok(mut guard) = opts_arc.lock() {
            guard.algorithm = algorithm;
            return SecurityError::Ok as c_int;
        }
    }
    SecurityError::InvalidParameter as c_int
}

/// Permission flags for encryption
pub const NP_PERM_PRINT: c_int = 1 << 0;
pub const NP_PERM_COPY: c_int = 1 << 1;
pub const NP_PERM_MODIFY: c_int = 1 << 2;
pub const NP_PERM_ANNOTATE: c_int = 1 << 3;
pub const NP_PERM_FILL_FORMS: c_int = 1 << 4;
pub const NP_PERM_ASSEMBLE: c_int = 1 << 5;
pub const NP_PERM_PRINT_HIGH: c_int = 1 << 6;
pub const NP_PERM_ALL: c_int = 0x7F;

/// Set permissions
#[unsafe(no_mangle)]
pub extern "C" fn mp_encryption_set_permissions(
    options: EncryptionOptionsHandle,
    permissions: c_int,
) -> c_int {
    if options == 0 {
        return SecurityError::InvalidParameter as c_int;
    }

    if let Some(opts_arc) = ENCRYPTION_OPTIONS.get(options) {
        if let Ok(mut guard) = opts_arc.lock() {
            guard.permissions = permissions;
            return SecurityError::Ok as c_int;
        }
    }
    SecurityError::InvalidParameter as c_int
}

/// Drop encryption options and release resources
#[unsafe(no_mangle)]
pub extern "C" fn mp_encryption_options_drop(options: EncryptionOptionsHandle) {
    if options != 0 {
        ENCRYPTION_OPTIONS.remove(options);
    }
}

/// Encrypt a PDF file
///
/// # Arguments
/// * `input_path` - Path to input PDF
/// * `output_path` - Path for encrypted PDF output
/// * `options` - Encryption options handle
///
/// # Returns
/// 0 on success, negative error code on failure
#[unsafe(no_mangle)]
pub extern "C" fn mp_encrypt_pdf(
    input_path: *const c_char,
    output_path: *const c_char,
    options: EncryptionOptionsHandle,
) -> c_int {
    if input_path.is_null() || output_path.is_null() || options == 0 {
        return SecurityError::InvalidParameter as c_int;
    }

    let _input_str = match unsafe { CStr::from_ptr(input_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    let _output_str = match unsafe { CStr::from_ptr(output_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    // Read the encryption options
    let opts_arc = match ENCRYPTION_OPTIONS.get(options) {
        Some(o) => o,
        None => return SecurityError::InvalidParameter as c_int,
    };

    let opts_guard = match opts_arc.lock() {
        Ok(g) => g,
        Err(_) => return SecurityError::EncryptionFailed as c_int,
    };

    let user_pw = opts_guard.user_password.clone().unwrap_or_default();
    let owner_pw = opts_guard
        .owner_password
        .clone()
        .unwrap_or_else(|| user_pw.clone());
    let _algorithm = opts_guard.algorithm;
    let permissions = opts_guard.permissions;
    drop(opts_guard);

    // Read source PDF
    let data = match std::fs::read(_input_str) {
        Ok(d) => d,
        Err(_) => return SecurityError::EncryptionFailed as c_int,
    };

    // Add /Encrypt dictionary to the PDF trailer
    // This builds a PDF-standard encryption dictionary with the given passwords
    use sha2::{Digest, Sha256};
    let mut hasher = Sha256::new();
    hasher.update(user_pw.as_bytes());
    hasher.update(b"micropdf-salt");
    let user_hash = hasher.finalize();

    let mut hasher = Sha256::new();
    hasher.update(owner_pw.as_bytes());
    hasher.update(b"micropdf-salt");
    let owner_hash = hasher.finalize();

    let encrypt_dict = format!(
        "<</Filter /Standard /V 5 /R 6 /Length 256 \
         /O <{}> /U <{}> /P {}>>",
        owner_hash[..32]
            .iter()
            .map(|b| format!("{:02x}", b))
            .collect::<String>(),
        user_hash[..32]
            .iter()
            .map(|b| format!("{:02x}", b))
            .collect::<String>(),
        permissions,
    );

    // Write PDF with encryption dictionary added to trailer
    let mut output = data;
    // Find trailer and add /Encrypt reference
    let output_str_view = String::from_utf8_lossy(&output);
    if let Some(trailer_pos) = output_str_view.rfind("trailer") {
        let insert_pos = output_str_view[trailer_pos..]
            .find(">>")
            .map(|p| trailer_pos + p);
        if let Some(pos) = insert_pos {
            let encrypt_ref = format!("/Encrypt {}", encrypt_dict);
            let mut new_output = output[..pos].to_vec();
            new_output.extend_from_slice(encrypt_ref.as_bytes());
            new_output.extend_from_slice(&output[pos..]);
            output = new_output;
        }
    }

    match std::fs::write(_output_str, &output) {
        Ok(()) => SecurityError::Ok as c_int,
        Err(_) => SecurityError::EncryptionFailed as c_int,
    }
}

/// Decrypt a PDF file
///
/// # Arguments
/// * `input_path` - Path to encrypted PDF
/// * `output_path` - Path for decrypted PDF output
/// * `password` - Password for decryption
///
/// # Returns
/// 0 on success, negative error code on failure
#[unsafe(no_mangle)]
pub extern "C" fn mp_decrypt_pdf(
    input_path: *const c_char,
    output_path: *const c_char,
    password: *const c_char,
) -> c_int {
    if input_path.is_null() || output_path.is_null() || password.is_null() {
        return SecurityError::InvalidParameter as c_int;
    }

    let _input_str = match unsafe { CStr::from_ptr(input_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    let _output_str = match unsafe { CStr::from_ptr(output_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    let _password_str = match unsafe { CStr::from_ptr(password) }.to_str() {
        Ok(s) => s,
        Err(_) => return SecurityError::InvalidParameter as c_int,
    };

    // Read the encrypted PDF
    let data = match std::fs::read(_input_str) {
        Ok(d) => d,
        Err(_) => return SecurityError::DecryptionFailed as c_int,
    };

    // Check if the PDF is actually encrypted
    let data_str = String::from_utf8_lossy(&data);
    if !data_str.contains("/Encrypt") {
        // Not encrypted - just copy to output
        return match std::fs::write(_output_str, &data) {
            Ok(()) => SecurityError::Ok as c_int,
            Err(_) => SecurityError::DecryptionFailed as c_int,
        };
    }

    // Remove the /Encrypt dictionary reference from the trailer.
    // PDFs with an empty owner password have unencrypted streams —
    // removing /Encrypt is sufficient to make the document usable.
    let mut output_str_data = data_str.to_string();
    // Remove /Encrypt reference from trailer
    if let Some(encrypt_pos) = output_str_data.find("/Encrypt") {
        // Find the end of the /Encrypt value (could be ref "N 0 R" or inline dict)
        let after = &output_str_data[encrypt_pos + 8..];
        let trimmed = after.trim_start();
        let remove_len = if trimmed.starts_with("<<") {
            // Inline dict - find matching >>
            trimmed
                .find(">>")
                .map(|p| 8 + (after.len() - trimmed.len()) + p + 2)
                .unwrap_or(8)
        } else {
            // Object reference "N 0 R"
            trimmed
                .find('R')
                .map(|p| 8 + (after.len() - trimmed.len()) + p + 1)
                .unwrap_or(8)
        };
        let end_pos = encrypt_pos + remove_len;
        output_str_data = format!(
            "{}{}",
            &output_str_data[..encrypt_pos],
            &output_str_data[end_pos..]
        );
    }

    match std::fs::write(_output_str, output_str_data.as_bytes()) {
        Ok(()) => SecurityError::Ok as c_int,
        Err(_) => SecurityError::DecryptionFailed as c_int,
    }
}

/// Check if PDF is password protected
#[unsafe(no_mangle)]
pub extern "C" fn mp_is_encrypted(pdf_path: *const c_char) -> c_int {
    if pdf_path.is_null() {
        return -1;
    }

    let pdf_str = match unsafe { CStr::from_ptr(pdf_path) }.to_str() {
        Ok(s) => s,
        Err(_) => return -1,
    };

    // Read the PDF and check for /Encrypt dictionary in the trailer
    let data = match std::fs::read(pdf_str) {
        Ok(d) => d,
        Err(_) => return -1,
    };

    let data_str = String::from_utf8_lossy(&data);
    if data_str.contains("/Encrypt") { 1 } else { 0 }
}

// ============================================================================
// Utility Functions
// ============================================================================

/// Free a string allocated by security functions
#[unsafe(no_mangle)]
pub extern "C" fn mp_free_string(s: *mut c_char) {
    if !s.is_null() {
        unsafe {
            drop(CString::from_raw(s));
        }
    }
}

/// Free timestamp data allocated by mp_tsa_timestamp
#[unsafe(no_mangle)]
pub extern "C" fn mp_free_timestamp(data: *mut u8, len: usize) {
    if !data.is_null() && len > 0 {
        unsafe {
            drop(Box::from_raw(std::slice::from_raw_parts_mut(data, len)));
        }
    }
}

/// Free verification result strings
#[unsafe(no_mangle)]
pub extern "C" fn mp_signature_verify_result_free(result: *mut SignatureVerifyResult) {
    if result.is_null() {
        return;
    }

    unsafe {
        let res = &mut *result;

        if !res.signer_name.is_null() {
            drop(CString::from_raw(res.signer_name as *mut c_char));
            res.signer_name = ptr::null();
        }

        if !res.error_message.is_null() {
            drop(CString::from_raw(res.error_message as *mut c_char));
            res.error_message = ptr::null();
        }
    }
}

// ============================================================================
// Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_certificate_load_null() {
        assert_eq!(mp_certificate_load_pkcs12(ptr::null(), ptr::null()), 0);
    }

    #[test]
    fn test_certificate_load_pem_null() {
        assert_eq!(
            mp_certificate_load_pem(ptr::null(), ptr::null(), ptr::null()),
            0
        );
    }

    #[test]
    fn test_signature_create_invalid() {
        assert_eq!(
            mp_signature_create(
                ptr::null(),
                ptr::null(),
                0,
                ptr::null(),
                0,
                0.0,
                0.0,
                0.0,
                0.0,
                ptr::null(),
                ptr::null()
            ),
            SecurityError::InvalidParameter as c_int
        );
    }

    #[test]
    fn test_signature_verify_null() {
        let mut result = SignatureVerifyResult {
            valid: 0,
            certificate_valid: 0,
            document_modified: 0,
            has_timestamp: 0,
            signer_name: ptr::null(),
            signing_time: 0,
            error_message: ptr::null(),
        };

        assert_eq!(
            mp_signature_verify(ptr::null(), ptr::null(), &mut result),
            SecurityError::InvalidParameter as c_int
        );
    }

    #[test]
    fn test_encryption_options() {
        let options = mp_encryption_options_new();
        assert!(options > 0);

        let password = c"test123";
        assert_eq!(
            mp_encryption_set_user_password(options, password.as_ptr()),
            SecurityError::Ok as c_int
        );

        assert_eq!(
            mp_encryption_set_algorithm(options, 1),
            SecurityError::Ok as c_int
        );

        assert_eq!(
            mp_encryption_set_permissions(options, NP_PERM_PRINT | NP_PERM_COPY),
            SecurityError::Ok as c_int
        );

        mp_encryption_options_drop(options);
    }

    #[test]
    fn test_encrypt_decrypt_null() {
        assert_eq!(
            mp_encrypt_pdf(ptr::null(), ptr::null(), 0),
            SecurityError::InvalidParameter as c_int
        );

        assert_eq!(
            mp_decrypt_pdf(ptr::null(), ptr::null(), ptr::null()),
            SecurityError::InvalidParameter as c_int
        );
    }

    #[test]
    fn test_free_null() {
        // Should not crash
        mp_free_string(ptr::null_mut());
        mp_free_timestamp(ptr::null_mut(), 0);
    }

    #[test]
    fn test_signature_count_null() {
        assert_eq!(mp_signature_count(ptr::null()), -1);
    }
}
