use std::process::{Child, Command, Stdio};
use std::thread;
use std::time::{Duration, Instant};

use assert_cmd::cargo::cargo_bin;
use predicates::str::contains;
use tempfile::TempDir;

fn start_daemon(home: &TempDir) -> Child {
    let mut child = Command::new(cargo_bin("multi-brain"))
        .arg("--home")
        .arg(home.path())
        .arg("daemon")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
        .expect("spawn daemon");

    let socket = home.path().join("multi-brain.sock");
    let deadline = Instant::now() + Duration::from_secs(5);
    while !socket.exists() && Instant::now() < deadline {
        thread::sleep(Duration::from_millis(25));
        if let Some(status) = child.try_wait().unwrap() {
            panic!("daemon exited early: {status}");
        }
    }
    assert!(socket.exists(), "daemon socket was not created");
    child
}

fn mb(home: &TempDir) -> assert_cmd::Command {
    let mut cmd = assert_cmd::Command::cargo_bin("multi-brain").unwrap();
    cmd.arg("--home").arg(home.path());
    cmd
}

fn output(home: &TempDir, args: &[&str]) -> String {
    let mut cmd = mb(home);
    cmd.args(args);
    let output = cmd.output().unwrap();
    assert!(
        output.status.success(),
        "command failed: {}\nstderr:{}",
        args.join(" "),
        String::from_utf8_lossy(&output.stderr)
    );
    String::from_utf8(output.stdout).unwrap()
}

#[test]
fn cli_register_spawn_list_ask_stop_with_mock_claude() {
    let home = tempfile::tempdir().unwrap();
    let cwd = tempfile::tempdir().unwrap();
    let mut daemon = start_daemon(&home);
    let mock = std::env::current_dir()
        .unwrap()
        .join("tests/fixtures/mock-claude");

    mb(&home)
        .args([
            "register",
            "--name",
            "reviewer",
            "--kind",
            "claude",
            "--command",
        ])
        .arg(mock)
        .args(["--cwd"])
        .arg(cwd.path())
        .args(["--personality", "critical reviewer"])
        .assert()
        .success()
        .stdout(contains("registered"));

    mb(&home)
        .args(["spawn", "reviewer"])
        .assert()
        .success()
        .stdout(contains("\"alive\": true"));

    mb(&home)
        .arg("list")
        .assert()
        .success()
        .stdout(contains("reviewer"));

    mb(&home)
        .args(["ask", "codex-main", "reviewer", "look at this design"])
        .assert()
        .success()
        .stdout(contains("asked"));

    mb(&home)
        .args(["messages", "reviewer"])
        .assert()
        .success()
        .stdout(contains("look at this design"));

    let events = output(&home, &["events", "reviewer", "--max", "20"]);
    assert!(events.contains("\"stream\": \"stdin\""));
    assert!(events.contains("look at this design"));

    mb(&home)
        .args(["stop", "reviewer"])
        .assert()
        .success()
        .stdout(contains("stopped"));

    let _ = daemon.kill();
}

#[test]
fn cli_auto_starts_daemon_headlessly() {
    let home = tempfile::tempdir().unwrap();
    mb(&home)
        .arg("ping")
        .assert()
        .success()
        .stdout(contains("pong"));
    assert!(home.path().join("multi-brain.sock").exists());
    assert!(home.path().join("logs/daemon.log").exists());
    mb(&home)
        .arg("list")
        .assert()
        .success()
        .stdout(contains("list"));
}

#[test]
fn cli_can_spawn_mock_codex() {
    let home = tempfile::tempdir().unwrap();
    let cwd = tempfile::tempdir().unwrap();
    let mut daemon = start_daemon(&home);
    let mock = std::env::current_dir()
        .unwrap()
        .join("tests/fixtures/mock-codex");

    mb(&home)
        .args([
            "ensure",
            "--name",
            "codexer",
            "--kind",
            "codex",
            "--command",
        ])
        .arg(mock)
        .args(["--cwd"])
        .arg(cwd.path())
        .args(["--personality", "rust maintainer"])
        .assert()
        .success()
        .stdout(contains("codexer"));

    mb(&home).args(["stop", "codexer"]).assert().success();

    let _ = daemon.kill();
}

#[test]
fn cli_injects_mcp_config_for_agent_workspaces() {
    let home = tempfile::tempdir().unwrap();
    let cwd = tempfile::tempdir().unwrap();

    mb(&home)
        .args(["inject-mcp", "--cwd"])
        .arg(cwd.path())
        .args(["--server-name", "multi-brain"])
        .assert()
        .success()
        .stdout(contains("injected_mcp"));

    let raw = std::fs::read_to_string(cwd.path().join(".mcp.json")).unwrap();
    assert!(raw.contains("multi-brain"));
    assert!(raw.contains(home.path().to_str().unwrap()));
}
