//! Comprehensive system tests for `bivvy completions`.
//!
//! Tests shell completion generation for all supported shells,
//! shell-specific syntax validation, subcommand coverage, help output,
//! and error handling for invalid inputs. All tests spawn via
//! `spawn_bivvy_global`, which pins `HOME` and the `XDG_*` base-directory
//! variables at a shared process-lifetime tempdir so the global bivvy
//! store stays isolated from the user environment on both macOS and
//! Linux. Each test verifies process exit codes, and help/error output
//! is captured via `insta` snapshots so regressions in user-facing text
//! are caught.
#![cfg(unix)]

mod system;

use system::helpers::*;

// =====================================================================
// HAPPY PATH — Shell-specific syntax validation
// =====================================================================
//
// Each shell has idiomatic tokens that clap_complete must emit. We assert
// on those tokens directly (rather than snapshotting the full script)
// because the scripts are large and regenerated by clap_complete — a
// snapshot would break on every clap upgrade and add no signal. The
// version-stable signal is "did clap_complete emit a real script for the
// right shell," which these tokens cover.

/// Bash completions contain the `_bivvy` function and the `COMPREPLY`
/// bash-specific builtin.
#[test]
fn completions_bash() {
    let mut s = spawn_bivvy_global(&["completions", "bash"]);
    let text = read_to_eof(&mut s);

    assert!(
        text.contains("_bivvy"),
        "Bash completions should contain '_bivvy' function name, got: {}",
        &text[..text.len().min(200)]
    );
    assert!(
        text.contains("COMPREPLY"),
        "Bash completions should contain 'COMPREPLY' bash-specific keyword, got: {}",
        &text[..text.len().min(200)]
    );
    assert_exit_code(&s, 0);
}

/// Zsh completions contain the `#compdef bivvy` directive and the
/// `_bivvy` function name.
#[test]
fn completions_zsh() {
    let mut s = spawn_bivvy_global(&["completions", "zsh"]);
    let text = read_to_eof(&mut s);

    assert!(
        text.contains("#compdef bivvy"),
        "Zsh completions should contain '#compdef bivvy' directive, got: {}",
        &text[..text.len().min(200)]
    );
    assert!(
        text.contains("_bivvy"),
        "Zsh completions should contain '_bivvy' function name, got: {}",
        &text[..text.len().min(200)]
    );
    assert_exit_code(&s, 0);
}

/// Fish completions contain `complete -c bivvy` and a `__fish_` helper.
#[test]
fn completions_fish() {
    let mut s = spawn_bivvy_global(&["completions", "fish"]);
    let text = read_to_eof(&mut s);

    assert!(
        text.contains("complete -c bivvy"),
        "Fish completions should contain 'complete -c bivvy' command, got: {}",
        &text[..text.len().min(200)]
    );
    assert!(
        text.contains("__fish"),
        "Fish completions should contain '__fish' helper function, got: {}",
        &text[..text.len().min(200)]
    );
    assert_exit_code(&s, 0);
}

/// PowerShell completions contain `Register-ArgumentCompleter` and
/// reference `bivvy`.
#[test]
fn completions_powershell() {
    let mut s = spawn_bivvy_global(&["completions", "powershell"]);
    let text = read_to_eof(&mut s);

    assert!(
        text.contains("Register-ArgumentCompleter"),
        "PowerShell completions should contain 'Register-ArgumentCompleter', got: {}",
        &text[..text.len().min(200)]
    );
    assert!(
        text.contains("'bivvy'"),
        "PowerShell completions should register 'bivvy' as the command, got: {}",
        &text[..text.len().min(200)]
    );
    assert_exit_code(&s, 0);
}

/// Elvish completions contain `edit:completion:arg-completer` and
/// reference `bivvy`.
#[test]
fn completions_elvish() {
    let mut s = spawn_bivvy_global(&["completions", "elvish"]);
    let text = read_to_eof(&mut s);

    assert!(
        text.contains("edit:completion:arg-completer"),
        "Elvish completions should contain 'edit:completion:arg-completer', got: {}",
        &text[..text.len().min(200)]
    );
    // Elvish completions register the command as a quoted string literal —
    // `contains("bivvy")` alone would trivially pass on any clap error that
    // mentions the binary name, so we assert on the quoted-literal form that
    // only appears in a real elvish completion script.
    assert!(
        text.contains("'bivvy'"),
        "Elvish completions should register 'bivvy' as a quoted literal, got: {}",
        &text[..text.len().min(200)]
    );
    assert_exit_code(&s, 0);
}

// =====================================================================
// HAPPY PATH — Subcommand coverage in completions
// =====================================================================
//
// The generated completion script must list every documented bivvy
// subcommand — otherwise tab completion is silently broken for users.
// We check this for every shell (not just bash) because each shell's
// template is independent and regressions have historically been
// shell-specific.

