"""Compatibility coverage for the reversible SBOM Nexus read facade."""
from __future__ import annotations

import uuid

from api.config import settings
from api.services.sbom_nexus import SBOMNexusError


async def _create_repo(client) -> dict:
    domain = await client.post(
        "/domains/", json={"slug": "sbom-test", "name": "SBOM Test"}
    )
    assert domain.status_code == 201
    repo = await client.post(
        "/repos/",
        json={
            "slug": "testrepo",
            "name": "Test Repo",
            "domain_slug": "sbom-test",
            "local_path": "/tmp/testrepo",
        },
    )
    assert repo.status_code == 201
    return repo.json()


def _entry(snapshot_id: str, nexus_repo_id: str) -> dict:
    return {
        "id": str(uuid.uuid4()),
        "repo_id": nexus_repo_id,
        "repo_slug": "testrepo",
        "snapshot_id": snapshot_id,
        "package_name": "fastapi",
        "package_version": "0.115.0",
        "ecosystem": "python",
        "license_spdx": "MIT",
        "is_direct": True,
        "is_dev": False,
        "snapshot_at": "2026-08-22T12:00:00+00:00",
        "created_at": "2026-08-22T12:00:01+00:00",
        "source_path": "uv.lock",
    }


async def test_nexus_read_mode_preserves_state_hub_response_contract(client, monkeypatch):
    repo = await _create_repo(client)
    snapshot_id = str(uuid.uuid4())
    nexus_repo_id = str(uuid.uuid4())
    entry = _entry(snapshot_id, nexus_repo_id)
    snapshot = {
        "id": snapshot_id,
        "repo_id": nexus_repo_id,
        "repo_slug": "testrepo",
        "snapshot_at": "2026-08-22T12:00:00+00:00",
        "source": "state-hub:manual",
        "legacy_id": snapshot_id,
        "entry_count": 1,
        "created_at": "2026-08-22T12:00:01+00:00",
        "status": "imported",
    }

    async def fake_get(path: str, *, params=None):
        if path == "/sbom/snapshots/":
            assert params == {"repo_slug": "testrepo"}
            return [snapshot]
        if path == f"/sbom/snapshots/{snapshot_id}":
            return {**snapshot, "entries": [entry]}
        if path == "/sbom/":
            assert params == {
                "repo_slug": "testrepo",
                "ecosystem": "python",
                "is_direct": True,
            }
            return [entry]
        if path == "/sbom/report/licences/":
            return {
                "groups": [
                    {
                        "license_spdx": "MIT",
                        "count": 1,
                        "repos": ["testrepo"],
                        "is_copyleft": False,
                    }
                ],
                "copyleft_direct_count": 0,
                "signal_qualification": "extra Nexus field",
            }
        if path == "/sbom/testrepo":
            return {
                "repo_slug": "testrepo",
                "last_sbom_at": "2026-08-22T12:00:00+00:00",
                "last_attempt_at": "2026-08-22T12:00:00+00:00",
                "entry_count": 1,
                "entries": [entry],
            }
        raise AssertionError(f"unexpected Nexus path {path}")

    monkeypatch.setattr(settings, "sbom_nexus_read_mode", "nexus")
    monkeypatch.setattr("api.routers.sbom.get_json", fake_get)

    snapshots = await client.get("/sbom/snapshots/?repo_slug=testrepo")
    detail = await client.get(f"/sbom/snapshots/{snapshot_id}")
    entries = await client.get(
        "/sbom/?repo_slug=testrepo&ecosystem=python&is_direct=true"
    )
    licences = await client.get("/sbom/report/licences/")
    repo_view = await client.get("/sbom/testrepo")

    assert snapshots.status_code == 200
    assert detail.status_code == 200
    assert entries.status_code == 200
    assert licences.status_code == 200
    assert repo_view.status_code == 200
    assert snapshots.json()[0]["id"] == snapshot_id
    assert snapshots.json()[0]["repo_id"] == repo["id"]
    assert snapshots.json()[0]["source"] == "manual"
    assert detail.json()["source"] == "manual"
    assert detail.json()["entries"][0]["repo_id"] == repo["id"]
    assert entries.json()[0]["repo_id"] == repo["id"]
    assert "source_path" not in entries.json()[0]
    assert set(licences.json()) == {"groups", "copyleft_direct_count"}
    assert repo_view.json()["entries"][0]["repo_id"] == repo["id"]
    assert set(repo_view.json()) == {
        "repo_slug",
        "last_sbom_at",
        "entry_count",
        "entries",
    }


async def test_nexus_read_mode_does_not_move_ingest_write_authority(client, monkeypatch):
    await _create_repo(client)

    async def unexpected_get(*args, **kwargs):
        raise AssertionError("POST /sbom/ingest/ must not call SBOM Nexus")

    monkeypatch.setattr(settings, "sbom_nexus_read_mode", "nexus")
    monkeypatch.setattr("api.routers.sbom.get_json", unexpected_get)
    response = await client.post(
        "/sbom/ingest/",
        json={
            "repo_slug": "testrepo",
            "entries": [
                {
                    "package_name": "fastapi",
                    "package_version": "0.115.0",
                    "ecosystem": "python",
                    "license_spdx": "MIT",
                }
            ],
        },
    )

    assert response.status_code == 200
    assert response.json()["ingested"] == 1


