//! PDF Signature FFI Module
//!
//! Provides support for PDF digital signatures, including signature
//! verification, signing, and certificate handling.

use crate::ffi::{Handle, HandleStore};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::ffi::{CStr, CString, c_char};
use std::ptr;
use std::sync::{Arc, LazyLock, Mutex};

// ============================================================================
// Type Aliases
// ============================================================================

type ContextHandle = Handle;
type DocumentHandle = Handle;
type AnnotHandle = Handle;
type PdfObjHandle = Handle;
type StreamHandle = Handle;

// ============================================================================
// Signature Error Types
// ============================================================================

/// Signature verification error codes
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(C)]
pub enum SignatureError {
    #[default]
    Okay = 0,
    NoSignatures = 1,
    NoCertificate = 2,
    DigestFailure = 3,
    SelfSigned = 4,
    SelfSignedInChain = 5,
    NotTrusted = 6,
    NotSigned = 7,
    Unknown = 8,
}

impl SignatureError {
    pub fn from_i32(value: i32) -> Self {
        match value {
            0 => SignatureError::Okay,
            1 => SignatureError::NoSignatures,
            2 => SignatureError::NoCertificate,
            3 => SignatureError::DigestFailure,
            4 => SignatureError::SelfSigned,
            5 => SignatureError::SelfSignedInChain,
            6 => SignatureError::NotTrusted,
            7 => SignatureError::NotSigned,
            _ => SignatureError::Unknown,
        }
    }

    pub fn description(&self) -> &'static str {
        match self {
            SignatureError::Okay => "signature is valid",
            SignatureError::NoSignatures => "no signatures found",
            SignatureError::NoCertificate => "certificate not found",
            SignatureError::DigestFailure => "digest verification failed",
            SignatureError::SelfSigned => "certificate is self-signed",
            SignatureError::SelfSignedInChain => "self-signed certificate in chain",
            SignatureError::NotTrusted => "certificate is not trusted",
            SignatureError::NotSigned => "document is not signed",
            SignatureError::Unknown => "unknown error",
        }
    }
}

// ============================================================================
// Signature Appearance Flags
// ============================================================================

/// Flags for signature appearance
pub const PDF_SIGNATURE_SHOW_LABELS: i32 = 1;
pub const PDF_SIGNATURE_SHOW_DN: i32 = 2;
pub const PDF_SIGNATURE_SHOW_DATE: i32 = 4;
pub const PDF_SIGNATURE_SHOW_TEXT_NAME: i32 = 8;
pub const PDF_SIGNATURE_SHOW_GRAPHIC_NAME: i32 = 16;
pub const PDF_SIGNATURE_SHOW_LOGO: i32 = 32;

/// Default signature appearance
pub const PDF_SIGNATURE_DEFAULT_APPEARANCE: i32 = PDF_SIGNATURE_SHOW_LABELS
    | PDF_SIGNATURE_SHOW_DN
    | PDF_SIGNATURE_SHOW_DATE
    | PDF_SIGNATURE_SHOW_TEXT_NAME
    | PDF_SIGNATURE_SHOW_GRAPHIC_NAME
    | PDF_SIGNATURE_SHOW_LOGO;

// ============================================================================
// Distinguished Name Structure
// ============================================================================

/// X.500 Distinguished Name for certificate identification
#[derive(Debug, Clone, Default)]
pub struct DistinguishedName {
    /// Common Name (CN)
    pub cn: Option<String>,
    /// Organization (O)
    pub o: Option<String>,
    /// Organizational Unit (OU)
    pub ou: Option<String>,
    /// Email address
    pub email: Option<String>,
    /// Country (C)
    pub c: Option<String>,
}

impl DistinguishedName {
    pub fn new() -> Self {
        Self::default()
    }

    /// Format the DN as a string
    pub fn format(&self) -> String {
        let mut parts = Vec::new();
        if let Some(ref cn) = self.cn {
            parts.push(format!("CN={}", cn));
        }
        if let Some(ref o) = self.o {
            parts.push(format!("O={}", o));
        }
        if let Some(ref ou) = self.ou {
            parts.push(format!("OU={}", ou));
        }
        if let Some(ref email) = self.email {
            parts.push(format!("EMAIL={}", email));
        }
        if let Some(ref c) = self.c {
            parts.push(format!("C={}", c));
        }
        parts.join(", ")
    }
}

// ============================================================================
// C-compatible DN structure
// ============================================================================

/// C-compatible Distinguished Name structure for FFI
#[repr(C)]
pub struct FfiDistinguishedName {
    pub cn: *const c_char,
    pub o: *const c_char,
    pub ou: *const c_char,
    pub email: *const c_char,
    pub c: *const c_char,
}

// ============================================================================
// Byte Range Structure
// ============================================================================

/// Byte range for signature coverage
#[derive(Debug, Clone, Default)]
#[repr(C)]
pub struct ByteRange {
    pub offset: i64,
    pub length: i64,
}

// ============================================================================
// Signature Information
// ============================================================================

/// Signature field information
#[derive(Debug, Clone)]
pub struct SignatureInfo {
    /// Whether the field is signed
    pub is_signed: bool,
    /// Signer's distinguished name
    pub signer_dn: Option<DistinguishedName>,
    /// Signing reason
    pub reason: Option<String>,
    /// Signing location
    pub location: Option<String>,
    /// Signing date (Unix timestamp)
    pub date: i64,
    /// Byte ranges covered by signature
    pub byte_ranges: Vec<ByteRange>,
    /// Signature contents (PKCS#7 data)
    pub contents: Vec<u8>,
    /// Whether document changed since signing
    pub incremental_change: bool,
    /// Digest verification status
    pub digest_status: SignatureError,
    /// Certificate verification status
    pub certificate_status: SignatureError,
}

impl Default for SignatureInfo {
    fn default() -> Self {
        Self::new()
    }
}

impl SignatureInfo {
    pub fn new() -> Self {
        Self {
            is_signed: false,
            signer_dn: None,
            reason: None,
            location: None,
            date: 0,
            byte_ranges: Vec::new(),
            contents: Vec::new(),
            incremental_change: false,
            digest_status: SignatureError::NotSigned,
            certificate_status: SignatureError::NotSigned,
        }
    }
}

// ============================================================================
// PKCS#7 Signer (for signing documents)
// ============================================================================

/// PKCS#7 Signer for creating digital signatures
#[derive(Debug)]
pub struct Pkcs7Signer {
    /// Signer's distinguished name
    pub dn: DistinguishedName,
    /// DER-encoded private key bytes
    pub private_key: Vec<u8>,
    /// DER-encoded X.509 certificate chain
    pub certificate: Vec<u8>,
    /// Maximum digest size
    pub max_digest_size: usize,
}

impl Pkcs7Signer {
    pub fn new(cn: &str) -> Self {
        Self {
            dn: DistinguishedName {
                cn: Some(cn.to_string()),
                ..Default::default()
            },
            private_key: Vec::new(),
            certificate: Vec::new(),
            max_digest_size: 8192, // Default PKCS#7 size
        }
    }

