"""Shared fixtures: a client over the real federation + a synthetic one (tmp).

The real-discovery ``client`` exercises the two live participants (mgf-fedres +
mgf-standard) the way the spec's acceptance criteria name them. The ``synthetic``
fixture builds a deterministic federation under ``tmp_path`` from hand-authored,
schema-validated payloads (so it never depends on the live repos' mutating state):
``alpha`` reports work + roadmap, ``beta`` reports an architecture graph + only
finished work, and ``broken`` carries a manifest on an unrecognized schema_version
(→ UNREADABLE). Files are hashed into the manifest so they read IN_SYNC (a tmp dir
has no git HEAD, so the STALE branch can't trigger).
"""

from __future__ import annotations

import hashlib
import json
from pathlib import Path
from typing import TYPE_CHECKING, Any

import pytest
from fastapi.testclient import TestClient
from mgf.fedres import validate_payload

from mgf.fed_web import create_app
from mgf.fed_web.discovery import ROOTS_ENV

if TYPE_CHECKING:
    from collections.abc import Iterator

# tests/conftest.py -> tests -> <project> -> PycharmProjects (the real workspace).
WORKSPACE = Path(__file__).resolve().parents[2]
REAL_PROJECTS = ("mgf-fedres", "mgf-standard")


def _real_federation_available() -> bool:
    """Are the real sibling participants checked out with a generated/ tree?

    True locally (``~/PycharmProjects`` holds the whole fleet); False in CI, which
    clones only this repo — so the real-federation tests are local integration checks,
    and the ``synthetic_client`` / ``tmp_path`` tests are the hermetic CI gate.
    """
    return all(
        (WORKSPACE / name / "fed-resources" / "generated" / "manifest.json").is_file()
        for name in REAL_PROJECTS
    )


#: Skip a test that reads the real sibling repos when they aren't checked out (CI).
requires_real_federation = pytest.mark.skipif(
    not _real_federation_available(),
    reason="real federation siblings (mgf-fedres / mgf-standard) not checked out — "
    "local integration test; the synthetic-fixture tests are the hermetic CI gate",
)


@pytest.fixture
def client(monkeypatch: pytest.MonkeyPatch) -> TestClient:
    """A client over the real default discovery (the live federation participants).

    Skips when the siblings aren't checked out (CI clones only this repo); the
    ``synthetic_client`` tests carry the hermetic coverage.
    """
    if not _real_federation_available():
        pytest.skip("real federation siblings not checked out (hermetic CI skip)")
    monkeypatch.delenv(ROOTS_ENV, raising=False)
    return TestClient(create_app())


