use nils_test_support::bin;
use nils_test_support::cmd::{self, CmdOutput};
use pretty_assertions::assert_eq;
use std::path::PathBuf;
use std::sync::OnceLock;

fn codex_cli_bin() -> PathBuf {
    bin::resolve("codex-cli")
}

fn run(args: &[&str]) -> CmdOutput {
    let bin = codex_cli_bin();
    cmd::run(&bin, args, &[], None)
}

fn completion_zsh() -> &'static str {
    static OUTPUT: OnceLock<String> = OnceLock::new();
    OUTPUT
        .get_or_init(|| {
            let output = run(&["completion", "zsh"]);
            assert_eq!(output.code, 0, "stderr: {}", output.stderr_text());
            output.stdout_text()
        })
        .as_str()
}

fn assert_contains_all(haystack: &str, needles: &[&str]) {
    for needle in needles {
        assert!(
            haystack.contains(needle),
            "missing completion contract token: {needle}"
        );
    }
}

#[test]
fn completion_contract_includes_top_level_command_families() {
    let script = completion_zsh();
    assert_contains_all(
        script,
        &[
            "'agent:Agent command group' \\",
            "'auth:Authentication command group' \\",
            "'diag:Diagnostics command group' \\",
            "'config:Configuration command group' \\",
            "'prompt-segment:Prompt-segment command group' \\",
        ],
    );
}

#[test]
fn completion_contract_is_context_aware_across_command_families() {
    let script = completion_zsh();
    assert_contains_all(
        script,
        &[
            "curcontext=\"${curcontext%:*:*}:codex-cli-agent-command-$line[1]:\"",
            "curcontext=\"${curcontext%:*:*}:codex-cli-auth-command-$line[1]:\"",
            "curcontext=\"${curcontext%:*:*}:codex-cli-diag-command-$line[1]:\"",
            "curcontext=\"${curcontext%:*:*}:codex-cli-config-command-$line[1]:\"",
            "'--api-key[Use API key login flow]' \\",
            "'--cached[Cached mode (no network)]' \\",
            "':key:_default' \\",
            "':value:_default' \\",
            "'--time-format=[Reset time format (local time)]:TIME_FORMAT:_default' \\",
            "'*::target:_default' \\",
            "'*::secret:_default' \\",
        ],
    );
}

#[test]
fn completion_contract_format_values_include_text_and_json() {
    let script = completion_zsh();
    let format_candidates = script.match_indices(":format:(text json)'").count();
    assert!(
        format_candidates >= 2,
        "expected at least 2 format candidate entries, got {format_candidates}"
    );
    assert_contains_all(
        script,
        &[
            "'--format=[Output format (\\`text\\` or \\`json\\`)]:format:(text json)' \\",
            "'(--format)--json[Hidden alias for \\`--format json\\` (kept for backwards compatibility)]' \\",
        ],
    );
}