    /// Get the signer's distinguished name
    pub fn get_signing_name(&self) -> &DistinguishedName {
        &self.dn
    }

    /// Get maximum digest size
    pub fn max_digest_size(&self) -> usize {
        self.max_digest_size
    }

    /// Create a PKCS#7 digest structure containing a SHA-256 hash of the
    /// input data, the signer's certificate (if present), and signer info.
    ///
    /// Without the `signatures` feature (which provides RSA/ECDSA crates),
    /// the signature value field contains the raw hash rather than a true
    /// asymmetric signature.  The outer structure is still valid DER so
    /// that verifiers can parse it and extract the digest.
    pub fn create_digest(&self, data: &[u8]) -> Vec<u8> {
        let hash = Sha256::digest(data);

        // --- build SignedData inner content ---
        let mut signed_data_content = Vec::new();

        // version INTEGER (1)
        signed_data_content.extend_from_slice(&[0x02, 0x01, 0x01]);

        // digestAlgorithms SET { SHA-256 AlgorithmIdentifier }
        let sha256_algo_id: &[u8] = &[
            0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
            0x00,
        ];
        signed_data_content.push(0x31); // SET
        signed_data_content.extend(encode_der_length(sha256_algo_id.len()));
        signed_data_content.extend_from_slice(sha256_algo_id);

        // contentInfo SEQUENCE { OID id-data }
        let content_info: &[u8] = &[
            0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01,
        ];
        signed_data_content.extend_from_slice(content_info);

        // certificates [0] IMPLICIT (include cert if available)
        if !self.certificate.is_empty() {
            signed_data_content.push(0xA0); // context [0]
            signed_data_content.extend(encode_der_length(self.certificate.len()));
            signed_data_content.extend_from_slice(&self.certificate);
        }

        // --- signerInfo ---
        let mut signer_info_content = Vec::new();
        // version INTEGER (1)
        signer_info_content.extend_from_slice(&[0x02, 0x01, 0x01]);
        // issuerAndSerialNumber (minimal placeholder SEQUENCE)
        signer_info_content.extend_from_slice(&[0x30, 0x04, 0x30, 0x00, 0x02, 0x00]);
        // digestAlgorithm
        signer_info_content.extend_from_slice(sha256_algo_id);

        // signedAttrs [0] IMPLICIT - messageDigest attribute
        let mut digest_attr = Vec::new();
        // SEQUENCE { OID messageDigest, SET { OCTET STRING hash } }
        let msg_digest_oid: &[u8] = &[
            0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x04,
        ];
        let hash_bytes = hash.as_slice();
        let mut inner_attr = Vec::new();
        inner_attr.extend_from_slice(msg_digest_oid);
        // SET { OCTET STRING }
        let octet_len = 2 + hash_bytes.len(); // tag + len + hash
        inner_attr.push(0x31); // SET
        inner_attr.extend(encode_der_length(octet_len));
        inner_attr.push(0x04); // OCTET STRING
        inner_attr.push(hash_bytes.len() as u8);
        inner_attr.extend_from_slice(hash_bytes);

        digest_attr.push(0x30); // SEQUENCE wrapper
        digest_attr.extend(encode_der_length(inner_attr.len()));
        digest_attr.extend_from_slice(&inner_attr);

        signer_info_content.push(0xA0); // [0] IMPLICIT
        signer_info_content.extend(encode_der_length(digest_attr.len()));
        signer_info_content.extend_from_slice(&digest_attr);

        // signatureAlgorithm (SHA-256 with RSA - placeholder OID)
        let sig_algo: &[u8] = &[
            0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05,
            0x00,
        ];
        signer_info_content.extend_from_slice(sig_algo);

        // signature OCTET STRING (the raw hash when no private key is available)
        let sig_value = if !self.private_key.is_empty() {
            // With a real key we would sign here; without the `signatures`
            // feature we fall back to the raw hash.
            hash_bytes.to_vec()
        } else {
            hash_bytes.to_vec()
        };
        signer_info_content.push(0x04); // OCTET STRING
        signer_info_content.extend(encode_der_length(sig_value.len()));
        signer_info_content.extend_from_slice(&sig_value);

        // Wrap signerInfo in SEQUENCE
        let mut signer_info = Vec::new();
        signer_info.push(0x30);
        signer_info.extend(encode_der_length(signer_info_content.len()));
        signer_info.extend_from_slice(&signer_info_content);

        // signerInfos SET
        signed_data_content.push(0x31); // SET
        signed_data_content.extend(encode_der_length(signer_info.len()));
        signed_data_content.extend_from_slice(&signer_info);

        // Wrap SignedData in SEQUENCE
        let mut signed_data = Vec::new();
        signed_data.push(0x30);
        signed_data.extend(encode_der_length(signed_data_content.len()));
        signed_data.extend_from_slice(&signed_data_content);

        // --- outer ContentInfo ---
        let signed_data_oid: &[u8] = &[
            0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02,
        ];
        let mut content_info_inner = Vec::new();
        content_info_inner.extend_from_slice(signed_data_oid);
        // [0] EXPLICIT
        content_info_inner.push(0xA0);
        content_info_inner.extend(encode_der_length(signed_data.len()));
        content_info_inner.extend_from_slice(&signed_data);

        let mut pkcs7 = Vec::new();
        pkcs7.push(0x30); // SEQUENCE
        pkcs7.extend(encode_der_length(content_info_inner.len()));
        pkcs7.extend_from_slice(&content_info_inner);

        // Pad to max_digest_size so callers that pre-allocate space work
        if pkcs7.len() < self.max_digest_size {
            pkcs7.resize(self.max_digest_size, 0);
        }

        pkcs7
    }
}

// ============================================================================
// PKCS#7 Verifier (for verifying signatures)
// ============================================================================

/// PKCS#7 Verifier for validating digital signatures
#[derive(Debug)]
pub struct Pkcs7Verifier {
    /// Trusted certificate store (placeholder)
    pub trusted_certs: Vec<Vec<u8>>,
}

impl Default for Pkcs7Verifier {
    fn default() -> Self {
        Self::new()
    }
}

impl Pkcs7Verifier {
    pub fn new() -> Self {
        Self {
            trusted_certs: Vec::new(),
        }
    }

    /// Add a trusted certificate
    pub fn add_trusted_cert(&mut self, cert: Vec<u8>) {
        self.trusted_certs.push(cert);
    }

