use x509_parser::extensions::{AuthorityKeyIdentifier, ParsedExtension, X509Extension};
use x509_parser::prelude::FromDer;
use x509_parser::prelude::X509Version;
use x509_parser::revocation_list::CertificateRevocationList;
use x509_parser::asn1_rs::Tag;
use x509_parser::certificate::X509Certificate;
use x509_parser::x509::SubjectPublicKeyInfo;
use x509_parser::x509::AlgorithmIdentifier;

const OID_SHA256_WITH_RSA_ENCRYPTION: &str = "1.2.840.113549.1.1.11";
const OID_AUTHORITY_KEY_IDENTIFIER: &str = "2.5.29.35";
const OID_CRL_NUMBER: &str = "2.5.29.20";
const OID_SUBJECT_KEY_IDENTIFIER: &str = "2.5.29.14";

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Asn1TimeEncoding {
    UtcTime,
    GeneralizedTime,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Asn1TimeUtc {
    pub utc: time::OffsetDateTime,
    pub encoding: Asn1TimeEncoding,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BigUnsigned {
    /// Minimal big-endian bytes. For zero, this is `[0]`.
    pub bytes_be: Vec<u8>,
}

impl BigUnsigned {
    pub fn to_hex_upper(&self) -> String {
        hex::encode_upper(&self.bytes_be)
    }

    pub fn to_u64(&self) -> Option<u64> {
        if self.bytes_be.len() > 8 {
            return None;
        }
        let mut value: u64 = 0;
        for &b in &self.bytes_be {
            value = (value << 8) | (b as u64);
        }
        Some(value)
    }
}

#[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(Debug, thiserror::Error)]
pub enum CrlDecodeError {
    #[error("X.509 CRL parse error: {0}")]
    Parse(String),

    #[error("trailing bytes after CRL DER: {0} bytes")]
    TrailingBytes(usize),

    #[error("CRL version must be v2, got {0:?}")]
    InvalidVersion(Option<u32>),

    #[error("CRL signatureAlgorithm must be sha256WithRSAEncryption ({OID_SHA256_WITH_RSA_ENCRYPTION}), got {0}")]
    InvalidSignatureAlgorithm(String),

    #[error("CRL signature algorithm parameters must be absent or NULL")]
    InvalidSignatureAlgorithmParameters,

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

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

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

    #[error("duplicate CRL extension OID {0}")]
    DuplicateExtension(String),

    #[error("AuthorityKeyIdentifier must contain keyIdentifier")]
    AkiMissingKeyIdentifier,

    #[error("AuthorityKeyIdentifier must not contain authorityCertIssuer or authorityCertSerialNumber")]
    AkiHasOtherFields,

    #[error("CRLNumber must be non-critical")]
    CrlNumberCritical,

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

    #[error("CRL entry extensions must not be present")]
    EntryExtensionsNotAllowed,

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

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

impl RpkixCrl {
    /// 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> {
        let (rem, crl) = CertificateRevocationList::from_der(der)
            .map_err(|e| CrlDecodeError::Parse(e.to_string()))?;
        if !rem.is_empty() {
            return Err(CrlDecodeError::TrailingBytes(rem.len()));
        }

        let version = match crl.version() {
            Some(X509Version::V2) => 2,
            Some(v) => return Err(CrlDecodeError::InvalidVersion(Some(v.0))),
            None => return Err(CrlDecodeError::InvalidVersion(None)),
        };

        let sig_oid = crl.signature_algorithm.algorithm.to_id_string();
        let tbs_sig_oid = crl.tbs_cert_list.signature.algorithm.to_id_string();
        if sig_oid != tbs_sig_oid {
            return Err(CrlDecodeError::SignatureAlgorithmMismatch);
        }
        if sig_oid != OID_SHA256_WITH_RSA_ENCRYPTION {
            return Err(CrlDecodeError::InvalidSignatureAlgorithm(sig_oid));
        }
        validate_sig_params(&crl.signature_algorithm)?;
        validate_sig_params(&crl.tbs_cert_list.signature)?;

        let extensions = parse_and_validate_extensions(crl.extensions())?;

        let revoked_certs = crl
            .iter_revoked_certificates()
            .map(|rc| {
                if !rc.extensions().is_empty() {
                    return Err(CrlDecodeError::EntryExtensionsNotAllowed);
                }
                let revocation_date = asn1_time_to_model(rc.revocation_date);
                validate_time_encoding("revocationDate", &revocation_date)?;
                Ok(RevokedCert {
                    serial_number: biguint_to_big_unsigned(rc.serial()),
                    revocation_date,
                })
            })
            .collect::<Result<Vec<_>, _>>()?;

        let this_update = asn1_time_to_model(crl.last_update());
        validate_time_encoding("thisUpdate", &this_update)?;

        let next_update = crl
            .next_update()
            .map(asn1_time_to_model)
            .ok_or(CrlDecodeError::NextUpdateMissing)?;
        validate_time_encoding("nextUpdate", &next_update)?;

        Ok(RpkixCrl {
            raw_der: der.to_vec(),
            version,
            issuer_dn: crl.issuer().to_string(),
            signature_algorithm_oid: OID_SHA256_WITH_RSA_ENCRYPTION.to_string(),
            this_update,
            next_update,
            revoked_certs,
            extensions,
        })
    }

    /// 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()));
        }
        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)
    }
}

