import subprocess
import pytest
from fastapi.testclient import TestClient

API_SESSION = "onako-api-test"


@pytest.fixture(autouse=True)
def cleanup_session():
    subprocess.run(["tmux", "kill-session", "-t", API_SESSION], capture_output=True)
    yield
    subprocess.run(["tmux", "kill-session", "-t", API_SESSION], capture_output=True)


@pytest.fixture
def client(tmp_path):
    import os
    import importlib
    os.environ["ONAKO_SESSION"] = API_SESSION
    from onako import tmux_orchestrator
    original_db = tmux_orchestrator.DB_PATH
    tmux_orchestrator.DB_PATH = tmp_path / "test.db"
    from onako import server
    importlib.reload(server)
    client = TestClient(server.app)
    yield client
    response = client.get("/tasks")
    for task in response.json():
        if task["id"] != "onako-main":
            client.delete(f"/tasks/{task['id']}")
    tmux_orchestrator.DB_PATH = original_db


def test_health(client):
    r = client.get("/health")
    assert r.status_code == 200
    assert r.json()["status"] == "ok"


def test_create_task(client):
    r = client.post("/tasks", json={"prompt": "echo api-test"})
    assert r.status_code == 200
    assert r.json()["id"].startswith("task-")


def test_list_tasks(client):
    client.post("/tasks", json={"prompt": "echo one"})
    client.post("/tasks", json={"prompt": "echo two"})
    r = client.get("/tasks")
    assert r.status_code == 200
    assert len(r.json()) >= 2


def test_get_task(client):
    create = client.post("/tasks", json={"prompt": "echo detail-test"})
    task_id = create.json()["id"]
    import time
    time.sleep(1)
    r = client.get(f"/tasks/{task_id}")
    assert r.status_code == 200
    assert r.json()["id"] == task_id
    assert "output" in r.json()


def test_get_task_raw(client):
    create = client.post("/tasks", json={"prompt": "echo raw-api-test"})
    task_id = create.json()["id"]
    import time
    time.sleep(1)
    r = client.get(f"/tasks/{task_id}/raw")
    assert r.status_code == 200
    assert "output" in r.json()


def test_send_message(client):
    create = client.post("/tasks", json={"prompt": "cat"})
    task_id = create.json()["id"]
    r = client.post(f"/tasks/{task_id}/message", json={"message": "hello"})
    assert r.status_code == 200


def test_delete_task(client):
    create = client.post("/tasks", json={"prompt": "sleep 999"})
    task_id = create.json()["id"]
    r = client.delete(f"/tasks/{task_id}")
    assert r.status_code == 200


def test_interrupt_task(client):
    create = client.post("/tasks", json={"prompt": "sleep 999"})
    task_id = create.json()["id"]
    r = client.post(f"/tasks/{task_id}/interrupt")
    assert r.status_code == 200
    assert r.json()["status"] == "interrupted"


def test_interrupt_nonexistent_task(client):
    r = client.post("/tasks/task-nonexistent/interrupt")
    assert r.status_code == 404


def test_get_nonexistent_task(client):
    r = client.get("/tasks/task-nonexistent")
    assert r.status_code == 404


def test_health_includes_skip_permissions(client):
    r = client.get("/health")
    assert r.status_code == 200
    assert "skip_permissions" in r.json()
    assert r.json()["skip_permissions"] is False


def test_create_task_default_no_skip(client):
    """When skip_permissions_default is False, command should not include the flag."""
    from unittest.mock import patch, MagicMock
    from onako import server

    mock_create = MagicMock(return_value={"id": "task-fake", "status": "running"})
    with patch.object(server, "skip_permissions_default", False), \
         patch.object(server.orch, "create_task", mock_create):
        r = client.post("/tasks", json={"prompt": "echo test"})
        assert r.status_code == 200
        command = mock_create.call_args[1]["command"]
        assert command.startswith("claude --session-id ")
        assert command.endswith("'echo test'")
        assert "--dangerously-skip-permissions" not in command


