"""S1: Zarządzanie projektami (CLI + API).

SCENARIUSZ: Developer dodaje projekty z różnych źródeł i zarządza nimi.

FLOW:
  1. Skanuje folder ~/github → auto-detect projektów
  2. Dodaje git repo → clone
  3. Dodaje projekt SSH
  4. Listuje projekty z filtrami
  5. Quick task z poziomu projektu
  6. Tickety widoczne w kontekście projektu
"""
from __future__ import annotations

import pytest


class TestProjectScanAndAdd:
    """S1.1: Skanowanie i dodawanie projektów."""

    def test_scan_local_directory(self, client, tmp_projects):
        """Skan folderu z 3 projektami.

        MUSI:
        - Znaleźć 3 projekty (nie .hidden)
        - Każdy ma name, source_type="local"

        NIE MOŻE:
        - Zawierać .hidden w wynikach
        - Zawierać duplikatów po drugim skanie
        - Crashować na pustym folderze
        """
        r = client.post("/dashboard/projects/scan", json={
            "directory": str(tmp_projects),
        })
        assert r.status_code == 200
        data = r.json()
        assert data["added"] == 3
        names = [p["name"] for p in data["projects"]]
        assert ".hidden" not in names

        # Drugi skan — zero nowych
        r2 = client.post("/dashboard/projects/scan", json={
            "directory": str(tmp_projects),
        })
        assert r2.json()["added"] == 0

    def test_scan_empty_directory(self, client, tmp_path):
        """Skan pustego folderu.

        MUSI:
        - Zwrócić 200 z added=0

        NIE MOŻE:
        - Crashować
        """
        r = client.post("/dashboard/projects/scan", json={
            "directory": str(tmp_path),
        })
        assert r.status_code == 200
        assert r.json()["added"] == 0

    def test_add_local_project(self, client, tmp_path):
        """Dodaj lokalny projekt.

        MUSI:
        - source_type="local"
        - name = nazwa folderu
        - id niepusty
        """
        proj = tmp_path / "my-app"
        proj.mkdir()
        (proj / "pyproject.toml").write_text("[project]\nname='my-app'")

        r = client.post("/dashboard/projects", json={"path": str(proj)})
        assert r.status_code == 200
        p = r.json()
        assert p["source_type"] == "local"
        assert p["name"] == "my-app"
        assert p["id"]

    def test_add_git_project(self, client):
        """Dodaj git repo URL.

        MUSI:
        - Zwrócić source_type="git"
        - Wygenerować name z URL
        """
        r = client.post("/dashboard/projects", json={
            "git_url": "https://github.com/example/test-repo.git",
        })
        assert r.status_code == 200
        assert r.json()["source_type"] == "git"
        assert r.json()["name"] == "test-repo"

    def test_add_ssh_project(self, client):
        """Dodaj zdalny projekt po SSH.

        MUSI:
        - source_type="ssh"
        - source_path zawiera user@host:path
        """
        r = client.post("/dashboard/projects", json={
            "ssh_host": "192.168.1.50",
            "ssh_path": "/opt/api",
            "ssh_user": "deploy",
            "name": "prod-api",
        })
        assert r.status_code == 200
        p = r.json()
        assert p["source_type"] == "ssh"
        assert "deploy@192.168.1.50" in p["source_path"]

    def test_add_without_any_source_returns_200(self, client):
        """Name-only project creation → 200.

        MUSI:
        - Zwrócić 200
        - Stworzyć projekt z podaną nazwą
        - source_type = local (default)
        """
        r = client.post("/dashboard/projects", json={"name": "nothing"})
        assert r.status_code == 200
        p = r.json()
        assert p["name"] == "nothing"
        assert p["source_type"] == "local"

    def test_list_projects_with_filters(self, client, tmp_path):
        """Listowanie z filtrami.

        MUSI:
        - Filter po source_type zwraca tylko matching
        """
        proj = tmp_path / "filtered-proj"
        proj.mkdir()
        (proj / "README.md").write_text("# test")
        client.post("/dashboard/projects", json={"path": str(proj)})

        r = client.get("/dashboard/projects?source_type=local")
        assert r.status_code == 200
        projects = r.json() if isinstance(r.json(), list) else r.json().get("projects", [])
        assert all(p["source_type"] == "local" for p in projects)


class TestProjectTicketCorrelation:
    """S1.2: Projekty powiązane z ticketami."""

    def test_quick_task_from_project(self, client, project_id):
        """Quick task tworzy ticket z project_id.

        MUSI:
        - Ticket ma project_id ustawiony
        - Ticket widoczny w GET /projects/{id}/tickets
        - title auto-generowany z task

        NIE MOŻE:
        - Wymagać osobnego title
        - Stworzyć ticket bez project_id
        """
        r = client.post(f"/dashboard/projects/{project_id}/tickets", json={
            "task": "napraw crash w endpoincie /health",
        })
        assert r.status_code == 200
        ticket = r.json()
        assert ticket["project_id"] == project_id
        assert ticket["title"]

        # Widoczny w ticketach projektu
        r2 = client.get(f"/dashboard/projects/{project_id}/tickets")
        assert r2.status_code == 200
        assert any(t["id"] == ticket["id"] for t in r2.json())

    def test_quick_task_with_tool_auto_assigns(self, client, project_id):
        """Quick task z tool od razu assignuje.

        MUSI:
        - assigned_tool jest ustawiony
        - status zmieniony (po assign)
        """
        r = client.post(f"/dashboard/projects/{project_id}/tickets", json={
            "task": "zrefaktoryzuj router",
            "tool": "aider",
        })
        ticket = r.json()
        assert ticket.get("assigned_tool") is not None or ticket.get("status") != "todo"

    def test_project_not_found_returns_404(self, client):
        """Nieistniejący projekt → 404."""
        r = client.get("/dashboard/projects/PROJ-NONEXISTENT")
        assert r.status_code == 404

    def test_project_ticket_on_nonexistent_project(self, client):
        """Tworzenie ticketu na nieistniejącym projekcie → 404."""
        r = client.post("/dashboard/projects/PROJ-FAKE/tickets", json={
            "task": "should fail",
        })
        assert r.status_code == 404