    /// Validate that `signature` contains a plausible PKCS#7 structure
    /// with an embedded certificate.
    ///
    /// Checks performed:
    /// 1. Non-empty input
    /// 2. Outer tag is ASN.1 SEQUENCE (0x30) - required for valid DER
    /// 3. Contains the PKCS#7 SignedData OID (1.2.840.113549.1.7.2)
    /// 4. Contains at least one certificate context tag ([0], 0xA0)
    ///
    /// When trusted certificates have been loaded via `add_trusted_cert`,
    /// the method also checks whether any certificate in the PKCS#7
    /// matches a trusted certificate (byte-level comparison of the DER).
    pub fn check_certificate(&self, signature: &[u8]) -> SignatureError {
        // Reject empty signatures outright
        if signature.is_empty() {
            return SignatureError::NoCertificate;
        }

        // Must start with ASN.1 SEQUENCE tag
        if signature[0] != 0x30 {
            return SignatureError::NoCertificate;
        }

        // Look for the PKCS#7 SignedData OID: 1.2.840.113549.1.7.2
        let signed_data_oid: &[u8] = &[
            0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02,
        ];
        if !signature
            .windows(signed_data_oid.len())
            .any(|w| w == signed_data_oid)
        {
            return SignatureError::NoCertificate;
        }

        // Check for certificate context tag [0] (0xA0) which wraps the
        // certificates field in SignedData
        let has_cert_tag = signature.windows(1).any(|w| w[0] == 0xA0);
        if !has_cert_tag {
            return SignatureError::NoCertificate;
        }

        // If we have trusted certs, verify at least one embedded cert
        // matches by scanning for its DER bytes within the signature
        if !self.trusted_certs.is_empty() {
            let any_trusted = self.trusted_certs.iter().any(|trusted| {
                !trusted.is_empty()
                    && signature
                        .windows(trusted.len())
                        .any(|w| w == trusted.as_slice())
            });
            if !any_trusted {
                return SignatureError::NotTrusted;
            }
        }

        // Self-signed detection: if the PKCS#7 contains exactly one
        // certificate (no chain), treat it as self-signed.  We detect
        // this heuristically by counting SEQUENCE tags (0x30 0x82)
        // after the first [0] context tag.
        if let Some(cert_start) = signature.iter().position(|&b| b == 0xA0) {
            let cert_region = &signature[cert_start..];
            let cert_count = cert_region
                .windows(2)
                .filter(|w| w[0] == 0x30 && w[1] == 0x82)
                .count();
            if cert_count <= 1 {
                return SignatureError::SelfSigned;
            }
        }

        SignatureError::Okay
    }

    /// Verify the digest embedded in a PKCS#7 signature against the
    /// provided document data.
    ///
    /// The method computes a SHA-256 hash of `data` and then searches
    /// the PKCS#7 DER structure for a messageDigest attribute whose
    /// OCTET STRING value matches.  The messageDigest attribute is
    /// identified by its OID (1.2.840.113549.1.9.4) followed by a SET
    /// containing the hash in an OCTET STRING.
    pub fn check_digest(&self, data: &[u8], signature: &[u8]) -> SignatureError {
        if signature.is_empty() {
            return SignatureError::DigestFailure;
        }

        // Must be a valid DER SEQUENCE
        if signature[0] != 0x30 {
            return SignatureError::DigestFailure;
        }

        // Compute SHA-256 of the document data
        let computed_hash = Sha256::digest(data);
        let computed = computed_hash.as_slice();

        // Look for the messageDigest OID (1.2.840.113549.1.9.4) in the
        // PKCS#7 structure to locate the embedded hash.
        let msg_digest_oid: &[u8] = &[
            0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x04,
        ];

        // Find every occurrence of the messageDigest OID and try to
        // extract the hash from the SET { OCTET STRING } that follows.
        let mut pos = 0;
        while pos + msg_digest_oid.len() < signature.len() {
            if let Some(offset) = signature[pos..]
                .windows(msg_digest_oid.len())
                .position(|w| w == msg_digest_oid)
            {
                let oid_end = pos + offset + msg_digest_oid.len();
                // After the OID we expect: 0x31 <len> 0x04 <len> <hash>
                // Walk forward looking for the OCTET STRING tag
                let remaining = &signature[oid_end..];
                if let Some(octet_pos) = remaining.iter().position(|&b| b == 0x04) {
                    let hash_start = octet_pos + 1;
                    if hash_start < remaining.len() {
                        let hash_len = remaining[hash_start] as usize;
                        let hash_data_start = hash_start + 1;
                        if hash_data_start + hash_len <= remaining.len() {
                            let embedded = &remaining[hash_data_start..hash_data_start + hash_len];
                            if embedded == computed {
                                return SignatureError::Okay;
                            }
                        }
                    }
                }
                pos = oid_end;
            } else {
                break;
            }
        }

        SignatureError::DigestFailure
    }

    /// Extract signer distinguished name from a PKCS#7 DER structure.
    ///
    /// Scans the DER bytes for common X.500 attribute OIDs (CN, O, OU,
    /// emailAddress, C) and extracts their UTF8String/PrintableString
    /// values.  Returns `None` if the signature is empty or not valid DER.
    pub fn get_signatory(&self, signature: &[u8]) -> Option<DistinguishedName> {
        if signature.is_empty() || signature[0] != 0x30 {
            return None;
        }

        let mut dn = DistinguishedName::new();
        let mut found_any = false;

        // X.500 attribute type OIDs (id-at prefix: 2.5.4.*)
        // CN  = 2.5.4.3  -> 55 04 03
        // C   = 2.5.4.6  -> 55 04 06
        // O   = 2.5.4.10 -> 55 04 0A
        // OU  = 2.5.4.11 -> 55 04 0B
        // emailAddress = 1.2.840.113549.1.9.1
        //   -> 2A 86 48 86 F7 0D 01 09 01

        let attr_oids: &[(&[u8], &str)] = &[
            (&[0x55, 0x04, 0x03], "CN"),
            (&[0x55, 0x04, 0x06], "C"),
            (&[0x55, 0x04, 0x0A], "O"),
            (&[0x55, 0x04, 0x0B], "OU"),
            (
                &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01],
                "EMAIL",
            ),
        ];

        for &(oid_bytes, attr_name) in attr_oids {
            // Search for each OID in the DER.  The value follows as a
            // string type (UTF8String 0x0C, PrintableString 0x13, or
            // IA5String 0x16) with a length byte and then the text.
            let mut search_pos = 0;
            while search_pos + oid_bytes.len() < signature.len() {
                if let Some(offset) = signature[search_pos..]
                    .windows(oid_bytes.len())
                    .position(|w| w == oid_bytes)
                {
                    let after_oid = search_pos + offset + oid_bytes.len();
                    // The next byte should be a string tag
                    if after_oid + 2 <= signature.len() {
                        let tag = signature[after_oid];
                        // Accept UTF8String (0x0C), PrintableString (0x13),
                        // IA5String (0x16), T61String (0x14)
                        if tag == 0x0C || tag == 0x13 || tag == 0x16 || tag == 0x14 {
                            let str_len = signature[after_oid + 1] as usize;
                            let str_start = after_oid + 2;
                            if str_start + str_len <= signature.len() {
                                if let Ok(value) =
                                    std::str::from_utf8(&signature[str_start..str_start + str_len])
                                {
                                    if !value.is_empty() {
                                        found_any = true;
                                        match attr_name {
                                            "CN" => {
                                                if dn.cn.is_none() {
                                                    dn.cn = Some(value.to_string());
                                                }
                                            }
                                            "C" => {
                                                if dn.c.is_none() {
                                                    dn.c = Some(value.to_string());
                                                }
                                            }
                                            "O" => {
                                                if dn.o.is_none() {
                                                    dn.o = Some(value.to_string());
                                                }
                                            }
                                            "OU" => {
                                                if dn.ou.is_none() {
                                                    dn.ou = Some(value.to_string());
                                                }
                                            }
                                            "EMAIL" => {
                                                if dn.email.is_none() {
                                                    dn.email = Some(value.to_string());
                                                }
                                            }
                                            _ => {}
                                        }
                                    }
                                }
                            }
                        }
                    }
                    search_pos = after_oid;
                } else {
                    break;
                }
            }
        }

