"""SLICE-1 integration — the corpus mirror reads sidecars (dual-read) and
derives the malformed flag (PUC-6, INV-3/INV-6).

Per docs/design/2026-06-06-configurable-collection-metadata.md §6.2-6.3.
"""
from __future__ import annotations

import asyncio
import json

from fastapi.testclient import TestClient

from app import cache, db, gitea as gitea_mod
from app.config import load_config

from test_propose_vertical import (  # noqa: F401 (fixtures)
    app_with_fake_gitea,
    tmp_env,
)


def _refresh():
    cfg = load_config()
    asyncio.run(cache.refresh_meta_repo(cfg, gitea_mod.Gitea(cfg)))


def _row(slug):
    return db.conn().execute(
        "SELECT * FROM cached_rfcs WHERE slug = ?", (slug,)
    ).fetchone()


def test_mirror_reads_metadata_from_sidecar(app_with_fake_gitea):
    app, fake = app_with_fake_gitea
    with TestClient(app):
        # A migrated entry: body-only .md + a sidecar holding the metadata.
        fake.files[("wiggleverse", "meta", "main", "rfcs/sidecar-one.md")] = {
            "content": "Just the prose body.\n", "sha": "s1"}
        fake.files[("wiggleverse", "meta", "main", "rfcs/sidecar-one.meta.yaml")] = {
            "content": "slug: sidecar-one\ntitle: From Sidecar\nstate: active\ntags:\n- alpha\n",
            "sha": "m1"}
        _refresh()

        row = _row("sidecar-one")
        assert row is not None
        assert row["title"] == "From Sidecar"
        assert row["state"] == "active"
        assert row["body"].strip() == "Just the prose body."
        assert row["metadata_malformed"] == 0


def test_malformed_sidecar_flags_but_still_loads(app_with_fake_gitea):
    app, fake = app_with_fake_gitea
    with TestClient(app):
        # .md still has frontmatter; sidecar is malformed (a list, not a map).
        fake.files[("wiggleverse", "meta", "main", "rfcs/bad-meta.md")] = {
            "content": "---\nslug: bad-meta\ntitle: Legacy Title\nstate: active\n---\n\nBody.\n",
            "sha": "b1"}
        fake.files[("wiggleverse", "meta", "main", "rfcs/bad-meta.meta.yaml")] = {
            "content": "- not\n- a\n- mapping\n", "sha": "b2"}
        _refresh()

        row = _row("bad-meta")
        assert row is not None  # INV-3: still loads
        assert row["metadata_malformed"] == 1
        # Falls back to the legacy .md frontmatter for the metadata.
        assert row["title"] == "Legacy Title"


def test_malformed_flag_surfaces_in_catalog_api(app_with_fake_gitea):
    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        fake.files[("wiggleverse", "meta", "main", "rfcs/flagged.md")] = {
            "content": "---\nslug: flagged\ntitle: Flagged\nstate: active\n---\n\nB.\n",
            "sha": "f1"}
        fake.files[("wiggleverse", "meta", "main", "rfcs/flagged.meta.yaml")] = {
            "content": "just a scalar\n", "sha": "f2"}
        fake.files[("wiggleverse", "meta", "main", "rfcs/clean.md")] = {
            "content": "---\nslug: clean\ntitle: Clean\nstate: active\n---\n\nB.\n",
            "sha": "c1"}
        _refresh()

        items = {i["slug"]: i for i in client.get("/api/rfcs").json()["items"]}
        assert items["flagged"]["metadata_malformed"] is True
        assert items["clean"]["metadata_malformed"] is False

        # And on the detail view.
        assert client.get("/api/rfcs/flagged").json()["metadata_malformed"] is True


