//! Integration tests for CLI pack commands

#![allow(deprecated)]

use assert_cmd::Command;
use predicates::prelude::*;
use serde_json::json;
use wiremock::{
    matchers::{body_json, method, path},
    Mock, ResponseTemplate,
};

mod common;
use common::*;

#[tokio::test]
async fn test_pack_list_authenticated() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    // Mock pack list endpoint
    mock_pack_list(&fixture.mock_server).await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("list");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("core"))
        .stdout(predicate::str::contains("linux"));
}

#[tokio::test]
async fn test_pack_list_unauthenticated() {
    let fixture = TestFixture::new().await;
    fixture.write_default_config();

    // Mock unauthorized response
    mock_unauthorized(&fixture.mock_server, "/api/v1/packs").await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("list");

    cmd.assert().failure();
}

#[tokio::test]
async fn test_pack_list_json_output() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    // Mock pack list endpoint
    mock_pack_list(&fixture.mock_server).await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("--json")
        .arg("pack")
        .arg("list");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains(r#""ref": "core""#))
        .stdout(predicate::str::contains(r#""ref": "linux""#));
}

#[tokio::test]
async fn test_pack_list_yaml_output() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    // Mock pack list endpoint
    mock_pack_list(&fixture.mock_server).await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("--yaml")
        .arg("pack")
        .arg("list");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("ref: core"))
        .stdout(predicate::str::contains("ref: linux"));
}

#[tokio::test]
async fn test_pack_get_by_ref() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    // Mock pack get endpoint
    mock_pack_get(&fixture.mock_server, "core").await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("show")
        .arg("core");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("core"))
        .stdout(predicate::str::contains("core pack"));
}

#[tokio::test]
async fn test_pack_get_not_found() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    // Mock 404 response
    mock_not_found(&fixture.mock_server, "/api/v1/packs/nonexistent").await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("show")
        .arg("nonexistent");

    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("Error"));
}

#[tokio::test]
async fn test_pack_list_with_profile() {
    let fixture = TestFixture::new().await;

    // Create multi-profile config with authentication on default
    let config = format!(
        r#"
current_profile: staging
default_output_format: table
profiles:
  default:
    api_url: {}
    auth_token: valid_token
    refresh_token: refresh_token
    description: Default server
  staging:
    api_url: {}
    auth_token: staging_token
    refresh_token: staging_refresh
    description: Staging server
"#,
        fixture.server_url(),
        fixture.server_url()
    );
    fixture.write_config(&config);

    // Mock pack list endpoint
    mock_pack_list(&fixture.mock_server).await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--profile")
        .arg("staging")
        .arg("pack")
        .arg("list");

    cmd.assert().success();
}

#[tokio::test]
async fn test_pack_list_with_api_url_override() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    // Mock pack list endpoint
    mock_pack_list(&fixture.mock_server).await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("list");

    cmd.assert().success();
}

#[tokio::test]
async fn test_pack_get_json_output() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    // Mock pack get endpoint
    mock_pack_get(&fixture.mock_server, "core").await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("-j")
        .arg("pack")
        .arg("show")
        .arg("core");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains(r#""ref": "core""#));
}

// ── pack create tests ──────────────────────────────────────────────────

#[tokio::test]
async fn test_pack_create_non_interactive() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    mock_pack_create(&fixture.mock_server).await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("create")
        .arg("--ref")
        .arg("my_pack")
        .arg("--label")
        .arg("My Pack")
        .arg("--description")
        .arg("A test pack")
        .arg("--pack-version")
        .arg("0.1.0")
        .arg("--tags")
        .arg("test");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("my_pack"))
        .stdout(predicate::str::contains("created successfully"));
}

