"""
Router tests: technical debt, extension points, contributions, SBOM ingest.
"""
from __future__ import annotations

import pytest


# ---------------------------------------------------------------------------
# Helpers (reuse domain/topic setup)
# ---------------------------------------------------------------------------

async def _setup(client):
    """Create a domain + topic; return (domain_slug, topic_id)."""
    r = await client.post("/domains/", json={"slug": "testdom", "name": "Test Domain"})
    assert r.status_code == 201
    r2 = await client.post("/topics/", json={
        "slug": "testtopic", "title": "Test Topic", "domain": "testdom",
    })
    assert r2.status_code == 201
    return "testdom", r2.json()["id"]


async def _create_repo(client, domain_slug, slug="testrepo"):
    r = await client.post("/repos/", json={
        "slug": slug,
        "name": "Test Repo",
        "domain_slug": domain_slug,
        "local_path": "/tmp/testrepo",
    })
    assert r.status_code == 201, r.text
    return r.json()


# ---------------------------------------------------------------------------
# Technical Debt
# ---------------------------------------------------------------------------

class TestTechnicalDebt:
    async def test_create_and_list(self, client):
        domain_slug, _ = await _setup(client)
        r = await client.post("/technical-debt/", json={
            "title": "Use UTC datetimes", "domain": domain_slug,
        })
        assert r.status_code == 201
        td_id = r.json()["id"]

        r2 = await client.get(f"/technical-debt/?domain={domain_slug}")
        assert r2.status_code == 200
        ids = [t["id"] for t in r2.json()]
        assert td_id in ids

    async def test_status_transition(self, client):
        domain_slug, _ = await _setup(client)
        r = await client.post("/technical-debt/", json={
            "title": "Old debt", "domain": domain_slug,
        })
        td_id = r.json()["id"]

        r2 = await client.patch(f"/technical-debt/{td_id}", json={"status": "resolved"})
        assert r2.status_code == 200
        assert r2.json()["status"] == "resolved"

    async def test_add_note(self, client):
        domain_slug, _ = await _setup(client)
        r = await client.post("/technical-debt/", json={
            "title": "Needs note", "domain": domain_slug,
        })
        td_id = r.json()["id"]

        r2 = await client.post(f"/technical-debt/{td_id}/notes", json={
            "step": "analysis",
            "content": "Root cause identified.",
        })
        assert r2.status_code == 201

        r3 = await client.get(f"/technical-debt/{td_id}/notes")
        assert r3.status_code == 200
        assert len(r3.json()) == 1


# ---------------------------------------------------------------------------
# Extension Points
# ---------------------------------------------------------------------------

class TestExtensionPoints:
    async def test_create_and_list(self, client):
        domain_slug, _ = await _setup(client)
        r = await client.post("/extension-points/", json={
            "domain": domain_slug,
            "title": "Auth hook",
            "ep_type": "hook",
        })
        assert r.status_code == 201
        ep_id = r.json()["id"]

        r2 = await client.get(f"/extension-points/?domain={domain_slug}")
        assert r2.status_code == 200
        ids = [e["id"] for e in r2.json()]
        assert ep_id in ids

    async def test_status_transition(self, client):
        domain_slug, _ = await _setup(client)
        r = await client.post("/extension-points/", json={
            "domain": domain_slug, "title": "Webhook", "ep_type": "webhook",
        })
        ep_id = r.json()["id"]

        r2 = await client.patch(f"/extension-points/{ep_id}", json={"status": "addressed"})
        assert r2.status_code == 200
        assert r2.json()["status"] == "addressed"


# ---------------------------------------------------------------------------
# Contributions
# ---------------------------------------------------------------------------

class TestContributions:
    async def test_create_draft_and_submit(self, client):
        r = await client.post("/contributions/", json={
            "type": "br",
            "title": "Fix null pointer",
            "target_org": "anthropics",
            "target_repo": "sdk",
        })
        assert r.status_code == 201
        c_id = r.json()["id"]
        assert r.json()["status"] == "draft"

        r2 = await client.patch(f"/contributions/{c_id}/status", json={
            "status": "submitted",
        })
        assert r2.status_code == 200
        assert r2.json()["status"] == "submitted"

    async def test_invalid_transition_returns_422(self, client):
        r = await client.post("/contributions/", json={
            "type": "br",
            "title": "Fix crash",
            "target_org": "anthropics",
            "target_repo": "sdk",
        })
        c_id = r.json()["id"]  # starts as draft

        # draft → merged is not allowed (must go through submitted first)
        r2 = await client.patch(f"/contributions/{c_id}/status", json={
            "status": "merged",
        })
        assert r2.status_code == 422

    async def test_list_by_type(self, client):
        await client.post("/contributions/", json={
            "type": "br", "title": "Bug A",
            "target_org": "org", "target_repo": "repo",
        })
        await client.post("/contributions/", json={
            "type": "fr", "title": "Feature B",
            "target_org": "org", "target_repo": "repo",
        })

        r = await client.get("/contributions/?type=br")
        assert r.status_code == 200
        types = [c["type"] for c in r.json()]
        assert all(t == "br" for t in types)
        assert len(types) == 1


# ---------------------------------------------------------------------------
# SBOM ingest
# ---------------------------------------------------------------------------

class TestSBOM:
    async def test_ingest_and_list_snapshots(self, client):
        domain_slug, _ = await _setup(client)
        await _create_repo(client, domain_slug)

        r = await client.post("/sbom/ingest/", json={
            "repo_slug": "testrepo",
            "entries": [
                {
                    "package_name": "fastapi",
                    "package_version": "0.115.0",
                    "ecosystem": "python",
                    "license_spdx": "MIT",
                    "is_direct": True,
                    "is_dev": False,
                },
                {
                    "package_name": "sqlalchemy",
                    "package_version": "2.0.0",
                    "ecosystem": "python",
                    "license_spdx": "MIT",
                    "is_direct": True,
                    "is_dev": False,
                },
            ],
        })
        assert r.status_code == 200
        assert r.json()["ingested"] == 2

        r2 = await client.get("/sbom/snapshots/?repo_slug=testrepo")
        assert r2.status_code == 200
        assert len(r2.json()) == 1

    async def test_ingest_unknown_repo_returns_404(self, client):
        r = await client.post("/sbom/ingest/", json={
            "repo_slug": "doesnotexist",
            "entries": [],
        })
        assert r.status_code == 404
