// NOTE: This whole file is compiled out by `#![cfg(any())]` (pre-existing gate) and is
// NOT part of the live build. It predates the `packs` -> `pack` noun rename and the
// removal of the `install`/`validate` verbs. As a surface migration we have rewritten
// the invocation strings to the live `pack` noun:
//   - `packs install --pack_id X --dry_run` -> `pack add <X>` (live verb is `add`; there
//     is no `--pack_id`/`--dry_run`/`install` — `pack add` takes a positional pack name).
//   - `packs list|show|search` -> `pack list|show|search`.
// The live `pack` noun has NO `validate` verb (validation lives under `policy validate`
// only — see crates/ggen-cli/src/cmds/policy.rs), so every `validate`-based test here is
// impossible on the current CLI and is additionally marked `#[ignore]` for the day the
// `cfg(any())` gate is lifted. The list/show JSON-shape assertions (5 packs,
// "startup-essentials"/"Startup Essentials") are pre-existing fixtures we did NOT verify
// against the live registry — UNVERIFIED; they remain gated off.
#![cfg(any())]
#![allow(
    clippy::unwrap_used,
    clippy::expect_used,
    clippy::panic,
    clippy::needless_raw_string_hashes,
    clippy::duration_suboptimal_units,
    clippy::branches_sharing_code,
    clippy::used_underscore_binding,
    clippy::single_char_pattern,
    clippy::ignore_without_reason,
    clippy::cloned_ref_to_slice_refs,
    clippy::doc_overindented_list_items,
    clippy::match_wildcard_for_single_variants,
    clippy::ignored_unit_patterns,
    clippy::needless_collect,
    clippy::unnecessary_map_or,
    clippy::manual_flatten,
    clippy::manual_strip,
    clippy::future_not_send,
    clippy::unnested_or_patterns,
    clippy::no_effect_underscore_binding,
    clippy::literal_string_with_formatting_args
)]
//! Minimal test suite for packs commands (80/20 focus)
//!
//! This test suite focuses on the critical 20% of functionality that delivers 80% of value:
//! - Each command returns valid JSON
//! - All 4 commands work end-to-end
//! - Invalid pack IDs return helpful errors
//! - Commands execute quickly (< 5000ms)

use serde_json::Value;
use std::process::Command;
use std::time::Instant;

// ============================================================================
// TEST UTILITIES
// ============================================================================

/// Execute `ggen pack <args>` and return stdout (noun migrated from removed `packs`).
fn run_packs_command(args: &[&str]) -> Result<String, String> {
    let output = Command::new("cargo")
        .args(["run", "--bin", "ggen", "--"])
        .args(["pack"])
        .args(args)
        .output()
        .map_err(|e| format!("Failed to execute command: {}", e))?;

    if output.status.success() {
        Ok(String::from_utf8_lossy(&output.stdout).to_string())
    } else {
        Err(String::from_utf8_lossy(&output.stderr).to_string())
    }
}

/// Parse JSON output from command
fn parse_json_output(output: &str) -> Result<Value, String> {
    serde_json::from_str(output).map_err(|e| format!("Invalid JSON: {}", e))
}

// ============================================================================
// UNIT TESTS - Valid JSON Output
// ============================================================================

#[test]
fn test_packs_list_returns_valid_json() {
    // Execute: ggen packs list
    let output = run_packs_command(&["list"]).expect("packs list should execute successfully");

    // Verify output is valid JSON
    let json = parse_json_output(&output).expect("Output should be valid JSON");

    // Verify contains expected structure
    assert!(json.get("packs").is_some(), "Should have 'packs' field");
    assert!(json.get("total").is_some(), "Should have 'total' field");

    // Verify contains 5 packs
    let packs = json["packs"].as_array().expect("packs should be an array");
    assert_eq!(packs.len(), 5, "Should have exactly 5 packs");

    // Verify each pack has required fields
    for pack in packs {
        assert!(pack.get("id").is_some(), "Pack should have 'id'");
        assert!(pack.get("name").is_some(), "Pack should have 'name'");
        assert!(
            pack.get("description").is_some(),
            "Pack should have 'description'"
        );
        assert!(
            pack.get("package_count").is_some(),
            "Pack should have 'package_count'"
        );
        assert!(
            pack.get("category").is_some(),
            "Pack should have 'category'"
        );
    }
}

