pub use crate::data_model::common::{Asn1TimeEncoding, Asn1TimeUtc, BigUnsigned};
use crate::data_model::oid::{
    OID_AUTHORITY_KEY_IDENTIFIER, OID_AUTHORITY_KEY_IDENTIFIER_RAW, OID_CRL_NUMBER,
    OID_CRL_NUMBER_RAW, OID_SHA256_WITH_RSA_ENCRYPTION, OID_SHA256_WITH_RSA_ENCRYPTION_RAW,
    OID_SUBJECT_KEY_IDENTIFIER_RAW,
};
use x509_parser::certificate::X509Certificate;
use x509_parser::extensions::{ParsedExtension, X509Extension};
use x509_parser::prelude::{FromDer, X509Version};
use x509_parser::revocation_list::CertificateRevocationList;
use x509_parser::x509::{AlgorithmIdentifier, SubjectPublicKeyInfo};
use x509_parser::{asn1_rs::Class as Asn1Class, asn1_rs::Tag as Asn1Tag};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RevokedCert {
    pub serial_number: BigUnsigned,
    pub revocation_date: Asn1TimeUtc,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CrlExtensions {
    pub authority_key_identifier: Vec<u8>,
    pub crl_number: BigUnsigned,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RpkixCrl {
    pub raw_der: Vec<u8>,
    pub version: u32,
    pub issuer_dn: String,
    pub signature_algorithm_oid: String,
    pub this_update: Asn1TimeUtc,
    pub next_update: Asn1TimeUtc,
    pub revoked_certs: Vec<RevokedCert>,
    pub extensions: CrlExtensions,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RpkixCrlParsed {
    pub raw_der: Vec<u8>,
    pub version: Option<X509Version>,
    pub issuer_dn: String,
    pub signature_algorithm: AlgorithmIdentifierValue,
    pub tbs_signature_algorithm: AlgorithmIdentifierValue,
    pub this_update: Asn1TimeUtc,
    pub next_update: Option<Asn1TimeUtc>,
    pub revoked_certs: Vec<RevokedCertParsed>,
    pub extensions: Vec<CrlExtensionParsed>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RevokedCertParsed {
    pub serial_number: BigUnsigned,
    pub revocation_date: Asn1TimeUtc,
    pub has_extensions: bool,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CrlExtensionParsed {
    AuthorityKeyIdentifier {
        key_identifier: Option<Vec<u8>>,
        has_other_fields: bool,
        critical: bool,
    },
    CrlNumber {
        number: der_parser::num_bigint::BigUint,
        critical: bool,
    },
    Other {
        oid: String,
        critical: bool,
    },
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AlgorithmIdentifierValue {
    pub oid: String,
    pub parameters: Option<AlgorithmParametersValue>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AlgorithmParametersValue {
    pub class: Asn1Class,
    pub tag: Asn1Tag,
    pub data: Vec<u8>,
}

impl AlgorithmIdentifierValue {
    pub fn params_absent_or_null(&self) -> bool {
        match &self.parameters {
            None => true,
            Some(p) if p.class == Asn1Class::Universal && p.tag == Asn1Tag::Null => true,
            Some(_p) => false,
        }
    }
}

#[derive(Debug, thiserror::Error)]
pub enum CrlParseError {
    #[error("X.509 CRL parse error: {0} (RFC 5280 §5.1; RFC 6487 §5)")]
    Parse(String),

    #[error("trailing bytes after CRL DER: {0} bytes (DER; RFC 5280 §5.1)")]
    TrailingBytes(usize),
}

#[derive(Debug, thiserror::Error)]
pub enum CrlProfileError {
    #[error("CRL version must be v2, got {0:?} (RFC 5280 §5.1; RFC 6487 §5)")]
    InvalidVersion(Option<u32>),

    #[error("CRL signatureAlgorithm must match TBSCertList.signature (RFC 5280 §5.1)")]
    SignatureAlgorithmMismatch,

    #[error(
        "CRL signatureAlgorithm must be sha256WithRSAEncryption ({OID_SHA256_WITH_RSA_ENCRYPTION}), got {0} (RFC 6487 §5; RFC 7935 §2)"
    )]
    InvalidSignatureAlgorithm(String),

    #[error(
        "CRL signature algorithm parameters must be absent or NULL (RFC 5280 §4.1.1.2; RFC 7935 §2)"
    )]
    InvalidSignatureAlgorithmParameters,

    #[error("CRL extensions must be exactly two (AKI + CRLNumber), got {0} (RFC 9829 §3.1)")]
    InvalidExtensionsCount(usize),

    #[error("unsupported CRL extension OID {0} (RFC 9829 §3.1)")]
    UnsupportedExtension(String),

    #[error("duplicate CRL extension OID {0} (RFC 5280 §4.2; RFC 9829 §3.1)")]
    DuplicateExtension(String),

    #[error("AuthorityKeyIdentifier CRL extension missing (RFC 9829 §3.1; RFC 5280 §5.2.1)")]
    AkiMissing,

    #[error("AuthorityKeyIdentifier must contain keyIdentifier (RFC 5280 §5.2.1; RFC 9829 §3.1)")]
    AkiMissingKeyIdentifier,

    #[error(
        "AuthorityKeyIdentifier must not contain authorityCertIssuer or authorityCertSerialNumber (RFC 5280 §5.2.1; RFC 9829 §3.1)"
    )]
    AkiHasOtherFields,

    #[error("CRLNumber CRL extension missing (RFC 9829 §3.1; RFC 5280 §5.2.3)")]
    CrlNumberMissing,

    #[error("CRLNumber must be non-critical (RFC 9829 §3.1; RFC 5280 §5.2.3)")]
    CrlNumberCritical,

    #[error("CRLNumber out of range (must fit in 0..2^159-1) (RFC 9829 §3.1)")]
    CrlNumberOutOfRange,

    #[error("CRL entry extensions must not be present (RFC 6487 §5; RFC 5280 §5.1)")]
    EntryExtensionsNotAllowed,

    #[error("CRL nextUpdate must be present (RFC 5280 §5.1.2.5; RFC 6487 §5)")]
    NextUpdateMissing,

    #[error(
        "{field} time encoding invalid for year {year}: got {encoding:?} (RFC 5280 §5.1.2.4-§5.1.2.6)"
    )]
    InvalidTimeEncoding {
        field: &'static str,
        year: i32,
        encoding: Asn1TimeEncoding,
    },
}