        if found_any { Some(dn) } else { None }
    }
}

// ============================================================================
// DER Encoding Helper
// ============================================================================

/// Encode a length value using DER definite-length encoding.
///
/// Lengths under 128 are encoded as a single byte.  Larger values use
/// the long form: a leading byte with the high bit set indicating how
/// many subsequent bytes carry the length.
fn encode_der_length(len: usize) -> Vec<u8> {
    if len < 128 {
        vec![len as u8]
    } else if len < 256 {
        vec![0x81, len as u8]
    } else if len < 65536 {
        vec![0x82, (len >> 8) as u8, len as u8]
    } else {
        vec![0x83, (len >> 16) as u8, (len >> 8) as u8, len as u8]
    }
}

// ============================================================================
// Global Handle Stores
// ============================================================================

pub static SIGNERS: LazyLock<HandleStore<Pkcs7Signer>> = LazyLock::new(HandleStore::new);
pub static VERIFIERS: LazyLock<HandleStore<Pkcs7Verifier>> = LazyLock::new(HandleStore::new);
pub static DISTINGUISHED_NAMES: LazyLock<HandleStore<DistinguishedName>> =
    LazyLock::new(HandleStore::new);
pub static SIGNATURE_INFOS: LazyLock<HandleStore<SignatureInfo>> = LazyLock::new(HandleStore::new);

// Store signatures per document
pub static DOC_SIGNATURES: LazyLock<Mutex<HashMap<DocumentHandle, Vec<SignatureInfo>>>> =
    LazyLock::new(|| Mutex::new(HashMap::new()));

// ============================================================================
// FFI Functions - Signature Query
// ============================================================================

/// Check if a signature field is signed.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_signature_is_signed(
    _ctx: ContextHandle,
    doc: DocumentHandle,
    _field: PdfObjHandle,
) -> i32 {
    let store = DOC_SIGNATURES.lock().unwrap();
    if let Some(sigs) = store.get(&doc) {
        if !sigs.is_empty() && sigs.iter().any(|s| s.is_signed) {
            return 1;
        }
    }
    0
}

/// Count signatures in document.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_count_signatures(_ctx: ContextHandle, doc: DocumentHandle) -> i32 {
    let store = DOC_SIGNATURES.lock().unwrap();
    if let Some(sigs) = store.get(&doc) {
        return sigs.len() as i32;
    }
    0
}

/// Get signature byte range.
/// Returns number of ranges, fills byte_range array.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_signature_byte_range(
    _ctx: ContextHandle,
    doc: DocumentHandle,
    _signature: PdfObjHandle,
    byte_range: *mut ByteRange,
) -> i32 {
    let store = DOC_SIGNATURES.lock().unwrap();
    if let Some(sigs) = store.get(&doc) {
        if let Some(sig) = sigs.first() {
            if !byte_range.is_null() && !sig.byte_ranges.is_empty() {
                unsafe {
                    *byte_range = sig.byte_ranges[0].clone();
                }
            }
            return sig.byte_ranges.len() as i32;
        }
    }
    0
}

/// Get signature contents (PKCS#7 data).
/// Returns size of contents, allocates and fills contents pointer.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_signature_contents(
    _ctx: ContextHandle,
    doc: DocumentHandle,
    _signature: PdfObjHandle,
    contents: *mut *mut c_char,
) -> usize {
    let store = DOC_SIGNATURES.lock().unwrap();
    if let Some(sigs) = store.get(&doc) {
        if let Some(sig) = sigs.first() {
            if !contents.is_null() && !sig.contents.is_empty() {
                let len = sig.contents.len();
                // Allocate using Box to ensure proper memory management
                let mut boxed = sig.contents.clone().into_boxed_slice();
                let ptr = boxed.as_mut_ptr() as *mut c_char;
                // SAFETY: We deliberately leak this memory to transfer ownership to the C caller.
                // The caller is responsible for freeing this memory using the appropriate
                // deallocation function (e.g., fz_free or standard C free if allocated via malloc).
                // Memory layout: contiguous array of len bytes, allocated via Rust's global allocator.
                // The corresponding cleanup should use: Box::from_raw(std::slice::from_raw_parts_mut(ptr, len))
                std::mem::forget(boxed);
                // SAFETY: contents was checked for null above
                unsafe {
                    *contents = ptr;
                }
                return len;
            }
        }
    }
    0
}

/// Check if document has incremental changes since signing.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_signature_incremental_change_since_signing(
    _ctx: ContextHandle,
    doc: DocumentHandle,
    _signature: PdfObjHandle,
) -> i32 {
    let store = DOC_SIGNATURES.lock().unwrap();
    if let Some(sigs) = store.get(&doc) {
        if let Some(sig) = sigs.first() {
            return if sig.incremental_change { 1 } else { 0 };
        }
    }
    0
}

// ============================================================================
// FFI Functions - Signature Verification
// ============================================================================

/// Check signature digest.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_check_digest(
    _ctx: ContextHandle,
    verifier: Handle,
    doc: DocumentHandle,
    _signature: PdfObjHandle,
) -> i32 {
    let store = DOC_SIGNATURES.lock().unwrap();
    if let Some(sigs) = store.get(&doc) {
        if let Some(sig) = sigs.first() {
            if let Some(verifier_arc) = VERIFIERS.get(verifier) {
                let v = verifier_arc.lock().unwrap();
                return v.check_digest(&[], &sig.contents) as i32;
            }
            return sig.digest_status as i32;
        }
    }
    SignatureError::NotSigned as i32
}

/// Check signature certificate.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_check_certificate(
    _ctx: ContextHandle,
    verifier: Handle,
    doc: DocumentHandle,
    _signature: PdfObjHandle,
) -> i32 {
    let store = DOC_SIGNATURES.lock().unwrap();
    if let Some(sigs) = store.get(&doc) {
        if let Some(sig) = sigs.first() {
            if let Some(verifier_arc) = VERIFIERS.get(verifier) {
                let v = verifier_arc.lock().unwrap();
                return v.check_certificate(&sig.contents) as i32;
            }
            return sig.certificate_status as i32;
        }
    }
    SignatureError::NotSigned as i32
}

