"""End-to-end integration tests for the §13.7 retire (soft-delete) flow.

Retire is an in-place frontmatter flip on the meta entry (state →
`retired`) committed via an auto-merged PR — the same machinery as
graduation, reused. The distinguishing rules under test:

  * Authority (§3.1): RFC owners (frontmatter) and site `owner`-role
    holders may retire; app admins may NOT. Un-retire is site-owners-only.
  * Visibility (§13.7): a retired entry drops out of the catalog
    (`GET /api/rfcs`), and `GET /api/rfcs/<slug>` 404s for everyone except
    a site owner (so the un-retire affordance has a surface).
  * Reversibility: a site owner can un-retire, restoring the prior state
    (and keeping the integer id intact); an RFC owner cannot.

These walk against the in-process FakeGitea from test_propose_vertical.py,
reusing the super-draft seed + sync graduation seam from the graduation
suite.
"""
from __future__ import annotations

import json as _json

from test_propose_vertical import (  # noqa: F401
    FakeGitea,
    app_with_fake_gitea,
    grant_rfc_collaborator,
    provision_user_row,
    sign_in_as,
    tmp_env,
)
from test_super_draft_vertical import seed_super_draft  # noqa: F401
from test_graduation_vertical import PITCH, seed_owned_super_draft  # noqa: F401


def _catalog_slugs(client) -> set[str]:
    return {i["slug"] for i in client.get("/api/rfcs").json()["items"]}


def test_rfc_owner_can_retire_and_entry_leaves_every_surface(app_with_fake_gitea):
    """An RFC owner (frontmatter, role contributor) retires their own
    super-draft. The entry flips to `retired`, drops out of the catalog,
    and `GET /api/rfcs/<slug>` 404s for them (they are not a site owner)."""
    from fastapi.testclient import TestClient
    from app import db, entry as entry_mod

    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=2, login="carol", role="contributor")
        seed_owned_super_draft(fake, slug="ohm", title="OHM",
                               pitch=PITCH, owners=["carol"], arbiters=["carol"])
        sign_in_as(client, user_id=2, gitea_login="carol",
                   display_name="Carol", role="contributor")

        assert "ohm" in _catalog_slugs(client)

        r = client.post("/api/rfcs/ohm/retire")
        assert r.status_code == 200, r.text
        assert r.json()["state"] == "retired"

        # §22.4a SLICE-4: the state flip lands in the metadata sidecar and the
        # `.md` is lazy-migrated to a clean body-only file (INV-2). Fields kept.
        import yaml as _yaml
        sc = _yaml.safe_load(
            fake.files[("wiggleverse", "meta", "main", "rfcs/ohm.meta.yaml")]["content"]
        )
        assert sc["state"] == "retired"
        assert "carol" in sc["owners"]
        assert "---" not in fake.files[("wiggleverse", "meta", "main", "rfcs/ohm.md")]["content"]

        # Cache flipped; gone from the catalog.
        cached = db.conn().execute(
            "SELECT state FROM cached_rfcs WHERE slug = 'ohm'"
        ).fetchone()
        assert cached["state"] == "retired"
        assert "ohm" not in _catalog_slugs(client)

        # The RFC owner is NOT a site owner → 404 on the entry read.
        assert client.get("/api/rfcs/ohm").status_code == 404

        # Audit row records the prior state for un-retire.
        row = db.conn().execute(
            "SELECT details FROM actions WHERE rfc_slug='ohm' AND action_kind='retire' ORDER BY id DESC LIMIT 1"
        ).fetchone()
        assert row is not None
        assert _json.loads(row["details"])["prior_state"] == "super-draft"


def test_site_owner_sees_retired_entry_but_admin_and_others_404(app_with_fake_gitea):
    """`GET /api/rfcs/<slug>` for a retired entry: site owner gets 200
    (so the un-retire UI has a surface); an admin, a non-owner contributor,
    and an anonymous viewer all get 404."""
    from fastapi.testclient import TestClient

    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=1, login="ben", role="owner")
        provision_user_row(user_id=2, login="carol", role="contributor")
        provision_user_row(user_id=3, login="dave", role="admin")
        seed_owned_super_draft(fake, slug="ohm", title="OHM",
                               pitch=PITCH, owners=["carol"])

        sign_in_as(client, user_id=2, gitea_login="carol",
                   display_name="Carol", role="contributor")
        assert client.post("/api/rfcs/ohm/retire").status_code == 200

        # Site owner: 200.
        sign_in_as(client, user_id=1, gitea_login="ben",
                   display_name="Ben", role="owner")
        r = client.get("/api/rfcs/ohm")
        assert r.status_code == 200, r.text
        assert r.json()["state"] == "retired"

        # Admin (not site owner): 404.
        sign_in_as(client, user_id=3, gitea_login="dave",
                   display_name="Dave", role="admin")
        assert client.get("/api/rfcs/ohm").status_code == 404

        # Non-owner contributor: 404.
        provision_user_row(user_id=4, login="erin", role="contributor")
        sign_in_as(client, user_id=4, gitea_login="erin",
                   display_name="Erin", role="contributor")
        assert client.get("/api/rfcs/ohm").status_code == 404


