//! OCSP example — build a certificate identifier and an OCSP request.
//!
//! This example shows the client-side construction of an OCSP request.
//! HTTP transport is the caller's responsibility; this example stops at the
//! DER-encoded request bytes that would be sent to the responder.
//!
//! Run with: cargo run --example ocsp -p native-ossl

use native_ossl::ocsp::{OcspCertId, OcspRequest, OcspResponseStatus};
use native_ossl::pkey::KeygenCtx;
use native_ossl::x509::{X509Builder, X509NameOwned};

fn make_cert(
    priv_key: &native_ossl::pkey::Pkey<native_ossl::pkey::Private>,
    pub_key: &native_ossl::pkey::Pkey<native_ossl::pkey::Public>,
    cn: &[u8],
    serial: i64,
) -> Result<native_ossl::x509::X509, Box<dyn std::error::Error>> {
    let mut name = X509NameOwned::new()?;
    name.add_entry_by_txt(c"CN", cn)?;
    let cert = X509Builder::new()?
        .set_version(2)?
        .set_serial_number(serial)?
        .set_not_before_offset(0)?
        .set_not_after_offset(365 * 86400)?
        .set_subject_name(&name)?
        .set_issuer_name(&name)?
        .set_public_key(pub_key)?
        .sign(priv_key, None)?
        .build();
    Ok(cert)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // ── Build a minimal CA and end-entity certificate pair ───────────────────

    let mut kgen = KeygenCtx::new(c"ED25519")?;
    let ca_priv = kgen.generate()?;
    let ca_pub = native_ossl::pkey::Pkey::<native_ossl::pkey::Public>::from(ca_priv.clone());

    let ee_priv = kgen.generate()?;
    let ee_pub = native_ossl::pkey::Pkey::<native_ossl::pkey::Public>::from(ee_priv.clone());

    let ca_cert = make_cert(&ca_priv, &ca_pub, b"Test CA", 1)?;
    let ee_cert = make_cert(&ee_priv, &ee_pub, b"end-entity.example.com", 42)?;

    // ── Build a certificate identifier (SHA-1, the OCSP default) ─────────────

    // Pass `None` for the digest to use SHA-1 — required by most deployed
    // OCSP responders regardless of the certificate's signature algorithm.
    let cert_id = OcspCertId::from_cert(None, &ee_cert, &ca_cert)?;
    println!("OcspCertId created for serial 42");

    // ── Build an OCSP request ─────────────────────────────────────────────────

    let mut req = OcspRequest::new()?;
    // add_cert_id consumes the cert_id (add0 semantics).
    // Clone first if you need to reuse it later (e.g. for find_status).
    req.add_cert_id(cert_id.clone())?;

    let req_der = req.to_der()?;
    println!("OCSP request DER: {} bytes", req_der.len());
    println!("Hex: {}", native_ossl::util::hex_encode(&req_der));

    // At this point `req_der` would be sent as the body of an HTTP POST
    // to the OCSP responder URL found in the certificate's AIA extension.

    // ── Parsing a response (demonstration with a constructed response) ────────

    // We can't get a real network response in this example, but demonstrate
    // what the status check looks like once the DER bytes arrive:
    //
    //   let resp = OcspResponse::from_der(&resp_der)?;
    //   assert_eq!(resp.status(), OcspResponseStatus::Successful);
    //   let basic = resp.basic()?;
    //   basic.verify(&store, 0)?;
    //   if let Some(status) = basic.find_status(&cert_id)? {
    //       match status.cert_status {
    //           OcspCertStatus::Good    => println!("certificate is valid"),
    //           OcspCertStatus::Revoked { reason } => println!("REVOKED: {reason}"),
    //           OcspCertStatus::Unknown => println!("responder does not know"),
    //       }
    //   }

    let _ = OcspResponseStatus::Successful; // confirm type is accessible
    println!("OcspResponseStatus variants accessible: OK");

    Ok(())
}
