"""S2: Pełny lifecycle ticketu (CLI + API).

SCENARIUSZ: Ticket przechodzi przez wszystkie statusy.

FLOW:
  todo → in_progress (assign tool) → running (dispatch)
  → done (success) LUB → failed (error) → retry lub manual close

Każdy krok logowany, widoczny w historii.
"""
from __future__ import annotations

import pytest


class TestTicketStatusTransitions:
    """S2.1: Przejścia między statusami."""

    def test_full_happy_path(self, client, project_id):
        """Ticket: create → assign → dispatch → done.

        MUSI:
        - create → status=todo
        - assign tool → status zmieniony (in_progress lub assigned)
        - dispatch → job created, status=running (lub in_progress)
        - Komentarz z wynikiem dodany automatycznie

        NIE MOŻE:
        - Ticket zniknąć po dispatch
        """
        # 1. Create
        r = client.post("/dashboard/tickets", json={
            "task": "dodaj .toonignore",
            "project_id": project_id,
            "priority": "critical",
        })
        assert r.status_code == 200
        ticket_id = r.json()["id"]
        assert r.json()["status"] == "todo"

        # 2. Assign tool
        r2 = client.post(f"/dashboard/tickets/{ticket_id}/assign", json={
            "tool": "aider",
        })
        assert r2.status_code == 200

        # 3. Verify status changed
        r3 = client.get(f"/dashboard/tickets/{ticket_id}")
        assert r3.status_code == 200
        assert r3.json()["status"] in ("in_progress", "assigned", "todo")
        assert r3.json()["assigned_tool"]["tool"] == "aider"

        # 4. Dispatch
        r4 = client.post("/dashboard/tools/dispatch", json={
            "ticket_id": ticket_id,
            "tool": "aider",
        })
        # Dispatch should succeed or return job info
        assert r4.status_code in (200, 201, 202)

        # 5. Ticket still exists
        r5 = client.get(f"/dashboard/tickets/{ticket_id}")
        assert r5.status_code == 200

    def test_failed_execution_updates_status(self, client, project_id):
        """Ticket z błędem wykonania → status=failed + komentarz z error.

        MUSI:
        - Ticket status zmieniony na failed/error
        - Komentarz z treścią błędu dodany

        NIE MOŻE:
        - Ticket pozostać w running na zawsze
        - Brak informacji o przyczynie błędu
        """
        # Create + set to failed manually (simulating tool failure callback)
        r = client.post("/dashboard/tickets", json={
            "task": "task that will fail",
            "project_id": project_id,
        })
        tid = r.json()["id"]

        # Simulate failure update (TicketStatus has no 'failed'; use 'cancelled')
        client.put(f"/dashboard/tickets/{tid}", json={"status": "cancelled"})
        client.post(f"/dashboard/tickets/{tid}/comment", json={
            "author": "aider",
            "content": "aider ❌ failed — SyntaxError: unexpected indent line 42",
        })

        r2 = client.get(f"/dashboard/tickets/{tid}")
        t = r2.json()
        assert t["status"] == "cancelled"
        assert any(
            "failed" in c.get("content", "").lower()
            for c in t.get("comments", [])
        )

    def test_ticket_with_comments_shows_history(self, client, project_id):
        """Komentarze tworzą historię zmian.

        MUSI:
        - Każdy komentarz ma: author, content, timestamp
        - Komentarze od narzędzi mają author=tool_name
        - Widoczne chronologicznie
        """
        r = client.post("/dashboard/tickets", json={
            "task": "test comments",
            "project_id": project_id,
        })
        tid = r.json()["id"]

        # Add manual comment
        client.post(f"/dashboard/tickets/{tid}/comment", json={
            "content": "started work",
            "author": "developer",
        })

        # Simulate tool comment
        client.post(f"/dashboard/tickets/{tid}/comment", json={
            "content": "3 files changed, tests passed",
            "author": "aider",
        })

        r2 = client.get(f"/dashboard/tickets/{tid}")
        comments = r2.json().get("comments", [])
        assert len(comments) >= 2
        assert comments[0]["author"] == "developer"
        assert comments[1]["author"] == "aider"

    def test_manual_status_update(self, client, project_id):
        """Ręczna zmiana statusu ticketu.

        MUSI:
        - PUT /tickets/{id} zmienia status
        - Nowy status widoczny w GET
        """
        r = client.post("/dashboard/tickets", json={
            "task": "manual update test",
            "project_id": project_id,
        })
        tid = r.json()["id"]

        r2 = client.put(f"/dashboard/tickets/{tid}", json={"status": "done"})
        assert r2.status_code == 200
        assert r2.json()["status"] == "done"

    def test_delete_ticket(self, client, project_id):
        """Usunięcie ticketu.

        MUSI:
        - DELETE → 200
        - GET po DELETE → 404
        """
        r = client.post("/dashboard/tickets", json={
            "task": "to be deleted",
            "project_id": project_id,
        })
        tid = r.json()["id"]

        r2 = client.delete(f"/dashboard/tickets/{tid}")
        assert r2.status_code == 200

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


class TestTicketMinimalistCreation:
    """S2.2: Uproszczone tworzenie ticketów."""

    def test_create_with_task_only(self, client):
        """Jedno pole wystarczy.

        MUSI:
        - task → ticket created
        - title auto-generated z task
        - type=task, priority=medium (defaults)

        NIE MOŻE:
        - Wymagać title osobno
        - Wymagać description
        - Wymagać project_id (opcjonalny)
        """
        r = client.post("/dashboard/tickets", json={
            "task": "napraw bug w routerze proxy",
        })
        assert r.status_code == 200
        t = r.json()
        assert t["title"]
        assert t["type"] in ("task", "bug")
        assert t["priority"] == "medium"

    def test_create_without_task_or_title_returns_400(self, client):
        """Brak task i title → 400.

        NIE MOŻE:
        - Stworzyć ticket bez treści
        """
        r = client.post("/dashboard/tickets", json={"priority": "high"})
        assert r.status_code == 400

    def test_create_with_inline_tool(self, client, project_id):
        """Task + tool w jednym requeście.

        MUSI:
        - Ticket stworzony z project_id
        - Tool od razu assigned
        - Jeden POST, nie dwa
        """
        r = client.post("/dashboard/tickets", json={
            "task": "zoptymalizuj queries w SQL",
            "project_id": project_id,
            "tool": "claude-code",
            "priority": "high",
        })
        assert r.status_code == 200
        t = r.json()
        assert t["project_id"] == project_id
        assert t.get("assigned_tool") is not None

    def test_create_with_legacy_title_field(self, client):
        """Backward compat: title zamiast task.

        MUSI:
        - Ticket stworzony z title
        """
        r = client.post("/dashboard/tickets", json={
            "title": "legacy ticket creation",
        })
        assert r.status_code == 200
        assert r.json()["title"] == "legacy ticket creation"