def test_create_task_with_global_skip(client):
    """When skip_permissions_default is True, command should include the flag."""
    from unittest.mock import patch, MagicMock
    from onako import server

    mock_create = MagicMock(return_value={"id": "task-fake", "status": "running"})
    with patch.object(server, "skip_permissions_default", True), \
         patch.object(server.orch, "create_task", mock_create):
        r = client.post("/tasks", json={"prompt": "echo test"})
        assert r.status_code == 200
        command = mock_create.call_args[1]["command"]
        assert command.startswith("claude --dangerously-skip-permissions --session-id ")
        assert command.endswith("'echo test'")


def test_create_task_per_task_skip_override(client):
    """Per-task skip_permissions=True overrides global default of False."""
    from unittest.mock import patch, MagicMock
    from onako import server

    mock_create = MagicMock(return_value={"id": "task-fake", "status": "running"})
    with patch.object(server, "skip_permissions_default", False), \
         patch.object(server.orch, "create_task", mock_create):
        r = client.post("/tasks", json={"prompt": "echo test", "skip_permissions": True})
        assert r.status_code == 200
        command = mock_create.call_args[1]["command"]
        assert command.startswith("claude --dangerously-skip-permissions --session-id ")
        assert command.endswith("'echo test'")


def test_create_task_per_task_no_skip_override(client):
    """Per-task skip_permissions=False overrides global default of True."""
    from unittest.mock import patch, MagicMock
    from onako import server

    mock_create = MagicMock(return_value={"id": "task-fake", "status": "running"})
    with patch.object(server, "skip_permissions_default", True), \
         patch.object(server.orch, "create_task", mock_create):
        r = client.post("/tasks", json={"prompt": "echo test", "skip_permissions": False})
        assert r.status_code == 200
        command = mock_create.call_args[1]["command"]
        assert command.startswith("claude --session-id ")
        assert command.endswith("'echo test'")
        assert "--dangerously-skip-permissions" not in command


def test_get_done_task_returns_last_output(client):
    """Done tasks should return saved last_output instead of empty output."""
    import time
    create = client.post("/tasks", json={"prompt": "echo done-output-test"})
    task_id = create.json()["id"]
    time.sleep(1)
    # Kill the task (saves snapshot)
    client.delete(f"/tasks/{task_id}")
    # Fetch the done task — should have output
    r = client.get(f"/tasks/{task_id}")
    assert r.status_code == 200
    assert "done-output-test" in r.json().get("output", "")


def test_hide_task(client):
    create = client.post("/tasks", json={"prompt": "echo hide-api"})
    task_id = create.json()["id"]
    client.delete(f"/tasks/{task_id}")
    r = client.post(f"/tasks/{task_id}/hide")
    assert r.status_code == 200
    assert r.json()["status"] == "hidden"


def test_hide_running_task_fails(client):
    create = client.post("/tasks", json={"prompt": "sleep 999"})
    task_id = create.json()["id"]
    r = client.post(f"/tasks/{task_id}/hide")
    assert r.status_code == 400


def test_hide_nonexistent_task(client):
    r = client.post("/tasks/task-nonexistent/hide")
    assert r.status_code == 404


# Worktree API tests


def test_list_worktrees_no_git_repo(client):
    """List worktrees should return empty list when not in a git repo."""
    from unittest.mock import patch
    from onako import server

    with patch.object(server, "worktree_manager", None):
        r = client.get("/worktrees")
        assert r.status_code == 200
        assert r.json() == []


def test_list_worktrees_with_worktrees(client):
    """List worktrees should return worktree info when manager is available."""
    from unittest.mock import patch, MagicMock
    from pathlib import Path
    from onako import server

    mock_manager = MagicMock()
    mock_manager.list_worktrees.return_value = [
        {"branch": "feature-a", "path": Path("/repo--feature-a")},
        {"branch": "feature-b", "path": Path("/repo--feature-b")},
    ]
    with patch.object(server, "worktree_manager", mock_manager):
        r = client.get("/worktrees")
        assert r.status_code == 200
        result = r.json()
        assert len(result) == 2
        assert result[0]["branch"] == "feature-a"
        assert result[0]["path"] == "/repo--feature-a"
        assert result[1]["branch"] == "feature-b"


