"""M3 sync-action tests (spec §5.5): deep Check sync + Force-sync, per-project + global.

These run against the real federation the spec's acceptance criteria name (mgf-fedres
+ mgf-standard). The load-bearing invariant — **Check sync is provably non-mutating**
— is asserted by snapshotting every byte of a project's ``generated/`` tree before and
after the action. Force-sync is the one sanctioned write (of the working tree's
``generated/`` only); its test snapshots, asserts the state files are byte-unchanged on
an already-in-sync project, and **restores** the tree so the sibling repo is left clean.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from mgf.fedres import SyncState, deep_check

from mgf.fed_web.discovery import DiskProvider, discover_projects, find_project
from mgf.fed_web.lenses import deep_meta, federation_state, run_check, run_force_sync

from .conftest import requires_real_federation

if TYPE_CHECKING:
    from pathlib import Path

    from fastapi.testclient import TestClient


# --------------------------------------------------------------------------- #
# helpers — byte-snapshot a generated/ tree (the non-mutation proof)           #
# --------------------------------------------------------------------------- #
def _provider(slug: str) -> DiskProvider:
    return next(p.provider for p in discover_projects() if p.slug == slug)


def _snapshot(gen_dir: Path) -> dict[str, bytes]:
    """Every file under ``generated/`` → its exact bytes (manifest included)."""
    return {
        str(p.relative_to(gen_dir)): p.read_bytes()
        for p in sorted(gen_dir.rglob("*"))
        if p.is_file()
    }


def _restore(gen_dir: Path, snap: dict[str, bytes]) -> None:
    """Put a generated/ tree back exactly as snapshotted (drop extras, rewrite bytes)."""
    for p in list(gen_dir.rglob("*")):
        if p.is_file() and str(p.relative_to(gen_dir)) not in snap:
            p.unlink()
    for rel, data in snap.items():
        path = gen_dir / rel
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_bytes(data)


def _state_files(snap: dict[str, bytes]) -> dict[str, bytes]:
    """The reproduce-gated state files (everything but the timestamped manifest)."""
    return {k: v for k, v in snap.items() if k != "manifest.json"}


# --------------------------------------------------------------------------- #
# POST /p/{slug}/check — deep, authoritative, PROVABLY non-mutating            #
# --------------------------------------------------------------------------- #
def test_project_check_returns_deep_state_and_is_non_mutating(client: TestClient) -> None:
    provider = _provider("mgf-fedres")
    gen = provider.generated_dir
    before = _snapshot(gen)

    resp = client.post("/p/mgf-fedres/check")

    assert resp.status_code == 200
    body = resp.text
    # mgf-fedres reads STALE *shallow* (source_commit trails HEAD) but IN_SYNC *deep*
    # (the state files regenerate byte-identical) — the deep verdict is what shows.
    assert "status-chip--in-sync" in body
    assert "deep check" in body and "in sync" in body
    # PROOF: the deep check wrote nothing — the tree is byte-for-byte unchanged.
    assert _snapshot(gen) == before


def test_run_check_writes_nothing_at_the_spine(client: TestClient) -> None:
    # The same non-mutation guarantee, asserted directly at the seam onto the spine.
    provider = _provider("mgf-fedres")
    before = _snapshot(provider.generated_dir)
    report, result = run_check(provider)
    assert report.state is SyncState.IN_SYNC
    assert result["ok"] is True
    assert _snapshot(provider.generated_dir) == before


def test_check_on_missing_project_is_honest_absence_not_500(client: TestClient) -> None:
    resp = client.post("/p/does-not-exist/check")
    assert resp.status_code == 200  # honest-absence, never a crash
    body = resp.text
    assert "UNKNOWN" in body
    assert "wired" in body


# --------------------------------------------------------------------------- #
# POST /check-all — deep-check the fleet; recompute the matrix + global pill   #
# --------------------------------------------------------------------------- #
def test_check_all_updates_matrix_and_pill_deep(client: TestClient) -> None:
    # On load the pill reads drift (shallow STALE); a deep Check all settles it.
    assert "status-chip--stale" in client.get("/").text

    # Snapshot both participants to prove the fleet check mutates nothing.
    snaps = {p.slug: _snapshot(p.provider.generated_dir) for p in discover_projects()}
    expected = federation_state([deep_meta(p.slug, p.provider) for p in discover_projects()])

    resp = client.post("/check-all")

    assert resp.status_code == 200
    body = resp.text
    assert "deep check ·" in body  # the matrix is now labelled deep, not shallow
    assert expected["label"] in body  # the recomputed pill (── "All in sync" here)
    assert "status-chip--in-sync" in body
    for slug in ("mgf-fedres", "mgf-standard"):
        assert f'href="/p/{slug}"' in body
    # PROOF: deep-checking the whole fleet wrote nothing.
    for p in discover_projects():
        assert _snapshot(p.provider.generated_dir) == snaps[p.slug]


# --------------------------------------------------------------------------- #
# POST /p/{slug}/force-sync — the ONE sanctioned write (generated/ only)       #
# --------------------------------------------------------------------------- #
def test_force_sync_settles_in_sync_and_leaves_state_files_unchanged(client: TestClient) -> None:
    provider = _provider("mgf-fedres")
    gen = provider.generated_dir
    before = _snapshot(gen)
    try:
        resp = client.post("/p/mgf-fedres/force-sync")

        assert resp.status_code == 200
        body = resp.text
        assert "status-chip--in-sync" in body
        assert "regenerated" in body
        # Already deep-in-sync → 0 state files change (only the manifest's source_commit
        # + generated_at advance — the STALE-on-shallow fix).
        assert "0 file(s) changed" in body
        after = _snapshot(gen)
        assert _state_files(after) == _state_files(before)
        assert deep_check(provider).state is SyncState.IN_SYNC
    finally:
        _restore(gen, before)  # leave the sibling repo exactly as we found it
    assert _snapshot(gen) == before


def test_run_force_sync_is_regenerate_only_no_commit(client: TestClient) -> None:
    # The spine call settles to IN_SYNC and reports 0 changed; never touches git.
    provider = _provider("mgf-fedres")
    gen = provider.generated_dir
    before = _snapshot(gen)
    try:
        report, result = run_force_sync(provider, "2026-06-29T00:00:00Z")
        assert report.state is SyncState.IN_SYNC
        assert result["ok"] is True and "regenerated" in result["text"]
        assert _state_files(_snapshot(gen)) == _state_files(before)
    finally:
        _restore(gen, before)


def test_force_sync_on_missing_project_is_honest_absence_not_500(client: TestClient) -> None:
    resp = client.post("/p/does-not-exist/force-sync")
    assert resp.status_code == 200
    assert "UNKNOWN" in resp.text


# --------------------------------------------------------------------------- #
# POST /force-sync-all — global; safe no-op when nothing is shallow-drifting   #
# --------------------------------------------------------------------------- #
def test_force_sync_all_is_safe_when_nothing_drifts(synthetic_client: TestClient) -> None:
    # alpha/beta read IN_SYNC (hashes match, no git HEAD in tmp), broken is UNREADABLE —
    # none are STALE/TAMPERED, so force-sync-all touches nothing and renders cleanly.
    resp = synthetic_client.post("/force-sync-all")
    assert resp.status_code == 200
    assert "status-chip--in-sync" in resp.text
    assert "status-chip--unreadable" in resp.text


@requires_real_federation
def test_find_project_resolves_real_participant() -> None:
    assert find_project("mgf-fedres") is not None
    assert find_project("nope-not-here") is None


# --------------------------------------------------------------------------- #
# Hermetic sync-action coverage (synthetic federation) — the tests above read  #
# the real fleet and only run locally; these gate CI. A generated-only         #
# synthetic project has no source/ to regenerate from, so a deep check / force #
# -sync resolves to an honest ERROR (never a 500) — which is itself the proof  #
# that the routes degrade gracefully on an un-regenerable project.             #
# --------------------------------------------------------------------------- #
def test_synthetic_check_route_renders_error_not_500(synthetic_client: TestClient) -> None:
    resp = synthetic_client.post("/p/alpha/check")
    assert resp.status_code == 200
    assert "status-chip--error" in resp.text  # no source/ → deep check can't regenerate


def test_synthetic_force_sync_renders_honest_error_not_500(synthetic_client: TestClient) -> None:
    resp = synthetic_client.post("/p/alpha/force-sync")
    assert resp.status_code == 200
    assert "source/project.toml is required" in resp.text  # the honest generator error


def test_synthetic_check_all_labels_the_matrix_deep(synthetic_client: TestClient) -> None:
    resp = synthetic_client.post("/check-all")
    assert resp.status_code == 200
    assert "deep check" in resp.text  # the matrix + pill recomputed authoritatively


def test_synthetic_check_on_missing_project_is_honest_absence(synthetic_client: TestClient) -> None:
    resp = synthetic_client.post("/p/ghost/check")
    assert resp.status_code == 200
    assert "UNKNOWN" in resp.text  # honest-absence, never a crash


def test_synthetic_force_sync_on_missing_project_is_honest_absence(
    synthetic_client: TestClient,
) -> None:
    resp = synthetic_client.post("/p/ghost/force-sync")
    assert resp.status_code == 200
    assert "UNKNOWN" in resp.text
