use super::util::current_dir;
use koala_core::invariant::{Context, Outcome, Registry};
use std::path::PathBuf;
use std::process::ExitCode;

pub fn list() -> ExitCode {
    let reg = Registry::builtin();
    println!("{} built-in invariant(s):\n", reg.len());
    for r in reg.rules() {
        let adr = r.adr().unwrap_or("—");
        println!("  {:<8} {:<36} {}", r.category().as_str(), r.id(), adr);
        println!("           {}", r.intent());
    }
    ExitCode::SUCCESS
}

pub fn explain(id: &str) -> ExitCode {
    let reg = Registry::builtin();
    match reg.find(id) {
        Some(r) => {
            println!("id        : {}", r.id());
            println!("category  : {}", r.category().as_str());
            println!("intent    : {}", r.intent());
            println!("adr       : {}", r.adr().unwrap_or("—"));
            ExitCode::SUCCESS
        }
        None => {
            eprintln!("error: no invariant with id `{id}`");
            eprintln!("hint : run `koala-core invariant list` to see all ids");
            ExitCode::from(2)
        }
    }
}

pub fn run(root: Option<PathBuf>) -> ExitCode {
    let root = root.unwrap_or_else(current_dir);
    let ctx = Context::new(root);
    let reg = Registry::builtin();
    let results = reg.evaluate_all(&ctx);

    let mut fails = 0usize;
    let mut skips = 0usize;
    for (id, outcome) in &results {
        match outcome {
            Outcome::Pass { info } => {
                let suffix = info
                    .as_deref()
                    .map(|s| format!(" — {s}"))
                    .unwrap_or_default();
                println!("PASS  {id}{suffix}");
            }
            Outcome::Fail { detail, reproduce } => {
                fails += 1;
                println!("FAIL  {id}");
                for line in detail.lines() {
                    println!("        {line}");
                }
                if let Some(r) = reproduce {
                    println!("        reproduce: {r}");
                }
            }
            Outcome::Skip { reason } => {
                skips += 1;
                println!("SKIP  {id} — {reason}");
            }
        }
    }

    println!(
        "\nsummary: {pass} pass, {fail} fail, {skip} skip (of {total})",
        pass = results.len() - fails - skips,
        fail = fails,
        skip = skips,
        total = results.len(),
    );

    if fails > 0 {
        ExitCode::from(1)
    } else {
        ExitCode::SUCCESS
    }
}