def test_delete_worktree_no_git_repo(client):
    """Delete worktree should return 400 when not in a git repo."""
    from unittest.mock import patch
    from onako import server

    with patch.object(server, "worktree_manager", None):
        r = client.delete("/worktrees/feature-a")
        assert r.status_code == 400
        assert "Not in a git repository" in r.json()["detail"]


def test_delete_worktree_success(client):
    """Delete worktree should call delete_worktree on the manager."""
    from unittest.mock import patch, MagicMock
    from onako import server

    mock_manager = MagicMock()
    with patch.object(server, "worktree_manager", mock_manager):
        r = client.delete("/worktrees/feature-a")
        assert r.status_code == 200
        assert r.json()["status"] == "deleted"
        mock_manager.delete_worktree.assert_called_once_with("feature-a")


def test_delete_worktree_with_slash_in_branch(client):
    """Delete worktree should handle branches with slashes."""
    from unittest.mock import patch, MagicMock
    from onako import server

    mock_manager = MagicMock()
    with patch.object(server, "worktree_manager", mock_manager):
        r = client.delete("/worktrees/feature/nested/branch")
        assert r.status_code == 200
        mock_manager.delete_worktree.assert_called_once_with("feature/nested/branch")


def test_create_task_with_branch(client):
    """Create task with branch should use worktree and pass branch to orchestrator."""
    from unittest.mock import patch, MagicMock
    from pathlib import Path
    from onako import server

    mock_manager = MagicMock()
    mock_manager.ensure_worktree.return_value = Path("/repo--my-feature")

    mock_create = MagicMock(return_value={"id": "task-fake", "status": "running"})

    with patch.object(server, "worktree_manager", mock_manager), \
         patch.object(server.orch, "create_task", mock_create):
        r = client.post("/tasks", json={"prompt": "implement feature", "branch": "my-feature"})
        assert r.status_code == 200
        mock_manager.ensure_worktree.assert_called_once_with("my-feature", "main")
        # Check that orchestrator was called with branch and worktree_path
        call_kwargs = mock_create.call_args[1]
        assert call_kwargs["working_dir"] == "/repo--my-feature"
        assert call_kwargs["branch"] == "my-feature"
        assert call_kwargs["worktree_path"] == "/repo--my-feature"


def test_create_task_with_branch_and_base(client):
    """Create task with branch and base_branch should pass both to ensure_worktree."""
    from unittest.mock import patch, MagicMock
    from pathlib import Path
    from onako import server

    mock_manager = MagicMock()
    mock_manager.ensure_worktree.return_value = Path("/repo--my-feature")

    mock_create = MagicMock(return_value={"id": "task-fake", "status": "running"})

    with patch.object(server, "worktree_manager", mock_manager), \
         patch.object(server.orch, "create_task", mock_create):
        r = client.post("/tasks", json={
            "prompt": "implement feature",
            "branch": "my-feature",
            "base_branch": "develop"
        })
        assert r.status_code == 200
        mock_manager.ensure_worktree.assert_called_once_with("my-feature", "develop")


def test_create_task_with_branch_no_worktree_manager(client):
    """Create task with branch should ignore branch when no worktree manager."""
    from unittest.mock import patch, MagicMock
    from onako import server

    mock_create = MagicMock(return_value={"id": "task-fake", "status": "running"})

    with patch.object(server, "worktree_manager", None), \
         patch.object(server.orch, "create_task", mock_create):
        r = client.post("/tasks", json={"prompt": "implement feature", "branch": "my-feature"})
        assert r.status_code == 200
        # Should still work, but branch/worktree_path should be None
        call_kwargs = mock_create.call_args[1]
        assert call_kwargs.get("branch") is None
        assert call_kwargs.get("worktree_path") is None