def test_admin_cannot_retire(app_with_fake_gitea):
    """§3.1: retire authority excludes app admins. An admin who is not an
    RFC owner gets 403 — the one lifecycle action where admin authority
    does not apply."""
    from fastapi.testclient import TestClient

    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=3, login="dave", role="admin")
        seed_owned_super_draft(fake, slug="ohm", title="OHM",
                               pitch=PITCH, owners=["carol"])
        sign_in_as(client, user_id=3, gitea_login="dave",
                   display_name="Dave", role="admin")
        r = client.post("/api/rfcs/ohm/retire")
        assert r.status_code == 403, r.text


def test_non_owner_contributor_cannot_retire(app_with_fake_gitea):
    """A signed-in contributor who is not an RFC owner gets 403."""
    from fastapi.testclient import TestClient

    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=4, login="erin", role="contributor")
        seed_owned_super_draft(fake, slug="ohm", title="OHM",
                               pitch=PITCH, owners=["carol"])
        sign_in_as(client, user_id=4, gitea_login="erin",
                   display_name="Erin", role="contributor")
        assert client.post("/api/rfcs/ohm/retire").status_code == 403


def test_site_owner_can_retire_active_and_unretire_restores_active_with_id(app_with_fake_gitea):
    """Round-trip on an active RFC: graduate (with a number) → retire →
    un-retire. The integer id survives, and un-retire restores `active`."""
    from fastapi.testclient import TestClient
    from app import db, entry as entry_mod

    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=1, login="ben", role="owner")
        seed_owned_super_draft(fake, slug="ohm", title="OHM",
                               pitch=PITCH, owners=["ben"], arbiters=["ben"])
        sign_in_as(client, user_id=1, gitea_login="ben",
                   display_name="Ben", role="owner", email="ben@test")

        # Graduate with a number.
        assert client.post("/api/rfcs/ohm/graduate?_sync=1",
                           json={"rfc_id": "RFC-0042", "owners": ["ben"]}).status_code == 200

        # Retire (site owner).
        assert client.post("/api/rfcs/ohm/retire").json()["state"] == "retired"
        assert "ohm" not in _catalog_slugs(client)

        # Un-retire (site owner) restores active, id intact.
        r = client.post("/api/rfcs/ohm/unretire")
        assert r.status_code == 200, r.text
        assert r.json()["state"] == "active"

        import yaml as _yaml
        sc = _yaml.safe_load(
            fake.files[("wiggleverse", "meta", "main", "rfcs/ohm.meta.yaml")]["content"]
        )
        assert sc["state"] == "active"
        assert sc["id"] == "RFC-0042"

        cached = db.conn().execute(
            "SELECT state, rfc_id FROM cached_rfcs WHERE slug = 'ohm'"
        ).fetchone()
        assert cached["state"] == "active"
        assert cached["rfc_id"] == "RFC-0042"
        assert "ohm" in _catalog_slugs(client)


def test_rfc_owner_cannot_unretire(app_with_fake_gitea):
    """Un-retire is site-owner-only: an RFC owner who could retire cannot
    bring it back (the soft-delete is recoverable only by the operator)."""
    from fastapi.testclient import TestClient

    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=2, login="carol", role="contributor")
        seed_owned_super_draft(fake, slug="ohm", title="OHM",
                               pitch=PITCH, owners=["carol"])
        sign_in_as(client, user_id=2, gitea_login="carol",
                   display_name="Carol", role="contributor")
        assert client.post("/api/rfcs/ohm/retire").status_code == 200
        # Same RFC owner tries to un-retire → 403.
        assert client.post("/api/rfcs/ohm/unretire").status_code == 403


def test_admin_retired_list_is_site_owner_only(app_with_fake_gitea):
    """GET /api/admin/retired-rfcs lists retired entries for a site owner;
    an admin (who lacks un-retire authority) gets 403."""
    from fastapi.testclient import TestClient

    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=1, login="ben", role="owner")
        provision_user_row(user_id=2, login="carol", role="contributor")
        provision_user_row(user_id=3, login="dave", role="admin")
        seed_owned_super_draft(fake, slug="ohm", title="OHM",
                               pitch=PITCH, owners=["carol"])
        sign_in_as(client, user_id=2, gitea_login="carol",
                   display_name="Carol", role="contributor")
        assert client.post("/api/rfcs/ohm/retire").status_code == 200

        # Admin: 403.
        sign_in_as(client, user_id=3, gitea_login="dave",
                   display_name="Dave", role="admin")
        assert client.get("/api/admin/retired-rfcs").status_code == 403

        # Site owner: 200, sees ohm with its restore target.
        sign_in_as(client, user_id=1, gitea_login="ben",
                   display_name="Ben", role="owner")
        r = client.get("/api/admin/retired-rfcs")
        assert r.status_code == 200, r.text
        items = r.json()["items"]
        ohm = next(i for i in items if i["slug"] == "ohm")
        assert ohm["restores_to"] == "super-draft"


def test_retired_entry_refuses_discussion_reads(app_with_fake_gitea):
    """A retired entry refuses content reads of every shape (§13.7) — the
    discussion/branch surfaces 404/409 rather than serve a soft-deleted RFC."""
    from fastapi.testclient import TestClient

    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=2, login="carol", role="contributor")
        seed_owned_super_draft(fake, slug="ohm", title="OHM",
                               pitch=PITCH, owners=["carol"])
        sign_in_as(client, user_id=2, gitea_login="carol",
                   display_name="Carol", role="contributor")
        assert client.post("/api/rfcs/ohm/retire").status_code == 200

        # The /main branch surface no longer serves it.
        assert client.get("/api/rfcs/ohm/main").status_code in (404, 409)