#[derive(Debug, thiserror::Error)]
pub enum CrlVerifyError {
    #[error("issuer certificate parse error: {0}")]
    IssuerCertificateParse(String),

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

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

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

    #[error("CRL parse error: {0}")]
    CrlParse(String),

    #[error("trailing bytes after CRL DER: {0} bytes")]
    CrlTrailingBytes(usize),

    #[error("CRL issuer DN does not match issuer certificate subject")]
    IssuerSubjectMismatch {
        crl_issuer_dn: String,
        issuer_subject_dn: String,
    },

    #[error("issuer certificate keyUsage present but missing cRLSign")]
    IssuerKeyUsageMissingCrlSign,

    #[error("CRL AKI.keyIdentifier does not match issuer certificate SKI")]
    AkiSkiMismatch,

    #[error("CRL signature verification failed: {0}")]
    InvalidSignature(String),
}

fn asn1_time_to_model(t: x509_parser::time::ASN1Time) -> Asn1TimeUtc {
    let encoding = if t.is_utctime() {
        Asn1TimeEncoding::UtcTime
    } else {
        Asn1TimeEncoding::GeneralizedTime
    };
    Asn1TimeUtc {
        utc: t.to_datetime(),
        encoding,
    }
}

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

fn validate_sig_params(sig: &AlgorithmIdentifier<'_>) -> Result<(), CrlDecodeError> {
    match sig.parameters.as_ref() {
        None => Ok(()),
        Some(p) if p.tag() == Tag::Null => Ok(()),
        Some(_p) => Err(CrlDecodeError::InvalidSignatureAlgorithmParameters),
    }
}

fn parse_and_validate_extensions(exts: &[X509Extension<'_>]) -> Result<CrlExtensions, CrlDecodeError> {
    if exts.len() != 2 {
        return Err(CrlDecodeError::InvalidExtensionsCount(exts.len()));
    }

    let mut authority_key_identifier: Option<Vec<u8>> = None;
    let mut crl_number: Option<BigUnsigned> = None;

    for ext in exts {
        let oid = ext.oid.to_id_string();
        match oid.as_str() {
            OID_AUTHORITY_KEY_IDENTIFIER => {
                if authority_key_identifier.is_some() {
                    return Err(CrlDecodeError::DuplicateExtension(oid));
                }
                let aki = parse_aki(ext)?;
                authority_key_identifier = Some(aki);
            }
            OID_CRL_NUMBER => {
                if crl_number.is_some() {
                    return Err(CrlDecodeError::DuplicateExtension(oid));
                }
                if ext.critical {
                    return Err(CrlDecodeError::CrlNumberCritical);
                }
                let n = parse_crl_number(ext)?;
                if n.bits() > 159 {
                    return Err(CrlDecodeError::CrlNumberOutOfRange);
                }
                crl_number = Some(biguint_to_big_unsigned(&n));
            }
            _ => return Err(CrlDecodeError::UnsupportedExtension(oid)),
        }
    }

    Ok(CrlExtensions {
        authority_key_identifier: authority_key_identifier.unwrap(),
        crl_number: crl_number.unwrap(),
    })
}

fn parse_aki(ext: &X509Extension<'_>) -> Result<Vec<u8>, CrlDecodeError> {
    let ParsedExtension::AuthorityKeyIdentifier(aki) = ext.parsed_extension() else {
        return Err(CrlDecodeError::Parse("AKI extension parse failed".into()));
    };
    validate_aki_profile(aki)?;
    Ok(aki
        .key_identifier
        .as_ref()
        .ok_or(CrlDecodeError::AkiMissingKeyIdentifier)?
        .0
        .to_vec())
}

fn validate_aki_profile(aki: &AuthorityKeyIdentifier<'_>) -> Result<(), CrlDecodeError> {
    if aki.key_identifier.is_none() {
        return Err(CrlDecodeError::AkiMissingKeyIdentifier);
    }
    if aki.authority_cert_issuer.is_some() || aki.authority_cert_serial.is_some() {
        return Err(CrlDecodeError::AkiHasOtherFields);
    }
    Ok(())
}

fn parse_crl_number(ext: &X509Extension<'_>) -> Result<der_parser::num_bigint::BigUint, CrlDecodeError> {
    match ext.parsed_extension() {
        ParsedExtension::CRLNumber(n) => Ok(n.clone()),
        ParsedExtension::ParseError { error } => Err(CrlDecodeError::Parse(error.to_string())),
        _ => Err(CrlDecodeError::Parse("CRLNumber extension parse failed".into())),
    }
}

fn biguint_to_big_unsigned(n: &der_parser::num_bigint::BigUint) -> BigUnsigned {
    let mut bytes = n.to_bytes_be();
    if bytes.is_empty() {
        bytes.push(0);
    }
    BigUnsigned { bytes_be: bytes }
}

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