"""End-to-end integration test for the §19.2 "in-app merge for
metadata PRs" candidate that Slice 8 settles.

Slice 4 lands the §9.5 metadata pane that opens a `meta_metadata` PR
(title/tags edit) on the meta repo. The Slice 4 build deferred the
merge surface to Gitea web for v1 — `api_prs.merge_pr` was scoped to
body-changing PRs (`rfc_branch` and `meta_body_edit`). Slice 8 extends
`_require_pr` to include `meta_metadata` so the merge gesture lands
in-app. The diff-rendered review surface degrades gracefully — a
metadata PR doesn't have a body diff worth reviewing — but the merge
button works.

The tests prove:

  * `POST /api/rfcs/<slug>/prs/<n>/merge` accepts a metadata PR and
    runs the underlying merge.
  * After the merge, the meta entry's title/tags carry forward and
    the cache reflects the new values.
  * A contributor (no role on the super-draft) is refused.
  * Withdraw also works against a metadata PR — the same surface
    supports the §10.8 withdraw gesture.
"""
from __future__ import annotations

import pytest

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


PITCH = (
    "Open Human Model is a framework for representing humans.\n\n"
    "It defines consent, trait, and agency in compatible terms."
)


def test_metadata_pr_merges_in_app(app_with_fake_gitea):
    """The headline assertion: an owner can hit the same
    `prs/<n>/merge` endpoint for a metadata PR and the change lands."""
    from fastapi.testclient import TestClient
    from app import db

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

        # Open the metadata PR via the §9.5 endpoint.
        r = client.post(
            "/api/rfcs/ohm/metadata",
            json={"title": "Open Human Model", "tags": ["identity", "schema"]},
        )
        assert r.status_code == 200, r.text
        pr_number = r.json()["pr_number"]

        # Verify the kind landed as meta_metadata.
        row = db.conn().execute(
            "SELECT pr_kind FROM cached_prs WHERE pr_number = ?", (pr_number,)
        ).fetchone()
        assert row["pr_kind"] == "meta_metadata"

        # Merge via the §10.5 endpoint — the Slice 8 extension.
        r = client.post(f"/api/rfcs/ohm/prs/{pr_number}/merge")
        assert r.status_code == 200, r.text

        # The cached entry now carries the new title + tags.
        cached = db.conn().execute(
            "SELECT title, tags_json FROM cached_rfcs WHERE slug = 'ohm'"
        ).fetchone()
        import json as _json
        tags = _json.loads(cached["tags_json"])
        assert cached["title"] == "Open Human Model"
        assert "identity" in tags and "schema" in tags

        # PR row's state is now 'merged'.
        post = db.conn().execute(
            "SELECT state FROM cached_prs WHERE pr_number = ?", (pr_number,)
        ).fetchone()
        assert post["state"] == "merged"


def test_metadata_pr_merge_refused_for_plain_contributor(app_with_fake_gitea):
    """§6.1 + §6.3: only owners/arbiters/admins can merge.

    A plain contributor without any per-RFC authority gets 403, same
    as the existing body-edit PR merge surface. Confirms the
    extension didn't widen the permission gate."""
    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="alice", role="contributor")
        seed_super_draft(fake, slug="ohm", title="OHM", pitch=PITCH)
        sign_in_as(client, user_id=1, gitea_login="ben",
                   display_name="Ben", role="owner", email="ben@test")

        # Ben opens the metadata PR.
        r = client.post(
            "/api/rfcs/ohm/metadata",
            json={"title": "OHM (revised)", "tags": ["identity"]},
        )
        pr_number = r.json()["pr_number"]

        # Alice (plain contributor) tries to merge — 403.
        sign_in_as(client, user_id=2, gitea_login="alice",
                   display_name="Alice", role="contributor")
        r = client.post(f"/api/rfcs/ohm/prs/{pr_number}/merge")
        assert r.status_code == 403


def test_metadata_pr_withdraw_works(app_with_fake_gitea):
    """§10.8 withdraw surface also handles meta_metadata PRs uniformly
    — the API doesn't care which kind."""
    from fastapi.testclient import TestClient
    from app import db

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

        r = client.post(
            "/api/rfcs/ohm/metadata",
            json={"title": "Something else", "tags": []},
        )
        pr_number = r.json()["pr_number"]

        r = client.post(f"/api/rfcs/ohm/prs/{pr_number}/withdraw")
        assert r.status_code == 200, r.text

        post = db.conn().execute(
            "SELECT state FROM cached_prs WHERE pr_number = ?", (pr_number,)
        ).fetchone()
        # Withdraw flips to 'withdrawn' via the audit-log marker the
        # reconciler reads, but a direct withdraw via api_prs may
        # leave it 'closed' depending on the refresh path. Either is
        # the closed-not-merged shape the surface needs.
        assert post["state"] in ("withdrawn", "closed")
