use metacrate_ci_matrix::{
    audit, audit_api_surface, audit_artifacts, audit_consolidated_ci, audit_dependencies,
    audit_documentation, audit_provenance, audit_release_candidate, load, run, run_release_gate,
    run_required_gate, workspace_root, write_api_baseline, write_documentation_report,
    write_provenance_reports,
};
use std::path::{Path, PathBuf};

fn main() {
    if let Err(error) = execute() {
        eprintln!("ci-matrix: {error}");
        std::process::exit(1);
    }
}

#[allow(clippy::too_many_lines)] // Flat CLI command routing is clearer than nested dispatch.
fn execute() -> Result<(), Box<dyn std::error::Error>> {
    let current = std::env::current_dir()?;
    let root = workspace_root(&current).ok_or("could not locate the Cargo workspace")?;
    let matrix = load(&root)?;
    let mut arguments = std::env::args().skip(1);
    match arguments.next().as_deref() {
        Some("ci-audit") if arguments.next().is_none() => {
            audit_consolidated_ci(&root)?;
            println!("consolidated CI coverage and workflow split: ok");
        }
        Some("required-gate") => {
            gate_command(&root, arguments, "required", run_required_gate)?;
        }
        Some("release-gate") => {
            gate_command(&root, arguments, "release", run_release_gate)?;
        }
        Some("audit") if arguments.next().is_none() => {
            audit(&root, &matrix)?;
            println!(
                "release CI matrix: {} validated profiles; Linux, Windows, macOS, stable, default/minimal/optional features, tests, examples, and docs covered",
                matrix.profiles.len()
            );
        }
        Some("run") => {
            let profile = arguments.next().ok_or("run requires a profile")?;
            let flag = arguments.next().ok_or("run requires --evidence FILE")?;
            let evidence = arguments.next().ok_or("run requires --evidence FILE")?;
            if flag != "--evidence" || arguments.next().is_some() {
                return Err("usage: ci-matrix run PROFILE --evidence FILE".into());
            }
            let evidence = absolute_or_rooted(&root, &evidence);
            run(&root, &matrix, &profile, &evidence)?;
            println!("release CI profile {profile}: ok ({})", evidence.display());
        }
        Some("dependency-audit") => {
            let flag = arguments
                .next()
                .ok_or("dependency-audit requires --evidence FILE")?;
            let evidence = arguments
                .next()
                .ok_or("dependency-audit requires --evidence FILE")?;
            if flag != "--evidence" || arguments.next().is_some() {
                return Err("usage: ci-matrix dependency-audit --evidence FILE".into());
            }
            let evidence = absolute_or_rooted(&root, &evidence);
            audit_dependencies(&root, &evidence)?;
            println!("dependency policy: ok ({})", evidence.display());
        }
        Some("artifact-audit") => {
            artifact_audit_command(&root, arguments)?;
        }
        Some("release-candidate-audit") => {
            release_candidate_audit_command(&root, arguments)?;
        }
        Some("documentation-report") if arguments.next().is_none() => {
            write_documentation_report(&root)?;
            println!("documentation coverage report: updated");
        }
        Some("documentation-audit") => {
            let flag = arguments
                .next()
                .ok_or("documentation-audit requires --evidence FILE")?;
            let evidence = arguments
                .next()
                .ok_or("documentation-audit requires --evidence FILE")?;
            if flag != "--evidence" || arguments.next().is_some() {
                return Err("usage: ci-matrix documentation-audit --evidence FILE".into());
            }
            let evidence = absolute_or_rooted(&root, &evidence);
            audit_documentation(&root, &evidence)?;
            println!("documentation coverage: ok ({})", evidence.display());
        }
        Some("api-baseline-write") if arguments.next().is_none() => {
            write_api_baseline(&root)?;
            println!("API/SemVer baseline and report: updated");
        }
        Some("api-audit") => {
            let flag = arguments
                .next()
                .ok_or("api-audit requires --evidence FILE")?;
            let evidence = arguments
                .next()
                .ok_or("api-audit requires --evidence FILE")?;
            if flag != "--evidence" || arguments.next().is_some() {
                return Err("usage: ci-matrix api-audit --evidence FILE".into());
            }
            let evidence = absolute_or_rooted(&root, &evidence);
            audit_api_surface(&root, &evidence)?;
            println!("API/SemVer surface: ok ({})", evidence.display());
        }
        Some("provenance-report") if arguments.next().is_none() => {
            write_provenance_reports(&root)?;
            println!("provenance and distribution reports: updated");
        }
        Some("provenance-audit") => {
            provenance_audit_command(&root, arguments)?;
        }
        _ => {
            return Err(
                "usage: ci-matrix ci-audit | required-gate --evidence FILE | release-gate --evidence FILE | audit | run PROFILE --evidence FILE | dependency-audit --evidence FILE | artifact-audit --artifact-dir DIR --package-dir DIR --evidence FILE | release-candidate-audit --evidence FILE | documentation-report | documentation-audit --evidence FILE | api-baseline-write | api-audit --evidence FILE | provenance-report | provenance-audit --evidence FILE"
                    .into(),
            );
        }
    }
    Ok(())
}