def test_malformed_sidecar_on_migrated_entry_still_loads_flagged(app_with_fake_gitea):
    # INV-3 regression: a migrated (body-only .md) entry whose sidecar is
    # corrupt must NOT vanish from the catalog — it loads (slug from the
    # filename stem) and is flagged malformed.
    app, fake = app_with_fake_gitea
    with TestClient(app):
        fake.files[("wiggleverse", "meta", "main", "rfcs/orphaned.md")] = {
            "content": "Just the body, no frontmatter.\n", "sha": "o1"}
        fake.files[("wiggleverse", "meta", "main", "rfcs/orphaned.meta.yaml")] = {
            "content": "- corrupt\n- list\n", "sha": "o2"}
        _refresh()

        row = _row("orphaned")
        assert row is not None  # did not vanish
        assert row["metadata_malformed"] == 1
        assert row["body"].strip() == "Just the body, no frontmatter."


def _set_default_fields_schema(schema):
    # apply_registry leaves the default collection's config_json untouched, so a
    # schema set here survives a corpus refresh (refresh_meta_repo only).
    db.conn().execute(
        "UPDATE collections SET config_json = ? WHERE id = 'default'",
        (json.dumps({"fields": schema}),))


# ---- §22.4a SLICE-2: advisory schema validation at ingest (INV-3) ----

def test_schema_violation_flags_malformed(app_with_fake_gitea):
    app, fake = app_with_fake_gitea
    with TestClient(app):
        _set_default_fields_schema(
            {"priority": {"type": "enum", "values": ["P0", "P1", "P2"]}})
        fake.files[("wiggleverse", "meta", "main", "rfcs/bad-prio.md")] = {
            "content": "---\nslug: bad-prio\ntitle: Bad\nstate: active\npriority: P9\n---\n\nB.\n",
            "sha": "bp1"}
        fake.files[("wiggleverse", "meta", "main", "rfcs/good-prio.md")] = {
            "content": "---\nslug: good-prio\ntitle: Good\nstate: active\npriority: P0\n---\n\nB.\n",
            "sha": "gp1"}
        _refresh()

        assert _row("bad-prio")["metadata_malformed"] == 1   # INV-3: flagged
        assert _row("bad-prio")["title"] == "Bad"            # still loads
        assert _row("good-prio")["metadata_malformed"] == 0


def test_schema_ignores_undeclared_keys(app_with_fake_gitea):
    # INV-7: keys the schema doesn't declare ride along and never flag malformed.
    app, fake = app_with_fake_gitea
    with TestClient(app):
        _set_default_fields_schema(
            {"priority": {"type": "enum", "values": ["P0", "P1"]}})
        fake.files[("wiggleverse", "meta", "main", "rfcs/extra-key.md")] = {
            "content": "---\nslug: extra-key\ntitle: Extra\nstate: active\nowner: hasan\n---\n\nB.\n",
            "sha": "ek1"}
        _refresh()

        assert _row("extra-key")["metadata_malformed"] == 0


def test_no_schema_never_flags(app_with_fake_gitea):
    # INV-5: a collection with no field schema validates nothing, even when an
    # entry carries values that would fail a schema if one existed.
    app, fake = app_with_fake_gitea
    with TestClient(app):
        fake.files[("wiggleverse", "meta", "main", "rfcs/anything.md")] = {
            "content": "---\nslug: anything\ntitle: Any\nstate: active\npriority: whatever\n---\n\nB.\n",
            "sha": "an1"}
        _refresh()

        assert _row("anything")["metadata_malformed"] == 0


def test_legacy_collection_without_sidecars_unchanged(app_with_fake_gitea):
    app, fake = app_with_fake_gitea
    with TestClient(app):
        fake.files[("wiggleverse", "meta", "main", "rfcs/legacy.md")] = {
            "content": "---\nslug: legacy\ntitle: Legacy\nstate: super-draft\n---\n\nPitch.\n",
            "sha": "l1"}
        _refresh()

        row = _row("legacy")
        assert row is not None
        assert row["title"] == "Legacy"
        assert row["state"] == "super-draft"
        assert row["body"].strip() == "Pitch."
        assert row["metadata_malformed"] == 0