#[test]
fn test_packs_show_returns_pack_details() {
    // Execute: ggen packs show --pack_id startup-essentials
    let output = run_packs_command(&["show", "--pack_id", "startup-essentials"])
        .expect("packs show should execute successfully");

    // Verify output is valid JSON
    let json = parse_json_output(&output).expect("Output should be valid JSON");

    // Verify pack details
    assert_eq!(
        json["id"], "startup-essentials",
        "Should have correct pack ID"
    );
    assert_eq!(
        json["name"], "Startup Essentials",
        "Should have correct pack name"
    );
    assert!(json.get("description").is_some(), "Should have description");
    assert!(json.get("category").is_some(), "Should have category");
    assert!(json.get("packages").is_some(), "Should have packages list");

    // Verify packages list
    let packages = json["packages"]
        .as_array()
        .expect("packages should be an array");
    assert!(!packages.is_empty(), "Should have at least one package");
    assert_eq!(
        json["package_count"],
        packages.len(),
        "package_count should match packages length"
    );
}

#[test]
#[ignore = "Live `pack add` (crates/ggen-cli/src/cmds/pack.rs::add) has no `--dry_run` \
and emits no 'DRY RUN'/'Would install' status — those assertions are fabricated. \
Invocation migrated to `pack add startup-essentials`. Real add-path coverage lives in \
proof_pack_test.rs::test_add_writes_lockfile_with_digest_and_emits_signed_receipt."]
fn test_packs_install_lists_packages() {
    // Migrated: `packs install --pack_id X --dry_run` -> `pack add X` (no such flags).
    let result = run_packs_command(&["add", "startup-essentials"]);

    // For now, accept either success or graceful failure due to marketplace unavailability
    // The important part is that --dry_run should not crash
    match result {
        Ok(output) => {
            // Verify output is valid JSON
            let json = parse_json_output(&output).expect("Output should be valid JSON");

            // Verify install output structure
            assert_eq!(
                json["pack_id"], "startup-essentials",
                "Should have correct pack ID"
            );
            assert!(json.get("pack_name").is_some(), "Should have pack_name");
            assert!(json.get("status").is_some(), "Should have status");

            // Verify status message contains dry run indicator
            let status = json["status"].as_str().expect("status should be string");
            assert!(
                status.contains("DRY RUN") || status.contains("Would install"),
                "Status should indicate dry run, got: {}",
                status
            );
        }
        Err(e) if e.contains("marketplace") => {
            // Marketplace registry unavailable is acceptable in test environment
            // The test validates that --dry_run doesn't cause panics
            eprintln!("Note: Marketplace unavailable in test environment: {}", e);
        }
        Err(e) => panic!("Unexpected error: {}", e),
    }
}

#[test]
#[ignore = "The live `pack` noun has NO `validate` verb (only `policy validate` exists, \
crates/ggen-cli/src/cmds/policy.rs). Intent (validate a pack by id) is impossible on the \
current CLI; no equivalent migration target. Invocation left referencing the removed \
verb intentionally — test stays ignored until/unless a `pack validate` verb is added."]
fn test_packs_validate_checks_pack() {
    // Execute: ggen pack validate --pack_id startup-essentials (REMOVED verb — see #[ignore]).
    let output = run_packs_command(&["validate", "--pack_id", "startup-essentials"])
        .expect("packs validate should execute successfully");

    // Verify output is valid JSON
    let json = parse_json_output(&output).expect("Output should be valid JSON");

    // Verify validation output structure
    assert_eq!(
        json["pack_id"], "startup-essentials",
        "Should have correct pack ID"
    );
    assert_eq!(json["valid"], true, "Pack should be valid");
    assert!(json.get("message").is_some(), "Should have message");
    assert!(
        json.get("package_count").is_some(),
        "Should have package_count"
    );

    // Verify message indicates success
    let message = json["message"].as_str().expect("message should be string");
    assert!(
        message.contains("valid"),
        "Message should indicate pack is valid"
    );
}

