import subprocess
import time
from pathlib import Path
import pytest
from onako.tmux_orchestrator import TmuxOrchestrator

SESSION_NAME = "onako-test"


@pytest.fixture(autouse=True)
def cleanup(tmp_path):
    """Kill test tmux session before and after each test."""
    subprocess.run(["tmux", "kill-session", "-t", SESSION_NAME], capture_output=True)
    yield
    subprocess.run(["tmux", "kill-session", "-t", SESSION_NAME], capture_output=True)


@pytest.fixture
def orch(tmp_path):
    return TmuxOrchestrator(session_name=SESSION_NAME, db_path=tmp_path / "test.db")


def test_create_task_returns_id(orch):
    task = orch.create_task("echo hello")
    assert task["id"].startswith("task-")
    assert task["status"] == "running"
    assert task["prompt"] == "echo hello"


def test_list_tasks_shows_created_task(orch):
    task = orch.create_task("echo hello")
    tasks = orch.list_tasks()
    assert any(t["id"] == task["id"] for t in tasks)


def test_get_output_captures_pane(orch):
    task = orch.create_task("echo hello-from-test")
    time.sleep(1)
    output = orch.get_output(task["id"])
    assert "hello-from-test" in output


def test_get_raw_output(orch):
    task = orch.create_task("echo raw-test")
    time.sleep(1)
    raw = orch.get_raw_output(task["id"])
    assert "raw-test" in raw


def test_send_message(orch):
    task = orch.create_task("cat")  # cat waits for stdin
    time.sleep(0.5)
    orch.send_message(task["id"], "hello-input")
    time.sleep(1)
    output = orch.get_output(task["id"])
    assert "hello-input" in output


def test_send_interrupt(orch):
    task = orch.create_task("sleep 999")
    time.sleep(0.5)
    orch.send_interrupt(task["id"])
    time.sleep(0.5)
    # Window should still exist after interrupt
    tasks = orch.list_tasks()
    assert any(t["id"] == task["id"] and t["status"] == "running" for t in tasks)


def test_kill_task(orch):
    task = orch.create_task("sleep 999")
    orch.kill_task(task["id"])
    tasks = orch.list_tasks()
    assert not any(t["id"] == task["id"] and t["status"] == "running" for t in tasks)


def test_create_multiple_tasks(orch):
    t1 = orch.create_task("echo one")
    t2 = orch.create_task("echo two")
    tasks = orch.list_tasks()
    ids = [t["id"] for t in tasks]
    assert t1["id"] in ids
    assert t2["id"] in ids
    assert t1["id"] != t2["id"]


def test_created_task_has_managed_origin(orch):
    task = orch.create_task("echo hello")
    assert task["origin"] == "managed"


def test_rediscover_external_window(orch):
    """Windows not created by onako are discovered with origin=external."""
    subprocess.run([
        "tmux", "new-window", "-t", SESSION_NAME, "-n", "my-feature",
    ], check=True)
    time.sleep(0.5)
    orch.rediscover_tasks()
    tasks = orch.list_tasks()
    external = [t for t in tasks if t["id"] == "my-feature"]
    assert len(external) == 1
    assert external[0]["origin"] == "external"


def test_external_window_output(orch):
    """Can read output from externally created windows."""
    subprocess.run([
        "tmux", "new-window", "-t", SESSION_NAME, "-n", "ext-test",
    ], check=True)
    subprocess.run([
        "tmux", "send-keys", "-t", f"{SESSION_NAME}:ext-test",
        "echo external-output", "Enter",
    ], check=True)
    time.sleep(1)
    orch.rediscover_tasks()
    output = orch.get_output("ext-test")
    assert "external-output" in output


def test_external_window_send_message(orch):
    """Can send messages to externally created windows."""
    subprocess.run([
        "tmux", "new-window", "-t", SESSION_NAME, "-n", "ext-msg",
    ], check=True)
    subprocess.run([
        "tmux", "send-keys", "-t", f"{SESSION_NAME}:ext-msg",
        "cat", "Enter",
    ], check=True)
    time.sleep(0.5)
    orch.rediscover_tasks()
    orch.send_message("ext-msg", "hello-external")
    time.sleep(1)
    output = orch.get_output("ext-msg")
    assert "hello-external" in output


def test_external_window_kill(orch):
    """Can kill externally created windows."""
    subprocess.run([
        "tmux", "new-window", "-t", SESSION_NAME, "-n", "ext-kill",
    ], check=True)
    time.sleep(0.5)
    orch.rediscover_tasks()
    orch.kill_task("ext-kill")
    tasks = orch.list_tasks()
    assert not any(t["id"] == "ext-kill" and t["status"] == "running" for t in tasks)


