"""API integration tests for Tickets & Sprints endpoints.

Tests the full HTTP API surface for ticket/sprint management:
  - CRUD via /dashboard/tickets and /dashboard/sprints
  - Tool assignment via /dashboard/tickets/{id}/assign
  - Comments via /dashboard/tickets/{id}/comment
  - Board view via /dashboard/tickets/board/view
  - Sprint stats via /dashboard/sprints/{id}
  - Filtering by status, priority, tool, tag, sprint
"""

from __future__ import annotations

import pytest
from unittest.mock import patch
from fastapi.testclient import TestClient

with patch.dict("os.environ", {
    "AI_PROXY_MASTER_KEY": "sk-test-tickets",
    "ANTHROPIC_API_KEY": "sk-ant-test",
    "REDIS_URL": "redis://localhost:6379/0",
}):
    from proxym.main import app

AUTH = {"Authorization": "Bearer sk-test-tickets"}


@pytest.fixture(autouse=True)
def _reset_ticket_state():
    """Reset ticket manager between tests."""
    import proxym.dashboard as dash
    dash._ticket_mgr = None
    from proxym.dashboard.tickets import TicketManager
    dash._ticket_mgr = TicketManager()  # in-memory, no persistence
    yield
    dash._ticket_mgr = None


@pytest.fixture
def client():
    return TestClient(app)


# ── Ticket CRUD via API ──────────────────────────────────────

class TestTicketAPI:
    def test_list_tickets_empty(self, client):
        r = client.get("/dashboard/tickets")
        assert r.status_code == 200
        assert r.json()["tickets"] == []

    def test_create_ticket(self, client):
        r = client.post("/dashboard/tickets", json={
            "title": "Fix auth bug",
            "description": "OAuth redirect broken",
            "priority": "high",
            "type": "bug",
            "tags": ["auth", "urgent"],
            "files": ["src/auth/login.py"],
            "estimated_hours": 2.0,
        })
        assert r.status_code == 200
        data = r.json()
        assert data["title"] == "Fix auth bug"
        assert data["priority"] == "high"
        assert data["type"] == "bug"
        assert data["tags"] == ["auth", "urgent"]
        assert data["id"].startswith("TKT-")

    def test_get_ticket(self, client):
        create_r = client.post("/dashboard/tickets", json={"title": "Test"})
        ticket_id = create_r.json()["id"]

        r = client.get(f"/dashboard/tickets/{ticket_id}")
        assert r.status_code == 200
        assert r.json()["title"] == "Test"
        assert "comments" in r.json()

    def test_get_ticket_not_found(self, client):
        r = client.get("/dashboard/tickets/TKT-FAKE00")
        assert r.status_code == 404

    def test_update_ticket(self, client):
        create_r = client.post("/dashboard/tickets", json={"title": "Original"})
        ticket_id = create_r.json()["id"]

        r = client.put(f"/dashboard/tickets/{ticket_id}", json={
            "title": "Updated",
            "status": "in_progress",
            "priority": "critical",
        })
        assert r.status_code == 200
        assert r.json()["title"] == "Updated"
        assert r.json()["status"] == "in_progress"
        assert r.json()["priority"] == "critical"

    def test_update_ticket_not_found(self, client):
        r = client.put("/dashboard/tickets/TKT-FAKE00", json={"title": "X"})
        assert r.status_code == 404

    def test_delete_ticket(self, client):
        create_r = client.post("/dashboard/tickets", json={"title": "Delete me"})
        ticket_id = create_r.json()["id"]

        r = client.delete(f"/dashboard/tickets/{ticket_id}")
        assert r.status_code == 200
        assert r.json()["ok"] is True

        r2 = client.get(f"/dashboard/tickets/{ticket_id}")
        assert r2.status_code == 404

    def test_delete_ticket_not_found(self, client):
        r = client.delete("/dashboard/tickets/TKT-FAKE00")
        assert r.status_code == 404


# ── Tool Assignment via API ───────────────────────────────────

class TestToolAssignmentAPI:
    def test_assign_tool(self, client):
        create_r = client.post("/dashboard/tickets", json={"title": "Assign me"})
        ticket_id = create_r.json()["id"]

        r = client.post(f"/dashboard/tickets/{ticket_id}/assign", json={
            "tool": "aider",
            "agent_mode": "code",
            "model": "claude-sonnet-4-6",
        })
        assert r.status_code == 200
        assert r.json()["ok"] is True
        ticket = r.json()["ticket"]
        assert ticket["assigned_tool"]["tool"] == "aider"
        assert ticket["status"] == "in_progress"

    def test_assign_vscode(self, client):
        create_r = client.post("/dashboard/tickets", json={"title": "VS Code task"})
        ticket_id = create_r.json()["id"]

        r = client.post(f"/dashboard/tickets/{ticket_id}/assign", json={
            "tool": "vscode",
            "agent_mode": "debug",
        })
        assert r.status_code == 200
        assert r.json()["ticket"]["assigned_tool"]["tool"] == "vscode"

    def test_assign_claude_code(self, client):
        create_r = client.post("/dashboard/tickets", json={"title": "Claude Code task"})
        ticket_id = create_r.json()["id"]

        r = client.post(f"/dashboard/tickets/{ticket_id}/assign", json={
            "tool": "claude-code",
            "model": "claude-opus-4-6",
        })
        assert r.status_code == 200
        assert r.json()["ticket"]["assigned_tool"]["tool"] == "claude-code"

    def test_assign_not_found(self, client):
        r = client.post("/dashboard/tickets/TKT-FAKE00/assign", json={"tool": "aider"})
        assert r.status_code == 404


