//! Tests for batch operations.

use assert_cmd::Command;
use predicates::prelude::*;
use serial_test::serial;
use std::fs;
use std::path::PathBuf;

#[macro_use]
mod common;
use common::{ensure_test_project, DaemonTestHarness};

const TEST_PROJECT: &str = "batch-test";
const TEST_PROGRAM: &str = "sample_binary";

fn create_batch_file(content: &str) -> PathBuf {
    let temp_dir = std::env::temp_dir();
    let batch_file = temp_dir.join(format!("ghidra_batch_{}.txt", std::process::id()));
    fs::write(&batch_file, content).expect("Failed to write batch file");
    batch_file
}

#[test]
#[serial]
fn test_batch_multiple_queries() {
    ensure_test_project(TEST_PROJECT, TEST_PROGRAM);

    let harness =
        DaemonTestHarness::new(TEST_PROJECT, TEST_PROGRAM).expect("Failed to start daemon");

    let batch_content = r#"
# Test batch file
query --address 0x100000
query --function main
"#;

    let batch_file = create_batch_file(batch_content);

    Command::cargo_bin("ghidra")
        .unwrap()
        .env("GHIDRA_CLI_SOCKET", harness.socket_path())
        .arg("batch")
        .arg(batch_file.to_str().unwrap())
        .assert()
        .success()
        .stdout(predicate::str::contains("commands_parsed"))
        .stdout(predicate::str::contains("results"));

    fs::remove_file(batch_file).ok();
    drop(harness);
}

#[test]
#[serial]
fn test_batch_empty_file() {
    ensure_test_project(TEST_PROJECT, TEST_PROGRAM);

    let harness =
        DaemonTestHarness::new(TEST_PROJECT, TEST_PROGRAM).expect("Failed to start daemon");

    let batch_content = r#"
# Only comments


# More comments
"#;

    let batch_file = create_batch_file(batch_content);

    Command::cargo_bin("ghidra")
        .unwrap()
        .env("GHIDRA_CLI_SOCKET", harness.socket_path())
        .arg("batch")
        .arg(batch_file.to_str().unwrap())
        .assert()
        .success()
        .stdout(predicate::str::contains("commands_parsed"));

    fs::remove_file(batch_file).ok();
    drop(harness);
}

#[test]
#[serial]
fn test_batch_with_comments() {
    ensure_test_project(TEST_PROJECT, TEST_PROGRAM);

    let harness =
        DaemonTestHarness::new(TEST_PROJECT, TEST_PROGRAM).expect("Failed to start daemon");

    let batch_content = r#"
# Query main function
query --function main
# Query by address
query --address 0x100000
# Another comment
"#;

    let batch_file = create_batch_file(batch_content);

    Command::cargo_bin("ghidra")
        .unwrap()
        .env("GHIDRA_CLI_SOCKET", harness.socket_path())
        .arg("batch")
        .arg(batch_file.to_str().unwrap())
        .assert()
        .success()
        .stdout(predicate::str::contains("commands_parsed"))
        .stdout(predicate::str::contains("2"));

    fs::remove_file(batch_file).ok();
    drop(harness);
}

#[test]
#[serial]
fn test_batch_invalid_file() {
    ensure_test_project(TEST_PROJECT, TEST_PROGRAM);

    let harness =
        DaemonTestHarness::new(TEST_PROJECT, TEST_PROGRAM).expect("Failed to start daemon");

    Command::cargo_bin("ghidra")
        .unwrap()
        .env("GHIDRA_CLI_SOCKET", harness.socket_path())
        .arg("batch")
        .arg("/nonexistent/batch/file.txt")
        .assert()
        .failure()
        .stderr(predicate::str::contains("not found").or(predicate::str::contains("No such file")));

    drop(harness);
}

#[test]
#[serial]
fn test_batch_with_invalid_command() {
    ensure_test_project(TEST_PROJECT, TEST_PROGRAM);

    let harness =
        DaemonTestHarness::new(TEST_PROJECT, TEST_PROGRAM).expect("Failed to start daemon");

    let batch_content = r#"
query --function main
invalid-command --arg value
query --address 0x100000
"#;

    let batch_file = create_batch_file(batch_content);

    Command::cargo_bin("ghidra")
        .unwrap()
        .env("GHIDRA_CLI_SOCKET", harness.socket_path())
        .arg("batch")
        .arg(batch_file.to_str().unwrap())
        .assert()
        .success()
        .stdout(predicate::str::contains("commands_parsed"))
        .stdout(predicate::str::contains("3"));

    fs::remove_file(batch_file).ok();
    drop(harness);
}