#[derive(Debug, thiserror::Error)]
pub enum CrlDecodeError {
    #[error("{0}")]
    Parse(#[from] CrlParseError),

    #[error("{0}")]
    Validate(#[from] CrlProfileError),
}

impl RpkixCrl {
    /// Parse step of scheme A (`parse → validate → verify`).
    pub fn parse_der(der: &[u8]) -> Result<RpkixCrlParsed, CrlParseError> {
        let (rem, crl) = CertificateRevocationList::from_der(der)
            .map_err(|e| CrlParseError::Parse(e.to_string()))?;
        if !rem.is_empty() {
            return Err(CrlParseError::TrailingBytes(rem.len()));
        }

        let revoked_certs = crl
            .iter_revoked_certificates()
            .map(|rc| RevokedCertParsed {
                serial_number: BigUnsigned::from_biguint(rc.serial()),
                revocation_date: crate::data_model::common::asn1_time_to_model(rc.revocation_date),
                has_extensions: !rc.extensions().is_empty(),
            })
            .collect::<Vec<_>>();

        let this_update = crate::data_model::common::asn1_time_to_model(crl.last_update());
        let next_update = crl
            .next_update()
            .map(crate::data_model::common::asn1_time_to_model);

        let extensions =
            parse_extensions_parse(crl.extensions()).map_err(|e| CrlParseError::Parse(e))?;

        Ok(RpkixCrlParsed {
            raw_der: der.to_vec(),
            version: crl.version(),
            issuer_dn: crl.issuer().to_string(),
            signature_algorithm: algorithm_identifier_value(&crl.signature_algorithm),
            tbs_signature_algorithm: algorithm_identifier_value(&crl.tbs_cert_list.signature),
            this_update,
            next_update,
            revoked_certs,
            extensions,
        })
    }

    /// Decode a DER-encoded X.509 v2 CRL and enforce the RPKI profile constraints from
    /// `specs/prepare/data_models/04_crl.md` (RFC 6487 §5; RFC 9829 §3.1; RFC 5280 §5.1).
    pub fn decode_der(der: &[u8]) -> Result<Self, CrlDecodeError> {
        Ok(Self::parse_der(der)?.validate_profile()?)
    }

    /// Profile validate step of scheme A (`parse → validate → verify`).
    ///
    /// `RpkixCrl` is already profile-validated when constructed via `decode_der()` /
    /// `RpkixCrlParsed::validate_profile()`.
    pub fn validate_profile(&self) -> Result<(), CrlProfileError> {
        Ok(())
    }

    /// Verify the cryptographic signature on this CRL using the issuer certificate.
    ///
    /// Signature verification needs the issuer public key (RFC 5280 §6.3.3 (f)-(g)).
    /// In RPKI practice, this public key is obtained from the CRL issuer CA certificate
    /// (and that certificate must already be validated up to the same trust anchor).
    ///
    /// This helper also performs common binding checks:
    /// - CRL `issuer_dn` must equal issuer certificate `subject`
    /// - if issuer KeyUsage is present, require `cRLSign`
    /// - if issuer SKI is present, require it matches CRL AKI.keyIdentifier
    pub fn verify_signature_with_issuer_certificate_der(
        &self,
        issuer_cert_der: &[u8],
    ) -> Result<(), CrlVerifyError> {
        let (rem, issuer_cert) = X509Certificate::from_der(issuer_cert_der)
            .map_err(|e| CrlVerifyError::IssuerCertificateParse(e.to_string()))?;
        if !rem.is_empty() {
            return Err(CrlVerifyError::IssuerCertificateTrailingBytes(rem.len()));
        }

        let subject_dn = issuer_cert.subject().to_string();
        if subject_dn != self.issuer_dn {
            return Err(CrlVerifyError::IssuerSubjectMismatch {
                crl_issuer_dn: self.issuer_dn.clone(),
                issuer_subject_dn: subject_dn,
            });
        }

        if let Some(ku) = issuer_cert
            .key_usage()
            .map_err(|e| CrlVerifyError::IssuerCertificateParse(e.to_string()))?
        {
            if !ku.value.crl_sign() {
                return Err(CrlVerifyError::IssuerKeyUsageMissingCrlSign);
            }
        }

        if let Some(issuer_ski) = get_subject_key_identifier(&issuer_cert) {
            if issuer_ski != self.extensions.authority_key_identifier {
                return Err(CrlVerifyError::AkiSkiMismatch);
            }
        }

        self.verify_signature_with_issuer_spki(issuer_cert.public_key())
    }

    /// Verify the cryptographic signature on this CRL using the issuer SubjectPublicKeyInfo.
    pub fn verify_signature_with_issuer_spki(
        &self,
        issuer_spki: &SubjectPublicKeyInfo<'_>,
    ) -> Result<(), CrlVerifyError> {
        let (rem, crl) = CertificateRevocationList::from_der(&self.raw_der)
            .map_err(|e| CrlVerifyError::CrlParse(e.to_string()))?;
        if !rem.is_empty() {
            return Err(CrlVerifyError::CrlTrailingBytes(rem.len()));
        }
        crate::crypto_sig_cache::verify_with_cache(
            crate::crypto_sig_cache::CryptoSigVerifyPoint::Crl,
            crl.tbs_cert_list.as_ref(),
            crl.signature_value.data.as_ref(),
            issuer_spki.raw,
            || {
                crl.verify_signature(issuer_spki)
                    .map_err(|e| CrlVerifyError::InvalidSignature(e.to_string()))
            },
        )
    }

    /// Verify the cryptographic signature on this CRL using a DER-encoded SubjectPublicKeyInfo.
    pub fn verify_signature_with_issuer_spki_der(
        &self,
        issuer_spki_der: &[u8],
    ) -> Result<(), CrlVerifyError> {
        let (rem, spki) = SubjectPublicKeyInfo::from_der(issuer_spki_der)
            .map_err(|e| CrlVerifyError::IssuerSpkiParse(e.to_string()))?;
        if !rem.is_empty() {
            return Err(CrlVerifyError::IssuerSpkiTrailingBytes(rem.len()));
        }
        self.verify_signature_with_issuer_spki(&spki)
    }
}

impl RpkixCrlParsed {
    /// Profile validate step of scheme A (`parse → validate → verify`).
    pub fn validate_profile(self) -> Result<RpkixCrl, CrlProfileError> {
        let version = match self.version {
            Some(X509Version::V2) => 2,
            Some(v) => return Err(CrlProfileError::InvalidVersion(Some(v.0))),
            None => return Err(CrlProfileError::InvalidVersion(None)),
        };

        // signatureAlgorithm must match tbsCertList.signature
        if self.signature_algorithm != self.tbs_signature_algorithm {
            return Err(CrlProfileError::SignatureAlgorithmMismatch);
        }
        let sig_oid = self.signature_algorithm.oid.clone();
        if sig_oid != OID_SHA256_WITH_RSA_ENCRYPTION {
            return Err(CrlProfileError::InvalidSignatureAlgorithm(sig_oid));
        }
        if !self.signature_algorithm.params_absent_or_null() {
            return Err(CrlProfileError::InvalidSignatureAlgorithmParameters);
        }

        let extensions = validate_extensions_profile(&self.extensions)?;

        let mut revoked_out = Vec::with_capacity(self.revoked_certs.len());
        for rc in self.revoked_certs {
            if rc.has_extensions {
                return Err(CrlProfileError::EntryExtensionsNotAllowed);
            }
            validate_time_encoding_rfc5280("revocationDate", &rc.revocation_date)?;
            revoked_out.push(RevokedCert {
                serial_number: rc.serial_number,
                revocation_date: rc.revocation_date,
            });
        }

        validate_time_encoding_rfc5280("thisUpdate", &self.this_update)?;

        let next_update = self.next_update.ok_or(CrlProfileError::NextUpdateMissing)?;
        validate_time_encoding_rfc5280("nextUpdate", &next_update)?;

        Ok(RpkixCrl {
            raw_der: self.raw_der,
            version,
            issuer_dn: self.issuer_dn,
            signature_algorithm_oid: OID_SHA256_WITH_RSA_ENCRYPTION.to_string(),
            this_update: self.this_update,
            next_update,
            revoked_certs: revoked_out,
            extensions,
        })
    }
}

#[derive(Debug, thiserror::Error)]
pub enum CrlVerifyError {
    #[error("issuer certificate parse error: {0} (RFC 5280 §4.1; RFC 6487 §4)")]
    IssuerCertificateParse(String),

    #[error("trailing bytes after issuer certificate DER: {0} bytes (DER; RFC 5280 §4.1)")]
    IssuerCertificateTrailingBytes(usize),

    #[error("issuer SubjectPublicKeyInfo parse error: {0} (RFC 5280 §4.1.2.7)")]
    IssuerSpkiParse(String),

    #[error(
        "trailing bytes after issuer SubjectPublicKeyInfo DER: {0} bytes (DER; RFC 5280 §4.1.2.7)"
    )]
    IssuerSpkiTrailingBytes(usize),