/// Get signature error description.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_signature_error_description(err: i32) -> *const c_char {
    let error = SignatureError::from_i32(err);
    match error {
        SignatureError::Okay => c"signature is valid".as_ptr(),
        SignatureError::NoSignatures => c"no signatures found".as_ptr(),
        SignatureError::NoCertificate => c"certificate not found".as_ptr(),
        SignatureError::DigestFailure => c"digest verification failed".as_ptr(),
        SignatureError::SelfSigned => c"certificate is self-signed".as_ptr(),
        SignatureError::SelfSignedInChain => c"self-signed certificate in chain".as_ptr(),
        SignatureError::NotTrusted => c"certificate is not trusted".as_ptr(),
        SignatureError::NotSigned => c"document is not signed".as_ptr(),
        SignatureError::Unknown => c"unknown error".as_ptr(),
    }
}

// ============================================================================
// FFI Functions - Distinguished Name
// ============================================================================

/// Get signatory information from signature.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_signature_get_signatory(
    _ctx: ContextHandle,
    verifier: Handle,
    doc: DocumentHandle,
    _signature: PdfObjHandle,
) -> Handle {
    let store = DOC_SIGNATURES.lock().unwrap();
    if let Some(sigs) = store.get(&doc) {
        if let Some(sig) = sigs.first() {
            if let Some(ref dn) = sig.signer_dn {
                return DISTINGUISHED_NAMES.insert(dn.clone());
            }
            // Try to get from verifier
            if let Some(verifier_arc) = VERIFIERS.get(verifier) {
                let v = verifier_arc.lock().unwrap();
                if let Some(dn) = v.get_signatory(&sig.contents) {
                    return DISTINGUISHED_NAMES.insert(dn);
                }
            }
        }
    }
    0
}

/// Drop a distinguished name.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_signature_drop_distinguished_name(_ctx: ContextHandle, dn: Handle) {
    DISTINGUISHED_NAMES.remove(dn);
}

/// Format distinguished name as string.
/// Caller must free the returned string.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_signature_format_distinguished_name(
    _ctx: ContextHandle,
    dn: Handle,
) -> *const c_char {
    if let Some(dn_arc) = DISTINGUISHED_NAMES.get(dn) {
        let dn_guard = dn_arc.lock().unwrap();
        let formatted = dn_guard.format();
        if let Ok(cstr) = CString::new(formatted) {
            return cstr.into_raw();
        }
    }
    ptr::null()
}

/// Get DN component (CN).
#[unsafe(no_mangle)]
pub extern "C" fn pdf_dn_cn(_ctx: ContextHandle, dn: Handle) -> *const c_char {
    if let Some(dn_arc) = DISTINGUISHED_NAMES.get(dn) {
        let dn_guard = dn_arc.lock().unwrap();
        if let Some(ref cn) = dn_guard.cn {
            if let Ok(cstr) = CString::new(cn.clone()) {
                return cstr.into_raw();
            }
        }
    }
    ptr::null()
}

/// Get DN component (O).
#[unsafe(no_mangle)]
pub extern "C" fn pdf_dn_o(_ctx: ContextHandle, dn: Handle) -> *const c_char {
    if let Some(dn_arc) = DISTINGUISHED_NAMES.get(dn) {
        let dn_guard = dn_arc.lock().unwrap();
        if let Some(ref o) = dn_guard.o {
            if let Ok(cstr) = CString::new(o.clone()) {
                return cstr.into_raw();
            }
        }
    }
    ptr::null()
}

/// Get DN component (OU).
#[unsafe(no_mangle)]
pub extern "C" fn pdf_dn_ou(_ctx: ContextHandle, dn: Handle) -> *const c_char {
    if let Some(dn_arc) = DISTINGUISHED_NAMES.get(dn) {
        let dn_guard = dn_arc.lock().unwrap();
        if let Some(ref ou) = dn_guard.ou {
            if let Ok(cstr) = CString::new(ou.clone()) {
                return cstr.into_raw();
            }
        }
    }
    ptr::null()
}

/// Get DN component (email).
#[unsafe(no_mangle)]
pub extern "C" fn pdf_dn_email(_ctx: ContextHandle, dn: Handle) -> *const c_char {
    if let Some(dn_arc) = DISTINGUISHED_NAMES.get(dn) {
        let dn_guard = dn_arc.lock().unwrap();
        if let Some(ref email) = dn_guard.email {
            if let Ok(cstr) = CString::new(email.clone()) {
                return cstr.into_raw();
            }
        }
    }
    ptr::null()
}

/// Get DN component (C - country).
#[unsafe(no_mangle)]
pub extern "C" fn pdf_dn_c(_ctx: ContextHandle, dn: Handle) -> *const c_char {
    if let Some(dn_arc) = DISTINGUISHED_NAMES.get(dn) {
        let dn_guard = dn_arc.lock().unwrap();
        if let Some(ref c) = dn_guard.c {
            if let Ok(cstr) = CString::new(c.clone()) {
                return cstr.into_raw();
            }
        }
    }
    ptr::null()
}

// ============================================================================
// FFI Functions - Signer Management
// ============================================================================

/// Create a new PKCS#7 signer.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_pkcs7_signer_new(_ctx: ContextHandle, cn: *const c_char) -> Handle {
    let cn_str = if !cn.is_null() {
        unsafe { CStr::from_ptr(cn).to_str().unwrap_or("Unknown") }
    } else {
        "Unknown"
    };

    let signer = Pkcs7Signer::new(cn_str);
    SIGNERS.insert(signer)
}

/// Keep (increment reference to) a signer.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_pkcs7_keep_signer(_ctx: ContextHandle, signer: Handle) -> Handle {
    SIGNERS.keep(signer)
}

/// Drop a signer.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_drop_signer(_ctx: ContextHandle, signer: Handle) {
    SIGNERS.remove(signer);
}

/// Get signer's distinguished name.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_pkcs7_signer_get_name(_ctx: ContextHandle, signer: Handle) -> Handle {
    if let Some(signer_arc) = SIGNERS.get(signer) {
        let s = signer_arc.lock().unwrap();
        return DISTINGUISHED_NAMES.insert(s.dn.clone());
    }
    0
}

/// Get signer's max digest size.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_pkcs7_signer_max_digest_size(_ctx: ContextHandle, signer: Handle) -> usize {
    if let Some(signer_arc) = SIGNERS.get(signer) {
        let s = signer_arc.lock().unwrap();
        return s.max_digest_size();
    }
    0
}

// ============================================================================
// FFI Functions - Verifier Management
// ============================================================================

/// Create a new PKCS#7 verifier.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_pkcs7_verifier_new(_ctx: ContextHandle) -> Handle {
    let verifier = Pkcs7Verifier::new();
    VERIFIERS.insert(verifier)
}

/// Drop a verifier.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_drop_verifier(_ctx: ContextHandle, verifier: Handle) {
    VERIFIERS.remove(verifier);
}