// ============================================================================
// EDGE CASE TESTS - Error Handling
// ============================================================================

#[test]
fn test_packs_invalid_id_returns_error() {
    // Execute: ggen packs show --pack_id nonexistent-pack
    let result = run_packs_command(&["show", "--pack_id", "nonexistent-pack"]);

    // Should return error (not panic)
    assert!(result.is_err(), "Invalid pack ID should return error");

    let error = result.unwrap_err();
    assert!(
        error.contains("Pack not found") || error.contains("nonexistent-pack"),
        "Error should mention pack not found or the invalid pack ID"
    );
}

#[test]
#[ignore = "The live `pack` noun has NO `validate` verb (only `policy validate` exists, \
crates/ggen-cli/src/cmds/policy.rs). Intent (validate detects an invalid pack) is \
impossible on the current CLI; no equivalent migration target. Stays ignored until a \
`pack validate` verb is added."]
fn test_packs_validate_invalid_pack_returns_false() {
    // Execute: ggen pack validate --pack_id invalid-pack-xyz (REMOVED verb — see #[ignore]).
    let output = run_packs_command(&["validate", "--pack_id", "invalid-pack-xyz"])
        .expect("validate should handle invalid packs gracefully");

    // Verify output is valid JSON
    let json = parse_json_output(&output).expect("Output should be valid JSON");

    // Verify validation correctly identifies invalid pack
    assert_eq!(
        json["pack_id"], "invalid-pack-xyz",
        "Should have correct pack ID"
    );
    assert_eq!(json["valid"], false, "Pack should be marked as invalid");
    assert!(
        json["package_count"].is_null(),
        "Invalid pack should have null package_count"
    );

    let message = json["message"].as_str().expect("message should be string");
    assert!(
        message.contains("not found"),
        "Message should indicate pack not found"
    );
}

// ============================================================================
// INTEGRATION TESTS - End-to-End
// ============================================================================

#[test]
#[ignore = "Step 4 calls the removed `validate` verb (no live `pack validate`; only \
`policy validate`, crates/ggen-cli/src/cmds/policy.rs). list/show were migrated to the \
live `pack` noun and `install --dry_run` to `pack add`, but the validate assertion \
cannot pass on the current CLI. Live list/show/add coverage lives in proof_pack_test.rs."]
fn test_packs_all_commands_work_end_to_end() {
    // Test complete workflow: list -> show -> add -> validate(REMOVED).

    // 1. List packs
    let list_output = run_packs_command(&["list"]).expect("list should work");
    let list_json = parse_json_output(&list_output).expect("list should return JSON");
    assert_eq!(list_json["total"], 5, "Should have 5 packs");

    // 2. Show first pack
    let pack_id = list_json["packs"][0]["id"]
        .as_str()
        .expect("First pack should have ID");
    let show_output = run_packs_command(&["show", "--pack_id", pack_id]).expect("show should work");
    let show_json = parse_json_output(&show_output).expect("show should return JSON");
    assert_eq!(show_json["id"], pack_id, "Should return correct pack");

    // 3. Add pack - skip assertions if marketplace unavailable
    //    (migrated from `install --pack_id X --dry_run`; live `add` takes a positional name).
    let _install_result = run_packs_command(&["add", pack_id]);
    // We don't assert here because marketplace might be unavailable in test environment
    // The key is that commands complete without panicking

    // 4. Validate pack (REMOVED verb — see #[ignore]).
    let validate_output =
        run_packs_command(&["validate", "--pack_id", pack_id]).expect("validate should work");
    let validate_json = parse_json_output(&validate_output).expect("validate should return JSON");
    assert_eq!(validate_json["valid"], true, "Pack should be valid");
}

