use super::*;
use crate::runner::{Database, Mode, Settings, Verbosity};

fn s(strs: &[&str]) -> Vec<String> {
    std::iter::once("prog")
        .chain(strs.iter().copied())
        .map(String::from)
        .collect()
}

fn apply(args: &[&str]) -> Settings {
    try_apply_cli_args(Settings::new(), s(args)).unwrap_or_else(|e| match e {
        CliError::Help(_) => panic!("unexpected help"),
        CliError::Parse(msg) => panic!("parse error: {msg}"),
    })
}

#[test]
fn test_no_args_returns_default() {
    let defaults = Settings::new();
    let parsed = apply(&[]);
    assert_eq!(parsed.test_cases, defaults.test_cases);
    assert_eq!(parsed.verbosity, defaults.verbosity);
    assert_eq!(parsed.seed, defaults.seed);
}

#[test]
fn test_test_cases_override() {
    let parsed = apply(&["--test-cases", "500"]);
    assert_eq!(parsed.test_cases, 500);
}

#[test]
fn test_seed_override() {
    let parsed = apply(&["--seed", "42"]);
    assert_eq!(parsed.seed, Some(42));
}

#[test]
fn test_seed_none() {
    let parsed = apply(&["--seed", "none"]);
    assert_eq!(parsed.seed, None);
}

#[test]
fn test_verbosity_override() {
    let parsed = apply(&["--verbosity", "quiet"]);
    assert_eq!(parsed.verbosity, Verbosity::Quiet);

    let parsed = apply(&["--verbosity", "verbose"]);
    assert_eq!(parsed.verbosity, Verbosity::Verbose);

    let parsed = apply(&["--verbosity", "debug"]);
    assert_eq!(parsed.verbosity, Verbosity::Debug);

    let parsed = apply(&["--verbosity", "normal"]);
    assert_eq!(parsed.verbosity, Verbosity::Normal);
}

#[test]
fn test_derandomize_override() {
    let parsed = apply(&["--derandomize", "true"]);
    assert!(parsed.derandomize);
    let parsed = apply(&["--derandomize", "false"]);
    assert!(!parsed.derandomize);
}

#[test]
fn test_database_path() {
    let parsed = apply(&["--database", "/tmp/example"]);
    assert_eq!(parsed.database, Database::Path("/tmp/example".to_string()));
}

#[test]
fn test_database_disabled() {
    let parsed = apply(&["--database", "disabled"]);
    assert_eq!(parsed.database, Database::Disabled);
}

#[test]
fn test_suppress_health_check_single() {
    let parsed = apply(&["--suppress-health-check", "too_slow"]);
    assert_eq!(
        parsed.suppress_health_check,
        vec![crate::runner::HealthCheck::TooSlow]
    );
}

#[test]
fn test_suppress_health_check_multiple() {
    let parsed = apply(&["--suppress-health-check", "too_slow,filter_too_much"]);
    assert_eq!(
        parsed.suppress_health_check,
        vec![
            crate::runner::HealthCheck::TooSlow,
            crate::runner::HealthCheck::FilterTooMuch
        ]
    );
}

#[test]
fn test_suppress_health_check_all() {
    let parsed = apply(&["--suppress-health-check", "all"]);
    assert_eq!(parsed.suppress_health_check.len(), 4);
}

#[test]
fn test_multiple_flags() {
    let parsed = apply(&["--test-cases", "7", "--seed", "9", "--verbosity", "quiet"]);
    assert_eq!(parsed.test_cases, 7);
    assert_eq!(parsed.seed, Some(9));
    assert_eq!(parsed.verbosity, Verbosity::Quiet);
}

#[test]
fn test_unknown_arg_error() {
    let err = try_apply_cli_args(Settings::new(), s(&["--nope"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("Unknown argument")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_missing_value_error() {
    let err = try_apply_cli_args(Settings::new(), s(&["--test-cases"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("requires a value")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_invalid_value_error() {
    let err = try_apply_cli_args(Settings::new(), s(&["--test-cases", "abc"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("non-negative integer")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_help_returns_help_error() {
    let err = try_apply_cli_args(Settings::new(), s(&["--help"])).unwrap_err();
    match err {
        CliError::Help(msg) => assert!(msg.contains("Usage:")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_short_help_returns_help_error() {
    let err = try_apply_cli_args(Settings::new(), s(&["-h"])).unwrap_err();
    matches!(err, CliError::Help(_));
}

#[test]
fn test_default_preserved_when_not_overridden() {
    let parsed = try_apply_cli_args(Settings::new().test_cases(42), s(&[])).unwrap();
    assert_eq!(parsed.test_cases, 42);
}

#[test]
fn test_explicit_override_wins_over_default() {
    let parsed =
        try_apply_cli_args(Settings::new().test_cases(42), s(&["--test-cases", "10"])).unwrap();
    assert_eq!(parsed.test_cases, 10);
}

#[test]
fn test_invalid_verbosity_error() {
    let err = try_apply_cli_args(Settings::new(), s(&["--verbosity", "loud"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("quiet|normal|verbose|debug")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_invalid_bool_error() {
    let err = try_apply_cli_args(Settings::new(), s(&["--derandomize", "maybe"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("true|false")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_invalid_health_check_error() {
    let err = try_apply_cli_args(Settings::new(), s(&["--suppress-health-check", "bad_name"]))
        .unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("does not recognise")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_bool_aliases() {
    let parsed = apply(&["--derandomize", "1"]);
    assert!(parsed.derandomize);
    let parsed = apply(&["--derandomize", "yes"]);
    assert!(parsed.derandomize);
    let parsed = apply(&["--derandomize", "0"]);
    assert!(!parsed.derandomize);
    let parsed = apply(&["--derandomize", "no"]);
    assert!(!parsed.derandomize);
}

#[test]
fn test_invalid_seed_error() {
    let err = try_apply_cli_args(Settings::new(), s(&["--seed", "abc"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("integer or 'none'")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_apply_cli_args_success() {
    match apply_cli_args(Settings::new(), s(&["--test-cases", "13"])) {
        CliOutcome::Success(settings) => assert_eq!(settings.test_cases, 13),
        other => panic!("expected Success, got {other:?}"),
    }
}

#[test]
fn test_apply_cli_args_help() {
    match apply_cli_args(Settings::new(), s(&["--help"])) {
        CliOutcome::Help(msg) => assert!(msg.contains("Usage:")),
        other => panic!("expected Help, got {other:?}"),
    }
}

#[test]
fn test_single_test_case_flag() {
    let parsed = apply(&["--single-test-case"]);
    assert_eq!(parsed.mode, Mode::SingleTestCase);
}

#[test]
fn test_single_test_case_default_is_test_run() {
    let parsed = apply(&[]);
    assert_eq!(parsed.mode, Mode::TestRun);
}

#[test]
fn test_apply_cli_args_parse_error() {
    match apply_cli_args(Settings::new(), s(&["--not-a-flag"])) {
        CliOutcome::ParseError(msg) => {
            assert!(msg.contains("Unknown argument"));
            assert!(msg.contains("Usage:"));
        }
        other => panic!("expected ParseError, got {other:?}"),
    }
}