const ALL_SUBCOMMANDS: &[&str] = &[
    "run",
    "init",
    "add",
    "templates",
    "status",
    "list",
    "lint",
    "last",
    "history",
    "feedback",
    "completions",
    "config",
    "cache",
    "update",
];

fn assert_contains_all_subcommands(shell: &str, text: &str) {
    for subcmd in ALL_SUBCOMMANDS {
        assert!(
            text.contains(subcmd),
            "{shell} completions should contain subcommand '{subcmd}', output length: {}",
            text.len()
        );
    }
}

/// Bash completions include all documented bivvy subcommands.
#[test]
fn completions_bash_contains_all_subcommands() {
    let mut s = spawn_bivvy_global(&["completions", "bash"]);
    let text = read_to_eof(&mut s);
    assert_contains_all_subcommands("Bash", &text);
    assert_exit_code(&s, 0);
}

/// Zsh completions include all documented bivvy subcommands.
#[test]
fn completions_zsh_contains_all_subcommands() {
    let mut s = spawn_bivvy_global(&["completions", "zsh"]);
    let text = read_to_eof(&mut s);
    assert_contains_all_subcommands("Zsh", &text);
    assert_exit_code(&s, 0);
}

/// Fish completions include all documented bivvy subcommands.
#[test]
fn completions_fish_contains_all_subcommands() {
    let mut s = spawn_bivvy_global(&["completions", "fish"]);
    let text = read_to_eof(&mut s);
    assert_contains_all_subcommands("Fish", &text);
    assert_exit_code(&s, 0);
}

/// PowerShell completions include all documented bivvy subcommands.
#[test]
fn completions_powershell_contains_all_subcommands() {
    let mut s = spawn_bivvy_global(&["completions", "powershell"]);
    let text = read_to_eof(&mut s);
    assert_contains_all_subcommands("PowerShell", &text);
    assert_exit_code(&s, 0);
}

/// Elvish completions include all documented bivvy subcommands.
#[test]
fn completions_elvish_contains_all_subcommands() {
    let mut s = spawn_bivvy_global(&["completions", "elvish"]);
    let text = read_to_eof(&mut s);
    assert_contains_all_subcommands("Elvish", &text);
    assert_exit_code(&s, 0);
}

// =====================================================================
// HELP
// =====================================================================

/// `--help` shows the expected description, the list of possible shell
/// values, and exits 0. The full help is snapshotted so that flag
/// renames, description edits, and shell-list changes are caught as
/// regressions.
#[test]
fn completions_help() {
    let mut s = spawn_bivvy_global(&["completions", "--help"]);
    let text = read_to_eof(&mut s);

    assert!(
        text.contains("Generate shell completions"),
        "Help should contain 'Generate shell completions', got: {}",
        &text[..text.len().min(300)]
    );
    assert!(
        text.contains("[possible values: bash, elvish, fish, powershell, zsh]"),
        "Help should list possible shell values, got: {}",
        &text[..text.len().min(500)]
    );

    insta::assert_snapshot!("completions_help", text);
    assert_exit_code(&s, 0);
}

// =====================================================================
// SAD PATH
// =====================================================================

/// Invoking `completions` with no shell argument shows the clap error
/// about a missing required argument, names `<SHELL>` as the missing
/// argument, and exits with clap's usage error code (2).
#[test]
fn completions_no_argument() {
    let mut s = spawn_bivvy_global(&["completions"]);
    let text = read_to_eof(&mut s);

    assert!(
        text.contains("the following required arguments were not provided"),
        "Missing shell arg should say 'the following required arguments were not provided', got: {}",
        &text[..text.len().min(300)]
    );
    assert!(
        text.contains("<SHELL>"),
        "Missing shell arg error should name '<SHELL>' as the missing argument, got: {}",
        &text[..text.len().min(300)]
    );

    insta::assert_snapshot!("completions_no_argument", text);
    assert_exit_code(&s, 2);
}

/// An unknown shell name shows the clap "invalid value" error, lists
/// the valid shells, and exits with clap's usage error code (2).
#[test]
fn completions_unknown_shell() {
    let mut s = spawn_bivvy_global(&["completions", "fakeshell"]);
    let text = read_to_eof(&mut s);

    assert!(
        text.contains("invalid value 'fakeshell' for '<SHELL>'"),
        "Unknown shell should say \"invalid value 'fakeshell' for '<SHELL>'\", got: {}",
        &text[..text.len().min(300)]
    );
    assert!(
        text.contains("[possible values: bash, elvish, fish, powershell, zsh]"),
        "Unknown shell error should list valid shells, got: {}",
        &text[..text.len().min(500)]
    );

    insta::assert_snapshot!("completions_unknown_shell", text);
    assert_exit_code(&s, 2);
}