fn gate_command(
    root: &Path,
    mut arguments: impl Iterator<Item = String>,
    gate: &str,
    run_gate: fn(&Path, &Path) -> metacrate_ci_matrix::Result<()>,
) -> Result<(), Box<dyn std::error::Error>> {
    let flag = arguments
        .next()
        .ok_or_else(|| format!("{gate}-gate requires --evidence FILE"))?;
    let evidence = arguments
        .next()
        .ok_or_else(|| format!("{gate}-gate requires --evidence FILE"))?;
    if flag != "--evidence" || arguments.next().is_some() {
        return Err(format!("usage: ci-matrix {gate}-gate --evidence FILE").into());
    }
    let evidence = absolute_or_rooted(root, &evidence);
    run_gate(root, &evidence)?;
    println!("{gate} CI gate: ok ({})", evidence.display());
    Ok(())
}

fn release_candidate_audit_command(
    root: &Path,
    mut arguments: impl Iterator<Item = String>,
) -> Result<(), Box<dyn std::error::Error>> {
    let flag = arguments
        .next()
        .ok_or("release-candidate-audit requires --evidence FILE")?;
    let evidence = arguments
        .next()
        .ok_or("release-candidate-audit requires --evidence FILE")?;
    if flag != "--evidence" || arguments.next().is_some() {
        return Err("usage: ci-matrix release-candidate-audit --evidence FILE".into());
    }
    let evidence = absolute_or_rooted(root, &evidence);
    audit_release_candidate(root, &evidence)?;
    println!("first release candidate: ok ({})", evidence.display());
    Ok(())
}

fn artifact_audit_command(
    root: &Path,
    mut arguments: impl Iterator<Item = String>,
) -> Result<(), Box<dyn std::error::Error>> {
    let artifact_dir = option(&mut arguments, "--artifact-dir")?;
    let package_dir = option(&mut arguments, "--package-dir")?;
    let evidence = option(&mut arguments, "--evidence")?;
    if arguments.next().is_some() {
        return Err(
            "usage: ci-matrix artifact-audit --artifact-dir DIR --package-dir DIR --evidence FILE"
                .into(),
        );
    }
    let artifact_dir = absolute_or_rooted(root, &artifact_dir);
    let package_dir = absolute_or_rooted(root, &package_dir);
    let evidence = absolute_or_rooted(root, &evidence);
    audit_artifacts(root, &artifact_dir, &package_dir, &evidence)?;
    println!("native release artifacts: ok ({})", evidence.display());
    Ok(())
}

fn option(
    arguments: &mut impl Iterator<Item = String>,
    expected: &str,
) -> Result<String, Box<dyn std::error::Error>> {
    let flag = arguments
        .next()
        .ok_or_else(|| format!("missing {expected}"))?;
    if flag != expected {
        return Err(format!("expected {expected}, found {flag}").into());
    }
    arguments
        .next()
        .ok_or_else(|| format!("missing value for {expected}").into())
}

fn provenance_audit_command(
    root: &Path,
    mut arguments: impl Iterator<Item = String>,
) -> Result<(), Box<dyn std::error::Error>> {
    let flag = arguments
        .next()
        .ok_or("provenance-audit requires --evidence FILE")?;
    let evidence = arguments
        .next()
        .ok_or("provenance-audit requires --evidence FILE")?;
    if flag != "--evidence" || arguments.next().is_some() {
        return Err("usage: ci-matrix provenance-audit --evidence FILE".into());
    }
    let evidence = absolute_or_rooted(root, &evidence);
    audit_provenance(root, &evidence)?;
    println!(
        "license and provenance surface: ok ({})",
        evidence.display()
    );
    Ok(())
}

fn absolute_or_rooted(root: &Path, value: &str) -> PathBuf {
    let path = PathBuf::from(value);
    if path.is_absolute() {
        path
    } else {
        root.join(path)
    }
}