    #[error("CRL parse error: {0} (RFC 5280 §5.1; RFC 6487 §5)")]
    CrlParse(String),

    #[error("trailing bytes after CRL DER: {0} bytes (DER; RFC 5280 §5.1)")]
    CrlTrailingBytes(usize),

    #[error(
        "CRL issuer DN does not match issuer certificate subject (RFC 5280 §5.1; RFC 5280 §6.3.3(b))"
    )]
    IssuerSubjectMismatch {
        crl_issuer_dn: String,
        issuer_subject_dn: String,
    },

    #[error(
        "issuer certificate keyUsage present but missing cRLSign (RFC 5280 §4.2.1.3; RFC 5280 §6.3.3(f))"
    )]
    IssuerKeyUsageMissingCrlSign,

    #[error(
        "CRL AKI.keyIdentifier does not match issuer certificate SKI (RFC 5280 §4.2.1.1; RFC 5280 §4.2.1.2; RFC 5280 §6.3.3(c)/(f))"
    )]
    AkiSkiMismatch,

    #[error("CRL signature verification failed: {0} (RFC 5280 §6.3.3(g); RFC 7935 §2)")]
    InvalidSignature(String),
}

fn validate_time_encoding_rfc5280(
    field: &'static str,
    t: &Asn1TimeUtc,
) -> Result<(), CrlProfileError> {
    let year = t.utc.year();
    let expected = if year <= 2049 {
        Asn1TimeEncoding::UtcTime
    } else {
        Asn1TimeEncoding::GeneralizedTime
    };
    if t.encoding != expected {
        return Err(CrlProfileError::InvalidTimeEncoding {
            field,
            year,
            encoding: t.encoding,
        });
    }
    Ok(())
}