# ── Comments via API ──────────────────────────────────────────

class TestCommentsAPI:
    def test_add_comment(self, client):
        create_r = client.post("/dashboard/tickets", json={"title": "With comments"})
        ticket_id = create_r.json()["id"]

        r = client.post(f"/dashboard/tickets/{ticket_id}/comment", json={
            "author": "aider",
            "content": "Fixed the bug in login.py",
        })
        assert r.status_code == 200
        assert r.json()["ok"] is True

        detail = client.get(f"/dashboard/tickets/{ticket_id}").json()
        assert len(detail["comments"]) == 1
        assert detail["comments"][0]["author"] == "aider"

    def test_add_comment_not_found(self, client):
        r = client.post("/dashboard/tickets/TKT-FAKE00/comment", json={
            "author": "test",
            "content": "test",
        })
        assert r.status_code == 404


# ── Filtering via API ─────────────────────────────────────────

class TestFilteringAPI:
    def _seed_tickets(self, client):
        client.post("/dashboard/tickets", json={
            "title": "Bug A", "status": "todo", "priority": "high", "type": "bug", "tags": ["auth"],
        })
        client.post("/dashboard/tickets", json={
            "title": "Feature B", "status": "in_progress", "priority": "medium", "type": "feature", "tags": ["ui"],
        })
        client.post("/dashboard/tickets", json={
            "title": "Task C", "status": "done", "priority": "low", "type": "task", "tags": ["auth"],
        })

    def test_filter_by_status(self, client):
        self._seed_tickets(client)
        r = client.get("/dashboard/tickets?status=todo")
        assert r.status_code == 200
        assert len(r.json()["tickets"]) == 1

    def test_filter_by_priority(self, client):
        self._seed_tickets(client)
        r = client.get("/dashboard/tickets?priority=high")
        assert len(r.json()["tickets"]) == 1

    def test_filter_by_tag(self, client):
        self._seed_tickets(client)
        r = client.get("/dashboard/tickets?tag=auth")
        assert len(r.json()["tickets"]) == 2


# ── Board View via API ────────────────────────────────────────

class TestBoardAPI:
    def test_board_view_empty(self, client):
        r = client.get("/dashboard/tickets/board/view")
        assert r.status_code == 200
        data = r.json()
        assert "todo" in data
        assert "done" in data
        assert all(isinstance(v, list) for v in data.values())

    def test_board_view_with_tickets(self, client):
        client.post("/dashboard/tickets", json={"title": "A", "status": "todo"})
        client.post("/dashboard/tickets", json={"title": "B", "status": "in_progress"})
        client.post("/dashboard/tickets", json={"title": "C", "status": "done"})

        r = client.get("/dashboard/tickets/board/view")
        data = r.json()
        assert len(data["todo"]) == 1
        assert len(data["in_progress"]) == 1
        assert len(data["done"]) == 1


# ── Sprint CRUD via API ──────────────────────────────────────

class TestSprintAPI:
    def test_list_sprints_empty(self, client):
        r = client.get("/dashboard/sprints")
        assert r.status_code == 200
        assert r.json()["sprints"] == []

    def test_create_sprint(self, client):
        r = client.post("/dashboard/sprints", json={
            "name": "Sprint 1",
            "goal": "Fix auth module",
            "start_date": "2026-03-23",
            "end_date": "2026-04-06",
        })
        assert r.status_code == 200
        data = r.json()
        assert data["name"] == "Sprint 1"
        assert data["id"].startswith("SPR-")
        assert data["progress_pct"] == 0

    def test_get_sprint_with_stats(self, client):
        sprint_r = client.post("/dashboard/sprints", json={"name": "Test Sprint"})
        sprint_id = sprint_r.json()["id"]

        client.post("/dashboard/tickets", json={"title": "A", "sprint_id": sprint_id})
        client.post("/dashboard/tickets", json={"title": "B", "sprint_id": sprint_id, "status": "done"})

        r = client.get(f"/dashboard/sprints/{sprint_id}")
        assert r.status_code == 200
        data = r.json()
        assert data["tickets_total"] == 2
        assert data["tickets_done"] == 1
        assert "stats" in data
        assert data["stats"]["total_tickets"] == 2

    def test_get_sprint_not_found(self, client):
        r = client.get("/dashboard/sprints/SPR-FAKE00")
        assert r.status_code == 404

    def test_update_sprint(self, client):
        sprint_r = client.post("/dashboard/sprints", json={"name": "Old Name"})
        sprint_id = sprint_r.json()["id"]

        r = client.put(f"/dashboard/sprints/{sprint_id}", json={
            "name": "New Name",
            "status": "active",
        })
        assert r.status_code == 200
        assert r.json()["name"] == "New Name"
        assert r.json()["status"] == "active"

    def test_delete_sprint(self, client):
        sprint_r = client.post("/dashboard/sprints", json={"name": "Delete me"})
        sprint_id = sprint_r.json()["id"]

        r = client.delete(f"/dashboard/sprints/{sprint_id}")
        assert r.status_code == 200
        assert r.json()["ok"] is True