# --------------------------------------------------------------------------- #
# synthetic federation                                                         #
# --------------------------------------------------------------------------- #
_ALPHA_STATUS: dict[str, Any] = {
    "schema_version": "0.1",
    "kind": "status",
    "project": "alpha",
    "summary": {"total": 2, "by_state": {"in_progress": 1, "next": 1}},
    "tasks": [
        {
            "id": "A-1",
            "title": "Wire the spine reader",
            "state": "in_progress",
            "owner": "implementer",
            "risk": "low",
            "impact": "high",
            "effort": "M",
            "eta": "2026-07",
            "stage": "phase-a",
            "started_at": "2026-06-10T00:00:00Z",
            "evidence": "reader returns honest-absence",
        },
        {
            "id": "A-2",
            "title": "Approve the schema freeze",
            "state": "next",
            "owner": "founder",
            "risk": "low",
            "impact": "high",
            "effort": "S",
            "eta": "2026-07",
            "stage": "phase-a",
            "evidence": "founder-only call",
        },
    ],
}
_ALPHA_ROADMAP: dict[str, Any] = {
    "schema_version": "0.1",
    "kind": "roadmap",
    "project": "alpha",
    "final_stage": {"id": "v1", "label": "v1.0"},
    "stages": [
        {
            "id": "phase-a",
            "label": "Phase A — Foundation",
            "state": "in_progress",
            "progress": 0.6,
            "depends_on": [],
            "owner": "implementer",
            "risk": "medium",
            "effort": "L",
            "eta": "2026-07",
            "summary": "spine + dashboard",
        },
        {
            "id": "v1",
            "label": "v1.0",
            "state": "planned",
            "progress": 0.0,
            "depends_on": ["phase-a"],
            "owner": "founder",
            "risk": "low",
            "effort": "S",
            "eta": "2026-Q4",
        },
    ],
}
_BETA_STATUS: dict[str, Any] = {
    "schema_version": "0.1",
    "kind": "status",
    "project": "beta",
    "summary": {"total": 1, "by_state": {"done": 1}},
    "tasks": [
        {
            "id": "B-1",
            "title": "Ship the graph projector",
            "state": "done",
            "owner": "implementer",
            "risk": "low",
            "impact": "medium",
            "effort": "S",
            "eta": "2026-06",
        }
    ],
}
_BETA_ARCH: dict[str, Any] = {
    "schema_version": "0.1",
    "kind": "architecture",
    "project": "beta",
    "mode": "graph",
    "graph": {
        "layers": ["core", "tools"],
        "nodes": [
            {"id": "engine", "label": "core/engine", "layer": "core"},
            {"id": "reader", "label": "core/reader", "layer": "core"},
            {"id": "cli", "label": "tools/cli", "layer": "tools"},
        ],
        "edges": [
            {"from": "cli", "to": "engine", "kind": "depends_on"},
            {"from": "engine", "to": "reader", "kind": "depends_on"},
        ],
    },
}


def _write_project(root: Path, slug: str, kind: str, files: dict[str, dict[str, Any]]) -> None:
    """Write a valid generated/ tree (payloads validated; hashes into the manifest)."""
    gen = root / slug / "fed-resources" / "generated"
    gen.mkdir(parents=True)
    manifest_files = []
    for name, payload in files.items():
        errors = validate_payload(payload)
        assert not errors, f"{slug}/{name} is not schema-valid: {errors}"
        raw = (json.dumps(payload, sort_keys=True, indent=2) + "\n").encode("utf-8")
        (gen / name).write_bytes(raw)
        manifest_files.append(
            {
                "path": name,
                "sha256": hashlib.sha256(raw).hexdigest(),
                "schema": f"{payload['kind']}@0.1",
            }
        )
    manifest = {
        "schema_version": "0.1",
        "kind": "manifest",
        "project": {"id": slug, "name": slug, "kind": kind},
        "generated_at": "2026-06-29T00:00:00Z",
        "source_commit": "0" * 40,
        "generator": {"name": "fedres", "version": "0.1.0"},
        "files": manifest_files,
    }
    (gen / "manifest.json").write_text(
        json.dumps(manifest, sort_keys=True, indent=2) + "\n", encoding="utf-8"
    )


@pytest.fixture
def synthetic(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
    """A pinned three-project federation under a tmp root (alpha/beta/broken)."""
    root = tmp_path / "ws"
    root.mkdir()
    _write_project(
        root, "alpha", "library", {"status.json": _ALPHA_STATUS, "roadmap.json": _ALPHA_ROADMAP}
    )
    _write_project(
        root, "beta", "standards", {"status.json": _BETA_STATUS, "architecture.json": _BETA_ARCH}
    )
    broken = root / "broken" / "fed-resources" / "generated"
    broken.mkdir(parents=True)
    (broken / "manifest.json").write_text(
        json.dumps(
            {
                "schema_version": "0.0-draft",
                "kind": "manifest",
                "project": {"id": "broken", "name": "Broken", "kind": "demo"},
            }
        ),
        encoding="utf-8",
    )
    monkeypatch.setenv(ROOTS_ENV, str(root))
    return root


@pytest.fixture
def synthetic_client(synthetic: Path) -> Iterator[TestClient]:
    """A client bound to the synthetic federation (env points discovery at tmp)."""
    yield TestClient(create_app())