async def test_nexus_write_mode_moves_authority_and_updates_projection(client, monkeypatch):
    repo = await _create_repo(client)
    snapshot_id = str(uuid.uuid4())
    observed = {}

    async def fake_post(path: str, *, body):
        observed["path"] = path
        observed["body"] = body
        return {
            "repo_slug": "testrepo",
            "snapshot_id": snapshot_id,
            "ingested": 1,
            "snapshot_at": "2026-08-22T13:00:00Z",
            "status": "ingested",
        }

    monkeypatch.setattr(settings, "sbom_nexus_read_mode", "legacy")
    monkeypatch.setattr(settings, "sbom_nexus_write_mode", "nexus")
    monkeypatch.setattr("api.routers.sbom.post_json", fake_post)
    response = await client.post(
        "/sbom/ingest/",
        json={
            "repo_slug": "testrepo",
            "entries": [
                {
                    "package_name": "fastapi",
                    "package_version": "0.115.0",
                    "ecosystem": "python",
                    "license_spdx": "MIT",
                }
            ],
        },
    )

    assert response.status_code == 200
    assert response.json() == {
        "repo_slug": "testrepo",
        "snapshot_id": snapshot_id,
        "ingested": 1,
        "snapshot_at": "2026-08-22T13:00:00Z",
    }
    assert observed["path"] == "/sbom/ingest/"
    assert observed["body"]["entries"][0]["ecosystem"] == "python"

    legacy_snapshots = await client.get("/sbom/snapshots/?repo_slug=testrepo")
    projected_repo = await client.get("/repos/testrepo")
    assert legacy_snapshots.json() == []
    assert projected_repo.json()["id"] == repo["id"]
    assert projected_repo.json()["last_sbom_at"] == "2026-08-22T13:00:00Z"
    assert projected_repo.json()["sbom_source"] == "sbom-nexus"


async def test_nexus_failure_is_visible_and_does_not_fall_back(client, monkeypatch):
    await _create_repo(client)

    async def unavailable(*args, **kwargs):
        raise SBOMNexusError(502, "SBOM Nexus is unavailable: ConnectError")

    monkeypatch.setattr(settings, "sbom_nexus_read_mode", "nexus")
    monkeypatch.setattr("api.routers.sbom.get_json", unavailable)
    response = await client.get("/sbom/snapshots/")

    assert response.status_code == 502
    assert response.json()["detail"] == "SBOM Nexus is unavailable: ConnectError"


async def test_repo_reads_project_nexus_last_attempt_at(client, monkeypatch):
    await _create_repo(client)

    async def repositories(path: str, **kwargs):
        assert path == "/repositories/"
        return [
            {
                "slug": "testrepo",
                "last_attempt_at": "2026-08-22T14:00:00Z",
            }
        ]

    monkeypatch.setattr(settings, "sbom_nexus_read_mode", "nexus")
    monkeypatch.setattr("api.routers.repos.get_sbom_nexus_json", repositories)

    collection = await client.get("/repos/")
    detail = await client.get("/repos/testrepo")

    assert collection.status_code == 200
    assert detail.status_code == 200
    assert collection.json()[0]["last_sbom_at"] == "2026-08-22T14:00:00Z"
    assert collection.json()[0]["sbom_source"] == "sbom-nexus"
    assert detail.json()["last_sbom_at"] == "2026-08-22T14:00:00Z"
    assert detail.json()["sbom_source"] == "sbom-nexus"


async def test_state_summaries_use_nexus_metrics(client, monkeypatch):
    async def sbom_metrics(path: str, **kwargs):
        if path == "/sbom/snapshots/":
            return [
                {
                    "snapshot_at": "2026-08-22T12:00:00Z",
                    "entry_count": 3,
                },
                {
                    "snapshot_at": "2026-08-22T14:00:00Z",
                    "entry_count": 5,
                },
            ]
        if path == "/sbom/report/licences/":
            return {"groups": [], "copyleft_direct_count": 7}
        raise AssertionError(f"unexpected Nexus path {path}")

    monkeypatch.setattr(settings, "sbom_nexus_read_mode", "nexus")
    monkeypatch.setattr("api.routers.state.get_sbom_nexus_json", sbom_metrics)
    monkeypatch.setattr("api.services.summary_cache.get_sbom_nexus_json", sbom_metrics)

    summary = await client.get("/state/summary", params={"refresh": "true"})
    overview = await client.get("/state/overview", params={"refresh": "true"})

    assert summary.status_code == 200
    assert overview.status_code == 200
    assert summary.json()["licence_risk_count"] == 7
    assert overview.json()["licence_risk_count"] == 7
    assert overview.json()["sbom_snapshot_count"] == 2
    assert overview.json()["sbom_package_total"] == 8