/// Add a trusted certificate to verifier.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_pkcs7_verifier_add_cert(
    _ctx: ContextHandle,
    verifier: Handle,
    cert: *const u8,
    len: usize,
) {
    if cert.is_null() || len == 0 {
        return;
    }

    if let Some(verifier_arc) = VERIFIERS.get(verifier) {
        let mut v = verifier_arc.lock().unwrap();
        let cert_data = unsafe { std::slice::from_raw_parts(cert, len).to_vec() };
        v.add_trusted_cert(cert_data);
    }
}

// ============================================================================
// FFI Functions - Signing Operations
// ============================================================================

/// Sign a signature field.
///
/// Parses the `reason` and `location` C strings and includes them in the
/// stored `SignatureInfo`, so they appear when the signature is later
/// queried or formatted.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_sign_signature(
    _ctx: ContextHandle,
    _widget: AnnotHandle,
    signer: Handle,
    date: i64,
    reason: *const c_char,
    location: *const c_char,
) {
    if let Some(signer_arc) = SIGNERS.get(signer) {
        let s = signer_arc.lock().unwrap();

        // Parse reason from C string
        let reason_str = if !reason.is_null() {
            let cstr = unsafe { CStr::from_ptr(reason) };
            cstr.to_str()
                .ok()
                .filter(|s| !s.is_empty())
                .map(String::from)
        } else {
            None
        };

        // Parse location from C string
        let location_str = if !location.is_null() {
            let cstr = unsafe { CStr::from_ptr(location) };
            cstr.to_str()
                .ok()
                .filter(|s| !s.is_empty())
                .map(String::from)
        } else {
            None
        };

        // Create signature info with actual reason/location
        let sig_info = SignatureInfo {
            is_signed: true,
            signer_dn: Some(s.dn.clone()),
            reason: reason_str,
            location: location_str,
            date,
            byte_ranges: vec![ByteRange {
                offset: 0,
                length: 0,
            }],
            contents: s.create_digest(&[]),
            incremental_change: false,
            digest_status: SignatureError::Okay,
            certificate_status: SignatureError::Okay,
        };

        // Index by widget handle — each widget corresponds to one signature field
        let mut store = DOC_SIGNATURES.lock().unwrap();
        store.entry(_widget).or_default().push(sig_info);
    }
}

/// Clear a signature from a widget.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_clear_signature(_ctx: ContextHandle, widget: AnnotHandle) {
    let mut store = DOC_SIGNATURES.lock().unwrap();
    store.remove(&widget);
}

/// Set signature value on a field.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_signature_set_value(
    _ctx: ContextHandle,
    doc: DocumentHandle,
    _field: PdfObjHandle,
    signer: Handle,
    stime: i64,
) {
    if let Some(signer_arc) = SIGNERS.get(signer) {
        let s = signer_arc.lock().unwrap();

        let sig_info = SignatureInfo {
            is_signed: true,
            signer_dn: Some(s.dn.clone()),
            reason: None,
            location: None,
            date: stime,
            byte_ranges: Vec::new(),
            contents: s.create_digest(&[]),
            incremental_change: false,
            digest_status: SignatureError::Okay,
            certificate_status: SignatureError::Okay,
        };

        let mut store = DOC_SIGNATURES.lock().unwrap();
        store.entry(doc).or_default().push(sig_info);
    }
}

// ============================================================================
// FFI Functions - Signature Info Formatting
// ============================================================================

/// Format signature info as string.
/// Caller must free the returned string.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_signature_info(
    _ctx: ContextHandle,
    name: *const c_char,
    dn: Handle,
    reason: *const c_char,
    location: *const c_char,
    date: i64,
    include_labels: i32,
) -> *const c_char {
    let mut parts = Vec::new();

    // Name
    if !name.is_null() {
        let name_str = unsafe { CStr::from_ptr(name).to_str().unwrap_or("") };
        if !name_str.is_empty() {
            if include_labels != 0 {
                parts.push(format!("Signed by: {}", name_str));
            } else {
                parts.push(name_str.to_string());
            }
        }
    }

    // DN
    if dn != 0 {
        if let Some(dn_arc) = DISTINGUISHED_NAMES.get(dn) {
            let dn_guard = dn_arc.lock().unwrap();
            let dn_str = dn_guard.format();
            if !dn_str.is_empty() {
                if include_labels != 0 {
                    parts.push(format!("DN: {}", dn_str));
                } else {
                    parts.push(dn_str);
                }
            }
        }
    }

    // Reason
    if !reason.is_null() {
        let reason_str = unsafe { CStr::from_ptr(reason).to_str().unwrap_or("") };
        if !reason_str.is_empty() {
            if include_labels != 0 {
                parts.push(format!("Reason: {}", reason_str));
            } else {
                parts.push(reason_str.to_string());
            }
        }
    }

    // Location
    if !location.is_null() {
        let loc_str = unsafe { CStr::from_ptr(location).to_str().unwrap_or("") };
        if !loc_str.is_empty() {
            if include_labels != 0 {
                parts.push(format!("Location: {}", loc_str));
            } else {
                parts.push(loc_str.to_string());
            }
        }
    }

    // Date
    if date != 0 {
        if include_labels != 0 {
            parts.push(format!("Date: {}", date));
        } else {
            parts.push(format!("{}", date));
        }
    }

    let result = parts.join("\n");
    if let Ok(cstr) = CString::new(result) {
        return cstr.into_raw();
    }
    ptr::null()
}

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

// ============================================================================
// FFI Functions - Additional Signature Management
// ============================================================================

/// Add a signature to document.
///
/// Creates a `Pkcs7Signer` with the given CN and uses `create_digest`
/// to produce a real PKCS#7 structure (with SHA-256 hash of the
/// document handle bytes as representative data) rather than returning
/// zero-filled placeholder bytes.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_add_signature(
    _ctx: ContextHandle,
    doc: DocumentHandle,
    cn: *const c_char,
    date: i64,
) -> i32 {
    let cn_str = if !cn.is_null() {
        unsafe { CStr::from_ptr(cn).to_str().unwrap_or("Unknown") }
    } else {
        "Unknown"
    };

    // Build representative data from the document handle and timestamp
    // so each signature gets a unique, non-zero digest.
    let mut doc_data = Vec::new();
    doc_data.extend_from_slice(&doc.to_le_bytes());
    doc_data.extend_from_slice(&date.to_le_bytes());
    doc_data.extend_from_slice(cn_str.as_bytes());

    // Use a real signer to produce PKCS#7 bytes
    let signer = Pkcs7Signer::new(cn_str);
    let contents = signer.create_digest(&doc_data);

    let sig_info = SignatureInfo {
        is_signed: true,
        signer_dn: Some(DistinguishedName {
            cn: Some(cn_str.to_string()),
            ..Default::default()
        }),
        reason: None,
        location: None,
        date,
        byte_ranges: vec![
            ByteRange {
                offset: 0,
                length: 1000,
            },
            ByteRange {
                offset: 2000,
                length: 3000,
            },
        ],
        contents,
        incremental_change: false,
        digest_status: SignatureError::Okay,
        certificate_status: SignatureError::Okay,
    };

    let mut store = DOC_SIGNATURES.lock().unwrap();
    let sigs = store.entry(doc).or_default();
    let idx = sigs.len() as i32;
    sigs.push(sig_info);
    idx
}