def test_create_task_with_working_dir_and_branch(client):
    """When both working_dir and branch provided, branch worktree should take precedence."""
    from unittest.mock import patch, MagicMock
    from pathlib import Path
    from onako import server

    mock_manager = MagicMock()
    mock_manager.ensure_worktree.return_value = Path("/repo--my-feature")

    mock_create = MagicMock(return_value={"id": "task-fake", "status": "running"})

    with patch.object(server, "worktree_manager", mock_manager), \
         patch.object(server.orch, "create_task", mock_create):
        r = client.post("/tasks", json={
            "prompt": "implement feature",
            "branch": "my-feature",
            "working_dir": "/some/other/dir"
        })
        assert r.status_code == 200
        # Worktree path should override working_dir
        call_kwargs = mock_create.call_args[1]
        assert call_kwargs["working_dir"] == "/repo--my-feature"


def test_branch_worktree_integration(client, tmp_path):
    """Full integration: create task with branch, verify worktree."""
    import os

    # Create a test git repo
    repo = tmp_path / "test-repo"
    repo.mkdir()
    subprocess.run(["git", "init"], cwd=repo, check=True, capture_output=True)
    subprocess.run(["git", "config", "user.email", "test@test.com"], cwd=repo, check=True, capture_output=True)
    subprocess.run(["git", "config", "user.name", "Test"], cwd=repo, check=True, capture_output=True)
    (repo / "README.md").write_text("# Test")
    subprocess.run(["git", "add", "."], cwd=repo, check=True, capture_output=True)
    subprocess.run(["git", "commit", "-m", "initial"], cwd=repo, check=True, capture_output=True)

    # Patch worktree_manager to use test repo
    from onako import server
    from onako.worktree_manager import WorktreeManager
    original_wm = server.worktree_manager
    server.worktree_manager = WorktreeManager(repo)

    try:
        # Create task with branch
        r = client.post("/tasks", json={"prompt": "echo test", "branch": "test-feature"})
        assert r.status_code == 200
        task = r.json()
        assert task["branch"] == "test-feature"
        assert "test-repo--test-feature" in task["worktree_path"]

        # Verify worktree was created
        r = client.get("/worktrees")
        assert r.status_code == 200
        worktrees = r.json()
        assert any(wt["branch"] == "test-feature" for wt in worktrees)
    finally:
        server.worktree_manager = original_wm
        subprocess.run(["git", "worktree", "prune"], cwd=repo, capture_output=True)


def test_stats_endpoint(client):
    """Stats endpoint should return valid stats structure."""
    # Create a couple tasks to generate events
    client.post("/tasks", json={"prompt": "echo stats-test-1"})
    client.post("/tasks", json={"prompt": "echo stats-test-2"})

    r = client.get("/stats")
    assert r.status_code == 200
    stats = r.json()

    # Check structure
    assert "volume" in stats
    assert "records" in stats
    assert "trends" in stats
    assert "heatmap" in stats
    assert "activity_by_hour" in stats

    # Volume should reflect created tasks (at least 2, maybe more from onako-main)
    assert stats["volume"]["total_tasks"] >= 2
    assert len(stats["activity_by_hour"]) == 24


def test_stats_message_tracking(client):
    """Stats should count messages sent."""
    create = client.post("/tasks", json={"prompt": "cat"})
    task_id = create.json()["id"]
    client.post(f"/tasks/{task_id}/message", json={"message": "hello"})
    client.post(f"/tasks/{task_id}/message", json={"message": "world"})

    r = client.get("/stats")
    stats = r.json()
    assert stats["volume"]["total_messages_sent"] >= 2


def test_resume_passes_worktree_manager(client):
    """Resume endpoint passes worktree_manager to orchestrator."""
    from unittest.mock import patch, MagicMock
    from onako import server

    mock_resume = MagicMock(return_value={"id": "task-fake", "status": "running", "prompt": "(resumed) test"})
    server.orch.tasks["task-fake"] = {"id": "task-fake", "status": "done", "prompt": "test"}

    with patch.object(server.orch, "resume_task", mock_resume):
        r = client.post("/tasks/task-fake/resume")
        assert r.status_code == 200
        # Verify worktree_manager was passed
        call_kwargs = mock_resume.call_args
        assert "worktree_manager" in call_kwargs.kwargs or len(call_kwargs.args) >= 3 or \
               call_kwargs[1].get("worktree_manager") is not None
