use rpki::data_model::signed_object::{RpkiSignedObject, SignedObjectVerifyError};
use x509_parser::prelude::FromDer;
use x509_parser::prelude::X509Certificate;

fn len_bytes(len: usize) -> Vec<u8> {
    if len < 128 {
        vec![len as u8]
    } else {
        let mut tmp = Vec::new();
        let mut n = len;
        while n > 0 {
            tmp.push((n & 0xFF) as u8);
            n >>= 8;
        }
        tmp.reverse();
        let mut out = vec![0x80 | (tmp.len() as u8)];
        out.extend(tmp);
        out
    }
}

fn tlv(tag: u8, content: &[u8]) -> Vec<u8> {
    let mut out = vec![tag];
    out.extend(len_bytes(content.len()));
    out.extend_from_slice(content);
    out
}

fn der_integer_bytes(bytes: &[u8]) -> Vec<u8> {
    tlv(0x02, bytes)
}

fn der_integer_u64(v: u64) -> Vec<u8> {
    let mut bytes = Vec::new();
    let mut n = v;
    if n == 0 {
        bytes.push(0);
    } else {
        while n > 0 {
            bytes.push((n & 0xFF) as u8);
            n >>= 8;
        }
        bytes.reverse();
        if bytes[0] & 0x80 != 0 {
            bytes.insert(0, 0);
        }
    }
    der_integer_bytes(&bytes)
}

fn der_null() -> Vec<u8> {
    vec![0x05, 0x00]
}

fn der_oid(oid: &str) -> Vec<u8> {
    use der_parser::asn1_rs::ToDer;
    use std::str::FromStr;
    let oid = der_parser::Oid::from_str(oid).unwrap();
    oid.to_der_vec().unwrap()
}

fn der_sequence(children: Vec<Vec<u8>>) -> Vec<u8> {
    let mut content = Vec::new();
    for c in children {
        content.extend(c);
    }
    tlv(0x30, &content)
}

fn der_bit_string(unused: u8, bytes: &[u8]) -> Vec<u8> {
    let mut content = vec![unused];
    content.extend_from_slice(bytes);
    tlv(0x03, &content)
}

fn rsa_spki_der_with_modulus_bytes(modulus: &[u8], exponent: u64) -> Vec<u8> {
    // SubjectPublicKeyInfo for RSA public key:
    // SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }
    // subjectPublicKey contains RSAPublicKey DER.
    let alg = der_sequence(vec![
        der_oid(rpki::data_model::oid::OID_RSA_ENCRYPTION),
        der_null(),
    ]);
    let rsa_pk = der_sequence(vec![der_integer_bytes(modulus), der_integer_u64(exponent)]);
    let spk = der_bit_string(0, &rsa_pk);
    der_sequence(vec![alg, spk])
}

#[test]
fn verify_mft_cms_signature_with_embedded_ee_cert() {
    let der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft",
    )
    .expect("read MFT fixture");
    let so = RpkiSignedObject::decode_der(&der).expect("decode signed object");
    so.verify_signature()
        .expect("CMS signature should verify with embedded EE cert");
}

#[test]
fn verify_fails_with_wrong_spki() {
    let mft_der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft",
    )
    .expect("read MFT fixture");
    let so = RpkiSignedObject::decode_der(&mft_der).expect("decode signed object");

    let issuer_cert_der = std::fs::read(
        "tests/fixtures/repository/rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer",
    )
    .expect("read issuer certificate fixture");
    let (_rem, issuer_cert) = X509Certificate::from_der(&issuer_cert_der).expect("parse cert");
    let wrong_spki_der = issuer_cert.public_key().raw.to_vec();

    let err = so
        .verify_signature_with_ee_spki_der(&wrong_spki_der)
        .unwrap_err();
    assert!(matches!(err, SignedObjectVerifyError::InvalidSignature));
}

#[test]
fn verify_fails_with_tampered_signature() {
    let der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft",
    )
    .expect("read MFT fixture");
    let mut so = RpkiSignedObject::decode_der(&der).expect("decode signed object");
    so.signed_data.signer_infos[0].signature[0] ^= 0x01;
    let err = so.verify_signature().unwrap_err();
    assert!(matches!(err, SignedObjectVerifyError::InvalidSignature));
}

#[test]
fn verify_rejects_spki_der_with_trailing_bytes() {
    let der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft",
    )
    .expect("read MFT fixture");
    let so = RpkiSignedObject::decode_der(&der).expect("decode signed object");

    let mut spki_der = so.signed_data.certificates[0].spki_der.clone();
    spki_der.push(0);
    let err = so.verify_signature_with_ee_spki_der(&spki_der).unwrap_err();
    assert!(matches!(
        err,
        SignedObjectVerifyError::EeSpkiTrailingBytes(1)
    ));
}

#[test]
fn verify_with_all_zero_modulus_exercises_strip_leading_zeros_fallback() {
    let der = std::fs::read(
        "tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft",
    )
    .expect("read MFT fixture");
    let so = RpkiSignedObject::decode_der(&der).expect("decode signed object");

    // modulus INTEGER 0, exponent 65537 (valid exponent encoding); signature verification must fail
    // but the SPKI parsing path should succeed.
    let spki_der = rsa_spki_der_with_modulus_bytes(&[0x00], 65537);
    let err = so.verify_signature_with_ee_spki_der(&spki_der).unwrap_err();
    assert!(matches!(err, SignedObjectVerifyError::InvalidSignature));
}