/// Get signature at index.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_get_signature(
    _ctx: ContextHandle,
    doc: DocumentHandle,
    index: i32,
) -> Handle {
    let store = DOC_SIGNATURES.lock().unwrap();
    if let Some(sigs) = store.get(&doc) {
        if let Some(sig) = sigs.get(index as usize) {
            return SIGNATURE_INFOS.insert(sig.clone());
        }
    }
    0
}

/// Drop a signature info handle.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_drop_signature_info(_ctx: ContextHandle, sig: Handle) {
    SIGNATURE_INFOS.remove(sig);
}

/// Clear all signatures from document.
#[unsafe(no_mangle)]
pub extern "C" fn pdf_clear_all_signatures(_ctx: ContextHandle, doc: DocumentHandle) {
    let mut store = DOC_SIGNATURES.lock().unwrap();
    store.remove(&doc);
}

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

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

    #[test]
    fn test_signature_error_types() {
        assert_eq!(SignatureError::from_i32(0), SignatureError::Okay);
        assert_eq!(SignatureError::from_i32(3), SignatureError::DigestFailure);
        assert_eq!(SignatureError::from_i32(99), SignatureError::Unknown);

        assert_eq!(SignatureError::Okay.description(), "signature is valid");
        assert_eq!(
            SignatureError::DigestFailure.description(),
            "digest verification failed"
        );
    }

    #[test]
    fn test_distinguished_name() {
        let dn = DistinguishedName {
            cn: Some("John Doe".to_string()),
            o: Some("ACME Corp".to_string()),
            ou: Some("Engineering".to_string()),
            email: Some("john@acme.com".to_string()),
            c: Some("US".to_string()),
        };

        let formatted = dn.format();
        assert!(formatted.contains("CN=John Doe"));
        assert!(formatted.contains("O=ACME Corp"));
        assert!(formatted.contains("OU=Engineering"));
        assert!(formatted.contains("EMAIL=john@acme.com"));
        assert!(formatted.contains("C=US"));
    }

    #[test]
    fn test_pkcs7_signer() {
        let signer = Pkcs7Signer::new("Test Signer");
        assert_eq!(signer.dn.cn, Some("Test Signer".to_string()));
        assert_eq!(signer.max_digest_size(), 8192);

        let digest = signer.create_digest(&[1, 2, 3, 4]);
        // Output is padded to max_digest_size
        assert_eq!(digest.len(), 8192);
        // Must start with ASN.1 SEQUENCE tag (valid PKCS#7)
        assert_eq!(digest[0], 0x30);
        // Must contain the signedData OID
        let signed_data_oid: &[u8] = &[
            0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02,
        ];
        assert!(
            digest
                .windows(signed_data_oid.len())
                .any(|w| w == signed_data_oid)
        );
    }

    #[test]
    fn test_pkcs7_signer_different_data_different_digest() {
        let signer = Pkcs7Signer::new("Signer");
        let d1 = signer.create_digest(&[1, 2, 3]);
        let d2 = signer.create_digest(&[4, 5, 6]);
        // Different inputs must produce different digests
        assert_ne!(d1, d2);
    }

    #[test]
    fn test_pkcs7_verifier() {
        let mut verifier = Pkcs7Verifier::new();
        assert!(verifier.trusted_certs.is_empty());

        verifier.add_trusted_cert(vec![1, 2, 3, 4]);
        assert_eq!(verifier.trusted_certs.len(), 1);

        // Empty signature should be rejected
        assert_eq!(
            verifier.check_certificate(&[]),
            SignatureError::NoCertificate
        );
        assert_eq!(
            verifier.check_digest(&[], &[]),
            SignatureError::DigestFailure
        );
    }

    #[test]
    fn test_pkcs7_verifier_with_real_digest() {
        let verifier = Pkcs7Verifier::new();
        let signer = Pkcs7Signer::new("Test");
        let data = b"hello world";
        let signature = signer.create_digest(data);

        // The digest produced by our signer should verify against
        // the same input data
        assert_eq!(
            verifier.check_digest(data, &signature),
            SignatureError::Okay
        );
        // Different data should fail verification
        assert_eq!(
            verifier.check_digest(b"wrong data", &signature),
            SignatureError::DigestFailure
        );
    }

    #[test]
    fn test_pkcs7_verifier_certificate_checks() {
        let verifier = Pkcs7Verifier::new();

        // Non-SEQUENCE tag -> NoCertificate
        assert_eq!(
            verifier.check_certificate(&[0x01, 0x02]),
            SignatureError::NoCertificate
        );

        // Valid PKCS#7 from our signer should be parseable
        let signer = Pkcs7Signer::new("Test");
        let sig = signer.create_digest(b"test");
        let result = verifier.check_certificate(&sig);
        // Our signer produces a single cert (self-signed)
        assert_eq!(result, SignatureError::SelfSigned);
    }

    #[test]
    fn test_pkcs7_verifier_get_signatory_empty() {
        let verifier = Pkcs7Verifier::new();
        assert!(verifier.get_signatory(&[]).is_none());
        assert!(verifier.get_signatory(&[0x01]).is_none());
    }

    #[test]
    fn test_signature_info() {
        let info = SignatureInfo::new();
        assert!(!info.is_signed);
        assert!(info.signer_dn.is_none());
        assert_eq!(info.digest_status, SignatureError::NotSigned);
    }

    #[test]
    fn test_ffi_signer_lifecycle() {
        let ctx = 0;
        let cn = CString::new("Test").unwrap();

        let signer = pdf_pkcs7_signer_new(ctx, cn.as_ptr());
        assert!(signer > 0);

        let max_size = pdf_pkcs7_signer_max_digest_size(ctx, signer);
        assert_eq!(max_size, 8192);

        let dn = pdf_pkcs7_signer_get_name(ctx, signer);
        assert!(dn > 0);

        pdf_signature_drop_distinguished_name(ctx, dn);
        pdf_drop_signer(ctx, signer);
    }

    #[test]
    fn test_ffi_verifier_lifecycle() {
        let ctx = 0;

        let verifier = pdf_pkcs7_verifier_new(ctx);
        assert!(verifier > 0);

        let cert = vec![1u8, 2, 3, 4];
        pdf_pkcs7_verifier_add_cert(ctx, verifier, cert.as_ptr(), cert.len());

        pdf_drop_verifier(ctx, verifier);
    }

    #[test]
    fn test_ffi_add_signature() {
        let ctx = 0;
        let doc: DocumentHandle = 888;
        let cn = CString::new("Signer").unwrap();

        // Add signature
        let idx = pdf_add_signature(ctx, doc, cn.as_ptr(), 1234567890);
        assert_eq!(idx, 0);

        // Count signatures
        assert_eq!(pdf_count_signatures(ctx, doc), 1);

        // Check if signed
        assert_eq!(pdf_signature_is_signed(ctx, doc, 0), 1);

        // Check incremental change
        assert_eq!(
            pdf_signature_incremental_change_since_signing(ctx, doc, 0),
            0
        );

        // Get signatory
        let verifier = pdf_pkcs7_verifier_new(ctx);
        let dn = pdf_signature_get_signatory(ctx, verifier, doc, 0);
        assert!(dn > 0);

        // Format DN
        let formatted = pdf_signature_format_distinguished_name(ctx, dn);
        assert!(!formatted.is_null());
        unsafe {
            let s = CStr::from_ptr(formatted).to_str().unwrap();
            assert!(s.contains("CN=Signer"));
            pdf_signature_free_string(ctx, formatted as *mut c_char);
        }

        pdf_signature_drop_distinguished_name(ctx, dn);
        pdf_drop_verifier(ctx, verifier);

        // Clear signatures
        pdf_clear_all_signatures(ctx, doc);
        assert_eq!(pdf_count_signatures(ctx, doc), 0);
    }

    #[test]
    fn test_ffi_signature_error_description() {
        let desc = pdf_signature_error_description(SignatureError::Okay as i32);
        assert!(!desc.is_null());
        let s = unsafe { CStr::from_ptr(desc).to_str().unwrap() };
        assert_eq!(s, "signature is valid");

        let desc2 = pdf_signature_error_description(SignatureError::DigestFailure as i32);
        let s2 = unsafe { CStr::from_ptr(desc2).to_str().unwrap() };
        assert_eq!(s2, "digest verification failed");
    }

    #[test]
    fn test_ffi_signature_info_format() {
        let ctx = 0;
        let name = CString::new("John Doe").unwrap();
        let reason = CString::new("Approved").unwrap();
        let location = CString::new("New York").unwrap();

        // Create a DN
        let dn_handle = DISTINGUISHED_NAMES.insert(DistinguishedName {
            cn: Some("John Doe".to_string()),
            o: Some("ACME".to_string()),
            ..Default::default()
        });

        let info = pdf_signature_info(
            ctx,
            name.as_ptr(),
            dn_handle,
            reason.as_ptr(),
            location.as_ptr(),
            1234567890,
            1, // include labels
        );

        assert!(!info.is_null());
        unsafe {
            let s = CStr::from_ptr(info).to_str().unwrap();
            assert!(s.contains("Signed by: John Doe"));
            assert!(s.contains("Reason: Approved"));
            assert!(s.contains("Location: New York"));
            pdf_signature_free_string(ctx, info as *mut c_char);
        }

        DISTINGUISHED_NAMES.remove(dn_handle);
    }

    #[test]
    fn test_byte_range() {
        let br = ByteRange {
            offset: 100,
            length: 500,
        };
        assert_eq!(br.offset, 100);
        assert_eq!(br.length, 500);
    }

    #[test]
    fn test_dn_components() {
        let ctx = 0;
        let dn_handle = DISTINGUISHED_NAMES.insert(DistinguishedName {
            cn: Some("Test CN".to_string()),
            o: Some("Test O".to_string()),
            ou: Some("Test OU".to_string()),
            email: Some("test@test.com".to_string()),
            c: Some("US".to_string()),
        });

        let cn = pdf_dn_cn(ctx, dn_handle);
        assert!(!cn.is_null());
        unsafe {
            assert_eq!(CStr::from_ptr(cn).to_str().unwrap(), "Test CN");
            pdf_signature_free_string(ctx, cn as *mut c_char);
        }

        let o = pdf_dn_o(ctx, dn_handle);
        assert!(!o.is_null());
        unsafe {
            assert_eq!(CStr::from_ptr(o).to_str().unwrap(), "Test O");
            pdf_signature_free_string(ctx, o as *mut c_char);
        }

        DISTINGUISHED_NAMES.remove(dn_handle);
    }

    #[test]
    fn test_pdf_sign_signature_reason_location() {
        let ctx = 0;
        let cn = CString::new("Signer").unwrap();
        let signer = pdf_pkcs7_signer_new(ctx, cn.as_ptr());

        let reason = CString::new("Approval").unwrap();
        let location = CString::new("Office").unwrap();
        let widget: AnnotHandle = 55555;

        pdf_sign_signature(
            ctx,
            widget,
            signer,
            1700000000,
            reason.as_ptr(),
            location.as_ptr(),
        );

        // Retrieve stored signature and verify reason/location
        let store = DOC_SIGNATURES.lock().unwrap();
        let sigs = store.get(&widget).expect("signature should be stored");
        assert_eq!(sigs.len(), 1);
        assert_eq!(sigs[0].reason, Some("Approval".to_string()));
        assert_eq!(sigs[0].location, Some("Office".to_string()));
        assert!(sigs[0].is_signed);
        assert_eq!(sigs[0].date, 1700000000);
        // Contents should start with SEQUENCE tag (valid PKCS#7)
        assert_eq!(sigs[0].contents[0], 0x30);
        drop(store);

        pdf_clear_signature(ctx, widget);
        pdf_drop_signer(ctx, signer);
    }

    #[test]
    fn test_pdf_sign_signature_null_reason_location() {
        let ctx = 0;
        let cn = CString::new("Signer").unwrap();
        let signer = pdf_pkcs7_signer_new(ctx, cn.as_ptr());
        let widget: AnnotHandle = 55556;

        pdf_sign_signature(ctx, widget, signer, 1700000000, ptr::null(), ptr::null());

        let store = DOC_SIGNATURES.lock().unwrap();
        let sigs = store.get(&widget).expect("signature should be stored");
        assert_eq!(sigs[0].reason, None);
        assert_eq!(sigs[0].location, None);
        drop(store);

        pdf_clear_signature(ctx, widget);
        pdf_drop_signer(ctx, signer);
    }

    #[test]
    fn test_pdf_add_signature_real_contents() {
        let ctx = 0;
        let doc: DocumentHandle = 77777;
        let cn = CString::new("RealSigner").unwrap();

        let idx = pdf_add_signature(ctx, doc, cn.as_ptr(), 1700000000);
        assert_eq!(idx, 0);

        let store = DOC_SIGNATURES.lock().unwrap();
        let sigs = store.get(&doc).expect("signature should be stored");
        let contents = &sigs[0].contents;

        // Must start with ASN.1 SEQUENCE (valid PKCS#7)
        assert_eq!(contents[0], 0x30);
        // Must not be all zeros (the old placeholder behavior)
        assert!(contents.iter().any(|&b| b != 0));
        drop(store);

        pdf_clear_all_signatures(ctx, doc);
    }

    #[test]
    fn test_encode_der_length_values() {
        assert_eq!(encode_der_length(0), vec![0x00]);
        assert_eq!(encode_der_length(127), vec![0x7F]);
        assert_eq!(encode_der_length(128), vec![0x81, 0x80]);
        assert_eq!(encode_der_length(255), vec![0x81, 0xFF]);
        assert_eq!(encode_der_length(256), vec![0x82, 0x01, 0x00]);
        assert_eq!(encode_der_length(65535), vec![0x82, 0xFF, 0xFF]);
    }
}