# ── Multi-Tool Workflow Integration via API ───────────────────

class TestMultiToolWorkflowAPI:
    """End-to-end API tests simulating multi-tool collaboration."""

    def test_full_workflow_aider_and_vscode(self, client):
        """Sprint with tasks assigned to aider and vscode."""
        # Create sprint
        sprint_r = client.post("/dashboard/sprints", json={
            "name": "Auth Sprint",
            "goal": "Fix all auth issues",
        })
        sprint_id = sprint_r.json()["id"]

        # Create tickets
        t1 = client.post("/dashboard/tickets", json={
            "title": "Refactor login", "sprint_id": sprint_id,
            "type": "refactor", "tags": ["auth"],
        }).json()
        t2 = client.post("/dashboard/tickets", json={
            "title": "Add OAuth tests", "sprint_id": sprint_id,
            "type": "test", "tags": ["auth", "test"],
        }).json()

        # Assign tools
        client.post(f"/dashboard/tickets/{t1['id']}/assign", json={
            "tool": "aider", "model": "claude-sonnet-4-6",
        })
        client.post(f"/dashboard/tickets/{t2['id']}/assign", json={
            "tool": "vscode", "agent_mode": "code",
        })

        # Add progress comments
        client.post(f"/dashboard/tickets/{t1['id']}/comment", json={
            "author": "aider", "content": "Refactoring login.py",
        })
        client.post(f"/dashboard/tickets/{t2['id']}/comment", json={
            "author": "vscode", "content": "Writing test_oauth.py",
        })

        # Complete tickets
        client.put(f"/dashboard/tickets/{t1['id']}", json={"status": "done", "actual_hours": 1.0})
        client.put(f"/dashboard/tickets/{t2['id']}", json={"status": "done", "actual_hours": 0.5})

        # Verify sprint progress
        sprint = client.get(f"/dashboard/sprints/{sprint_id}").json()
        assert sprint["tickets_done"] == 2
        assert sprint["progress_pct"] == 100.0

        # Complete sprint
        client.put(f"/dashboard/sprints/{sprint_id}", json={"status": "completed"})
        final = client.get(f"/dashboard/sprints/{sprint_id}").json()
        assert final["status"] == "completed"

    def test_claude_code_picks_up_ticket(self, client):
        """Simulate claude-code reading a ticket and completing it."""
        t = client.post("/dashboard/tickets", json={
            "title": "Implement API pagination",
            "description": "Add limit/offset to all list endpoints",
            "type": "feature",
            "priority": "high",
            "files": ["src/proxym/api/completions.py", "src/proxym/dashboard/__init__.py"],
        }).json()

        client.post(f"/dashboard/tickets/{t['id']}/assign", json={
            "tool": "claude-code",
            "model": "claude-opus-4-6",
        })

        client.post(f"/dashboard/tickets/{t['id']}/comment", json={
            "author": "claude-code",
            "content": "Added pagination to completions.py and dashboard __init__.py",
        })

        client.put(f"/dashboard/tickets/{t['id']}", json={
            "status": "in_review",
        })

        ticket = client.get(f"/dashboard/tickets/{t['id']}").json()
        assert ticket["status"] == "in_review"
        assert ticket["assigned_tool"]["tool"] == "claude-code"
        assert len(ticket["comments"]) == 1

    def test_windsurf_and_cursor_parallel(self, client):
        """Two different tools working in parallel on different tickets."""
        t1 = client.post("/dashboard/tickets", json={
            "title": "Fix CSS layout", "type": "bug",
        }).json()
        t2 = client.post("/dashboard/tickets", json={
            "title": "Add dark mode", "type": "feature",
        }).json()

        client.post(f"/dashboard/tickets/{t1['id']}/assign", json={"tool": "windsurf"})
        client.post(f"/dashboard/tickets/{t2['id']}/assign", json={"tool": "cursor"})

        # Filter by tool
        windsurf_tasks = client.get("/dashboard/tickets?tool=windsurf").json()
        cursor_tasks = client.get("/dashboard/tickets?tool=cursor").json()
        assert len(windsurf_tasks["tickets"]) == 1
        assert len(cursor_tasks["tickets"]) == 1