#[tokio::test]
async fn test_pack_create_json_output() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    mock_pack_create(&fixture.mock_server).await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("--json")
        .arg("pack")
        .arg("create")
        .arg("--ref")
        .arg("my_pack");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains(r#""ref": "my_pack""#))
        .stdout(predicate::str::contains(r#""id": 42"#));
}

#[tokio::test]
async fn test_pack_create_conflict() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    mock_pack_create_conflict(&fixture.mock_server).await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("create")
        .arg("--ref")
        .arg("my_pack");

    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("already exists"));
}

#[tokio::test]
async fn test_pack_create_missing_ref() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("create");

    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("Pack ref is required"));
}

#[tokio::test]
async fn test_pack_create_default_label_from_ref() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    // Use a custom mock that validates the request body contains the derived label
    Mock::given(method("POST"))
        .and(path("/api/v1/packs"))
        .and(body_json(json!({
            "ref": "my_cool_pack",
            "label": "My Cool Pack",
            "version": "0.1.0",
            "tags": []
        })))
        .respond_with(ResponseTemplate::new(201).set_body_json(json!({
            "data": {
                "id": 99,
                "ref": "my_cool_pack",
                "label": "My Cool Pack",
                "version": "0.1.0",
                "enabled": true,
                "created": "2024-01-01T00:00:00Z",
                "updated": "2024-01-01T00:00:00Z"
            }
        })))
        .mount(&fixture.mock_server)
        .await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("create")
        .arg("--ref")
        .arg("my_cool_pack");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("my_cool_pack"))
        .stdout(predicate::str::contains("created successfully"));
}

#[tokio::test]
async fn test_pack_create_default_version() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    // Verify the default version "0.1.0" is sent when --pack-version is not specified
    Mock::given(method("POST"))
        .and(path("/api/v1/packs"))
        .and(body_json(json!({
            "ref": "versioned_pack",
            "label": "Versioned Pack",
            "version": "0.1.0",
            "tags": []
        })))
        .respond_with(ResponseTemplate::new(201).set_body_json(json!({
            "data": {
                "id": 7,
                "ref": "versioned_pack",
                "label": "Versioned Pack",
                "version": "0.1.0",
                "enabled": true,
                "created": "2024-01-01T00:00:00Z",
                "updated": "2024-01-01T00:00:00Z"
            }
        })))
        .mount(&fixture.mock_server)
        .await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("create")
        .arg("--ref")
        .arg("versioned_pack");

    cmd.assert().success();
}

#[tokio::test]
async fn test_pack_create_with_tags() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    Mock::given(method("POST"))
        .and(path("/api/v1/packs"))
        .and(body_json(json!({
            "ref": "tagged",
            "label": "Tagged",
            "version": "0.1.0",
            "tags": ["networking", "monitoring"]
        })))
        .respond_with(ResponseTemplate::new(201).set_body_json(json!({
            "data": {
                "id": 10,
                "ref": "tagged",
                "label": "Tagged",
                "version": "0.1.0",
                "tags": ["networking", "monitoring"],
                "enabled": true,
                "created": "2024-01-01T00:00:00Z",
                "updated": "2024-01-01T00:00:00Z"
            }
        })))
        .mount(&fixture.mock_server)
        .await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("create")
        .arg("--ref")
        .arg("tagged")
        .arg("--tags")
        .arg("networking,monitoring");

    cmd.assert().success();
}

#[tokio::test]
async fn test_pack_list_empty_result() {
    let fixture = TestFixture::new().await;
    fixture.write_authenticated_config("valid_token", "refresh_token");

    // Mock empty pack list
    use serde_json::json;
    use wiremock::{
        matchers::{method, path},
        Mock, ResponseTemplate,
    };

    Mock::given(method("GET"))
        .and(path("/api/v1/packs"))
        .respond_with(ResponseTemplate::new(200).set_body_json(json!({
            "data": []
        })))
        .mount(&fixture.mock_server)
        .await;

    let mut cmd = Command::cargo_bin("attune").unwrap();
    cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
        .env("HOME", fixture.config_dir_path())
        .arg("--api-url")
        .arg(fixture.server_url())
        .arg("pack")
        .arg("list");

    cmd.assert().success();
}