def test_hide_task(orch):
    """Hiding a done task sets status to 'hidden'."""
    task = orch.create_task("echo hide-me")
    orch.kill_task(task["id"])
    assert orch.tasks[task["id"]]["status"] == "done"
    orch.hide_task(task["id"])
    assert orch.tasks[task["id"]]["status"] == "hidden"


def test_hide_task_not_running(orch):
    """Cannot hide a running task."""
    task = orch.create_task("sleep 999")
    with pytest.raises(ValueError, match="not done"):
        orch.hide_task(task["id"])


def test_hide_task_not_found(orch):
    """Hiding a nonexistent task raises."""
    with pytest.raises(ValueError, match="not found"):
        orch.hide_task("task-nonexistent")


def test_resume_task_reuses_same_record(orch):
    """Resuming a task reuses the original record instead of creating a new one."""
    task = orch.create_task("echo original")
    task_id = task["id"]
    # Simulate a claude_session_id (normally set by server)
    orch.tasks[task_id]["claude_session_id"] = "fake-session-id"
    orch._save_task(orch.tasks[task_id])
    # Kill the task so it becomes "done"
    orch.kill_task(task_id)
    assert orch.tasks[task_id]["status"] == "done"
    # Resume
    resumed = orch.resume_task(task_id)
    assert resumed["id"] == task_id  # same record
    assert resumed["status"] == "running"
    assert "(resumed)" in resumed["prompt"]
    # No new task record was created
    task_ids = [t["id"] for t in orch.list_tasks() if "original" in t["prompt"]]
    assert task_ids == [task_id]


def test_create_task_with_branch(orch):
    """Tasks can be created with branch and worktree_path."""
    task = orch.create_task("echo hello", branch="feature-x", worktree_path="/tmp/fake")
    assert task["branch"] == "feature-x"
    assert task["worktree_path"] == "/tmp/fake"


def test_create_task_without_branch(orch):
    """Tasks created without branch have None values."""
    task = orch.create_task("echo hello")
    assert task.get("branch") is None
    assert task.get("worktree_path") is None


def test_kill_task_saves_last_output(orch):
    """Killing a task snapshots the last output."""
    task = orch.create_task("echo snapshot-test-content")
    time.sleep(1)
    orch.kill_task(task["id"])
    assert orch.tasks[task["id"]]["status"] == "done"
    assert "snapshot-test-content" in orch.tasks[task["id"]].get("last_output", "")


def test_resume_clears_last_output(orch):
    """Resuming a task clears the saved last_output."""
    task = orch.create_task("echo resume-clear-test")
    task_id = task["id"]
    orch.tasks[task_id]["claude_session_id"] = "fake-session-id"
    orch._save_task(orch.tasks[task_id])
    time.sleep(1)
    orch.kill_task(task_id)
    assert orch.tasks[task_id].get("last_output")  # has snapshot
    orch.resume_task(task_id)
    assert orch.tasks[task_id].get("last_output") is None


def test_natural_finish_saves_last_output(orch):
    """When a task finishes naturally (window closes), last_output is saved."""
    task = orch.create_task("echo natural-finish-content && sleep 1 && exit 0")
    time.sleep(0.5)  # let echo run but before exit
    orch.list_tasks()  # sync tick caches output while window is alive
    time.sleep(2)  # wait for exit 0 to close the window
    orch.list_tasks()  # sync tick detects window gone, persists cached output
    t = orch.tasks[task["id"]]
    assert t["status"] == "done"
    assert "natural-finish-content" in t.get("last_output", "")


def test_resume_recreates_missing_worktree(orch, tmp_path):
    """Resume re-creates a worktree if it was cleaned up."""
    from unittest.mock import MagicMock

    task = orch.create_task("echo hello", branch="feat/test", worktree_path=str(tmp_path / "gone"))
    task_id = task["id"]
    orch.tasks[task_id]["claude_session_id"] = "fake-session-id"
    orch._save_task(orch.tasks[task_id])
    orch.kill_task(task_id)

    # Worktree dir doesn't exist (was cleaned)
    assert not (tmp_path / "gone").exists()

    mock_wm = MagicMock()
    mock_wm.ensure_worktree.return_value = tmp_path / "gone"
    orch.resume_task(task_id, worktree_manager=mock_wm)
    mock_wm.ensure_worktree.assert_called_once_with("feat/test")
