use std::path::PathBuf;

use rpki::cir::{CanonicalInputRepresentation, CirTrustAnchor, encode_cir, sha256};

const USAGE: &str = "Usage: cir_ta_only_fixture --tal-path <path> --ta-path <path> --tal-uri <url> --validation-time <rfc3339> --cir-out <path> --repo-bytes-db <path>";

fn parse_args(
    argv: &[String],
) -> Result<
    (
        PathBuf,
        PathBuf,
        String,
        time::OffsetDateTime,
        PathBuf,
        PathBuf,
    ),
    String,
> {
    let mut tal_path = None;
    let mut ta_path = None;
    let mut tal_uri = None;
    let mut validation_time = None;
    let mut cir_out = None;
    let mut repo_bytes_db = None;
    let mut i = 1usize;
    while i < argv.len() {
        match argv[i].as_str() {
            "--tal-path" => {
                i += 1;
                tal_path = Some(PathBuf::from(
                    argv.get(i).ok_or("--tal-path requires a value")?,
                ));
            }
            "--ta-path" => {
                i += 1;
                ta_path = Some(PathBuf::from(
                    argv.get(i).ok_or("--ta-path requires a value")?,
                ));
            }
            "--tal-uri" => {
                i += 1;
                tal_uri = Some(argv.get(i).ok_or("--tal-uri requires a value")?.clone());
            }
            "--validation-time" => {
                i += 1;
                let raw = argv.get(i).ok_or("--validation-time requires a value")?;
                validation_time = Some(
                    time::OffsetDateTime::parse(
                        raw,
                        &time::format_description::well_known::Rfc3339,
                    )
                    .map_err(|e| format!("invalid validation time: {e}"))?,
                );
            }
            "--cir-out" => {
                i += 1;
                cir_out = Some(PathBuf::from(
                    argv.get(i).ok_or("--cir-out requires a value")?,
                ));
            }
            "--repo-bytes-db" => {
                i += 1;
                repo_bytes_db = Some(PathBuf::from(
                    argv.get(i).ok_or("--repo-bytes-db requires a value")?,
                ));
            }
            "-h" | "--help" => return Err(USAGE.to_string()),
            other => return Err(format!("unknown argument: {other}\n\n{USAGE}")),
        }
        i += 1;
    }
    Ok((
        tal_path.ok_or_else(|| format!("--tal-path is required\n\n{USAGE}"))?,
        ta_path.ok_or_else(|| format!("--ta-path is required\n\n{USAGE}"))?,
        tal_uri.ok_or_else(|| format!("--tal-uri is required\n\n{USAGE}"))?,
        validation_time.ok_or_else(|| format!("--validation-time is required\n\n{USAGE}"))?,
        cir_out.ok_or_else(|| format!("--cir-out is required\n\n{USAGE}"))?,
        repo_bytes_db.ok_or_else(|| format!("--repo-bytes-db is required\n\n{USAGE}"))?,
    ))
}

fn main() -> Result<(), String> {
    let argv: Vec<String> = std::env::args().collect();
    let (tal_path, ta_path, tal_uri, validation_time, cir_out, repo_bytes_db) = parse_args(&argv)?;
    let _ = repo_bytes_db;

    let tal_bytes = std::fs::read(&tal_path).map_err(|e| format!("read tal failed: {e}"))?;
    let ta_bytes = std::fs::read(&ta_path).map_err(|e| format!("read ta failed: {e}"))?;
    let tal = rpki::data_model::tal::Tal::decode_bytes(&tal_bytes)
        .map_err(|e| format!("decode tal failed: {e}"))?;
    let ta_rsync_uri = tal
        .ta_uris
        .iter()
        .find(|uri| uri.scheme() == "rsync")
        .ok_or("tal must contain an rsync URI")?
        .as_str()
        .to_string();

    let ta_certificate_sha256 = sha256(&ta_bytes);

    let cir = CanonicalInputRepresentation::new_v4(
        validation_time,
        Vec::new(),
        Vec::new(),
        vec![CirTrustAnchor {
            ta_rsync_uri,
            tal_uri,
            tal_bytes,
            ta_certificate_der: ta_bytes,
            ta_certificate_sha256,
        }],
        Vec::new(),
        Vec::new(),
    );
    let der = encode_cir(&cir).map_err(|e| format!("encode cir failed: {e}"))?;
    if let Some(parent) = cir_out.parent() {
        std::fs::create_dir_all(parent).map_err(|e| format!("create cir parent failed: {e}"))?;
    }
    std::fs::write(&cir_out, der).map_err(|e| format!("write cir failed: {e}"))?;
    Ok(())
}
