#![cfg(feature = "cli")]

use assert_cmd::Command;
use predicates::prelude::*;

fn pickaxe() -> Command {
    Command::new(assert_cmd::cargo::cargo_bin!("pickaxe"))
}

// --- Run pipeline with defaults ---

#[test]
fn grounded_tweety_produces_valid_json() {
    let output = pickaxe()
        .args(["-i", "tests/fixtures/tweety.json", "-s", "grounded"])
        .output()
        .unwrap();

    assert!(output.status.success());
    let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
    assert!(json["num_arguments"].is_number());
    assert!(json["num_attacks"].is_number());
    assert!(json["num_extensions"].is_number());
    assert!(json["extensions"].is_array());
    assert!(json["timings_ms"].is_object());
    assert!(json["timings_ms"]["grounding"].is_number());
    assert!(json["timings_ms"]["construction"].is_number());
    assert!(json["timings_ms"]["attacks"].is_number());
    assert!(json["timings_ms"]["evaluation"].is_number());
    assert!(json["timings_ms"]["total"].is_number());
}

#[test]
fn no_arguments_exits_with_code_2() {
    pickaxe()
        .assert()
        .failure()
        .code(2)
        .stderr(predicate::str::contains("--input"));
}

#[test]
fn nonexistent_file_exits_with_code_1() {
    pickaxe()
        .args(["-i", "nonexistent.json", "-s", "grounded"])
        .assert()
        .failure()
        .code(1)
        .stderr(predicate::str::contains("failed to read input file"));
}

// --- Select pipeline components ---

#[test]
fn custom_components_produces_output() {
    pickaxe()
        .args([
            "-i",
            "tests/fixtures/tweety.json",
            "-s",
            "stable",
            "--grounder",
            "semi-naive",
            "--attack-computer",
            "indexed",
            "--solver",
            "scc-sat",
        ])
        .assert()
        .success();
}

// --- Pipeline presets ---

#[test]
fn preset_classical_produces_output() {
    pickaxe()
        .args([
            "-i",
            "tests/fixtures/tweety.json",
            "-s",
            "grounded",
            "--preset",
            "classical",
        ])
        .assert()
        .success();
}

#[test]
fn preset_conflicts_with_component_flags() {
    pickaxe()
        .args([
            "-i",
            "tests/fixtures/tweety.json",
            "-s",
            "grounded",
            "--preset",
            "classical",
            "--grounder",
            "semi-naive",
        ])
        .assert()
        .failure()
        .stderr(predicate::str::contains("mutually exclusive"));
}

// --- Preference ordering ---

#[test]
fn last_link_elitist_differs_from_none() {
    let out_none = pickaxe()
        .args([
            "-i",
            "tests/fixtures/tweety.json",
            "-s",
            "preferred",
            "-p",
            "none",
        ])
        .output()
        .unwrap();
    let out_lle = pickaxe()
        .args([
            "-i",
            "tests/fixtures/tweety.json",
            "-s",
            "preferred",
            "-p",
            "last-link-elitist",
        ])
        .output()
        .unwrap();

    assert!(out_none.status.success());
    assert!(out_lle.status.success());

    let json_none: serde_json::Value = serde_json::from_slice(&out_none.stdout).unwrap();
    let json_lle: serde_json::Value = serde_json::from_slice(&out_lle.stdout).unwrap();

    // With rule preferences (d2 > d1), last-link-elitist should produce
    // different extension conclusions than none (where all attacks succeed).
    let concl_none = &json_none["extensions"][0]["conclusions"];
    let concl_lle = &json_lle["extensions"][0]["conclusions"];
    assert_ne!(concl_none, concl_lle);
}

#[test]
fn flat_preference_produces_output() {
    pickaxe()
        .args([
            "-i",
            "tests/fixtures/tweety.json",
            "-s",
            "preferred",
            "-p",
            "flat",
        ])
        .assert()
        .success();
}

// --- Help and version ---

#[test]
fn help_lists_all_flags() {
    pickaxe()
        .arg("--help")
        .assert()
        .success()
        .stdout(predicate::str::contains("--input"))
        .stdout(predicate::str::contains("--semantics"))
        .stdout(predicate::str::contains("--grounder"))
        .stdout(predicate::str::contains("--attack-computer"))
        .stdout(predicate::str::contains("--solver"))
        .stdout(predicate::str::contains("--preset"))
        .stdout(predicate::str::contains("--preferences"))
        .stdout(predicate::str::contains("--format"))
        .stdout(predicate::str::contains("--accrual-lifting"));
}

#[test]
fn version_displays_version() {
    pickaxe()
        .arg("--version")
        .assert()
        .success()
        .stdout(predicate::str::contains("0.1.0"));
}