fn algorithm_identifier_value(ai: &AlgorithmIdentifier<'_>) -> AlgorithmIdentifierValue {
    let parameters = ai.parameters.as_ref().map(|p| AlgorithmParametersValue {
        class: p.class(),
        tag: p.tag(),
        data: p.as_bytes().to_vec(),
    });
    // NOTE(perf): Avoid `to_id_string()` allocations for the signature algorithms we expect
    // in RPKI CRLs. Fall back to `to_id_string()` for unexpected algorithms (mostly error paths).
    let oid = if ai.algorithm.as_bytes() == OID_SHA256_WITH_RSA_ENCRYPTION_RAW {
        OID_SHA256_WITH_RSA_ENCRYPTION.to_string()
    } else {
        ai.algorithm.to_id_string()
    };
    AlgorithmIdentifierValue { oid, parameters }
}

fn parse_extensions_parse(exts: &[X509Extension<'_>]) -> Result<Vec<CrlExtensionParsed>, String> {
    let mut out = Vec::with_capacity(exts.len());
    for ext in exts {
        let oid = ext.oid.as_bytes();
        if oid == OID_AUTHORITY_KEY_IDENTIFIER_RAW {
            let ParsedExtension::AuthorityKeyIdentifier(aki) = ext.parsed_extension() else {
                return Err("AKI extension parse failed".to_string());
            };
            out.push(CrlExtensionParsed::AuthorityKeyIdentifier {
                key_identifier: aki.key_identifier.as_ref().map(|k| k.0.to_vec()),
                has_other_fields: aki.authority_cert_issuer.is_some()
                    || aki.authority_cert_serial.is_some(),
                critical: ext.critical,
            });
        } else if oid == OID_CRL_NUMBER_RAW {
            match ext.parsed_extension() {
                ParsedExtension::CRLNumber(n) => out.push(CrlExtensionParsed::CrlNumber {
                    number: n.clone(),
                    critical: ext.critical,
                }),
                _ => return Err("CRLNumber extension parse failed".to_string()),
            }
        } else {
            out.push(CrlExtensionParsed::Other {
                oid: ext.oid.to_id_string(),
                critical: ext.critical,
            })
        }
    }
    Ok(out)
}

fn validate_extensions_profile(
    exts: &[CrlExtensionParsed],
) -> Result<CrlExtensions, CrlProfileError> {
    if exts.len() != 2 {
        return Err(CrlProfileError::InvalidExtensionsCount(exts.len()));
    }

    let mut seen: Vec<String> = Vec::new();
    let mut authority_key_identifier: Option<Vec<u8>> = None;
    let mut crl_number: Option<BigUnsigned> = None;

    for ext in exts {
        match ext {
            CrlExtensionParsed::AuthorityKeyIdentifier {
                key_identifier,
                has_other_fields,
                critical: _,
            } => {
                let oid = OID_AUTHORITY_KEY_IDENTIFIER.to_string();
                if seen.iter().any(|s| s == &oid) {
                    return Err(CrlProfileError::DuplicateExtension(oid));
                }
                seen.push(oid.clone());

                if *has_other_fields {
                    return Err(CrlProfileError::AkiHasOtherFields);
                }
                let ki = key_identifier
                    .as_ref()
                    .ok_or(CrlProfileError::AkiMissingKeyIdentifier)?;
                authority_key_identifier = Some(ki.clone());
            }
            CrlExtensionParsed::CrlNumber { number, critical } => {
                let oid = OID_CRL_NUMBER.to_string();
                if seen.iter().any(|s| s == &oid) {
                    return Err(CrlProfileError::DuplicateExtension(oid));
                }
                seen.push(oid.clone());

                if *critical {
                    return Err(CrlProfileError::CrlNumberCritical);
                }
                if number.bits() > 159 {
                    return Err(CrlProfileError::CrlNumberOutOfRange);
                }
                crl_number = Some(BigUnsigned::from_biguint(number));
            }
            CrlExtensionParsed::Other { oid, .. } => {
                return Err(CrlProfileError::UnsupportedExtension(oid.clone()));
            }
        }
    }

    Ok(CrlExtensions {
        authority_key_identifier: authority_key_identifier.ok_or(CrlProfileError::AkiMissing)?,
        crl_number: crl_number.ok_or(CrlProfileError::CrlNumberMissing)?,
    })
}

fn get_subject_key_identifier(cert: &X509Certificate<'_>) -> Option<Vec<u8>> {
    cert.extensions()
        .iter()
        .find(|ext| ext.oid.as_bytes() == OID_SUBJECT_KEY_IDENTIFIER_RAW)
        .and_then(|ext| match ext.parsed_extension() {
            ParsedExtension::SubjectKeyIdentifier(ki) => Some(ki.0.to_vec()),
            _ => None,
        })
}