#[test]
fn test_packs_list_with_category_filter() {
    // Execute: ggen packs list --category startup
    let output = run_packs_command(&["list", "--category", "startup"])
        .expect("list with category should work");

    let json = parse_json_output(&output).expect("Should return JSON");
    let packs = json["packs"].as_array().expect("Should have packs array");

    // Verify all returned packs match the category
    for pack in packs {
        let category = pack["category"]
            .as_str()
            .expect("Pack should have category");
        assert_eq!(
            category, "startup",
            "All packs should have 'startup' category"
        );
    }

    // Should have at least 1 startup pack
    assert!(!packs.is_empty(), "Should have at least one startup pack");
}

// ============================================================================
// PERFORMANCE TESTS - Speed
// ============================================================================

#[test]
#[ignore = "Exercises the removed `validate` verb (no live `pack validate`; only \
`policy validate`, crates/ggen-cli/src/cmds/policy.rs) with `.expect(...)`. list/show \
migrated to the live `pack` noun and `install --dry_run` to `pack add`, but the validate \
step cannot run on the current CLI. Stays ignored until a `pack validate` verb exists."]
fn test_packs_commands_execute_quickly() {
    // Each command should complete in < 60000ms (20 seconds, accounting for cargo build)

    // Test list command
    let start = Instant::now();
    run_packs_command(&["list"]).expect("list should work");
    let list_duration = start.elapsed();
    assert!(
        list_duration.as_millis() < 60000,
        "list should complete in < 60000ms (got {}ms)",
        list_duration.as_millis()
    );

    // Test show command
    let start = Instant::now();
    run_packs_command(&["show", "--pack_id", "startup-essentials"]).expect("show should work");
    let show_duration = start.elapsed();
    assert!(
        show_duration.as_millis() < 60000,
        "show should complete in < 60000ms (got {}ms)",
        show_duration.as_millis()
    );

    // Test add command (migrated from `install --pack_id X --dry_run`) -
    // may fail if marketplace unavailable, but should complete quickly.
    let start = Instant::now();
    let _install_result = run_packs_command(&["add", "startup-essentials"]);
    let install_duration = start.elapsed();
    assert!(
        install_duration.as_millis() < 60000,
        "add should complete in < 60000ms (got {}ms), even if marketplace unavailable",
        install_duration.as_millis()
    );

    // Test validate command (REMOVED verb — see #[ignore]).
    let start = Instant::now();
    run_packs_command(&["validate", "--pack_id", "startup-essentials"])
        .expect("validate should work");
    let validate_duration = start.elapsed();
    assert!(
        validate_duration.as_millis() < 60000,
        "validate should complete in < 60000ms (got {}ms)",
        validate_duration.as_millis()
    );
}

// ============================================================================
// DATA VALIDATION TESTS
// ============================================================================

#[test]
#[ignore = "Validates every listed pack via the removed `validate` verb (no live \
`pack validate`; only `policy validate`, crates/ggen-cli/src/cmds/policy.rs). The `list` \
call migrated to the live `pack` noun, but the per-pack validate loop cannot run on the \
current CLI. Stays ignored until a `pack validate` verb exists."]
fn test_packs_all_defined_packs_are_valid() {
    // Get all packs
    let output = run_packs_command(&["list"]).expect("list should work");
    let json = parse_json_output(&output).expect("Should return JSON");
    let packs = json["packs"].as_array().expect("Should have packs array");

    // Validate each pack
    for pack in packs {
        let pack_id = pack["id"].as_str().expect("Pack should have ID");

        let validate_output = run_packs_command(&["validate", "--pack_id", pack_id])
            .unwrap_or_else(|_| panic!("validate should work for pack {}", pack_id));
        let validate_json = parse_json_output(&validate_output)
            .unwrap_or_else(|_| panic!("validate should return JSON for pack {}", pack_id));

        assert_eq!(
            validate_json["valid"], true,
            "Pack {} should be valid",
            pack_id
        );
        assert!(
            validate_json["package_count"].as_u64().unwrap() > 0,
            "Pack {} should have at least one package",
            pack_id
        );
    }
}
