"""SLICE-1 integration — frontmatter→sidecar migration tool (PUC-5).

Per docs/design/2026-06-06-configurable-collection-metadata.md §6.5 / §7.2:
a tool walks a collection; for each entry with legacy frontmatter it writes
`<slug>.meta.yaml` and rewrites `<slug>.md` to the body only — one commit per
collection, idempotent, preserving unknown keys (INV-7).
"""
from __future__ import annotations

import asyncio

import yaml

from app import gitea as gitea_mod, metadata
from app.config import load_config

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


ALPHA_MD = """---
slug: alpha
title: Alpha
state: active
owners:
- ben.stull
tags:
- one
priority: P1
---

Alpha body prose.
"""

BETA_MD = """---
slug: beta
title: Beta
state: super-draft
---

Beta body prose.
"""


def _seed_entries(fake):
    fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")] = {
        "content": ALPHA_MD, "sha": "a0001"}
    fake.files[("wiggleverse", "meta", "main", "rfcs/beta.md")] = {
        "content": BETA_MD, "sha": "b0001"}


def _run_migration(subfolder=""):
    cfg = load_config()
    gitea = gitea_mod.Gitea(cfg)
    return asyncio.run(
        metadata.migrate_collection(
            gitea, org="wiggleverse", repo="meta", subfolder=subfolder
        )
    )


def test_migration_writes_sidecars_and_strips_bodies(app_with_fake_gitea):
    _app, fake = app_with_fake_gitea
    _seed_entries(fake)

    summary = _run_migration()

    assert sorted(summary["migrated"]) == ["alpha", "beta"]

    # Sidecars now exist.
    alpha_sc = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.meta.yaml")]["content"]
    beta_sc = fake.files[("wiggleverse", "meta", "main", "rfcs/beta.meta.yaml")]["content"]
    alpha_vals = yaml.safe_load(alpha_sc)
    assert alpha_vals["slug"] == "alpha"
    assert alpha_vals["title"] == "Alpha"
    assert alpha_vals["tags"] == ["one"]
    # INV-7: the unknown key rides along into the sidecar.
    assert alpha_vals["priority"] == "P1"

    # .md bodies are stripped of frontmatter (INV-2).
    alpha_md = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"]
    assert "---" not in alpha_md
    assert "priority:" not in alpha_md
    assert alpha_md.strip() == "Alpha body prose."
    assert beta_sc  # beta got a sidecar too


def test_migration_is_idempotent(app_with_fake_gitea):
    _app, fake = app_with_fake_gitea
    _seed_entries(fake)

    first = _run_migration()
    assert sorted(first["migrated"]) == ["alpha", "beta"]
    assert first["committed"] is True

    commits_after_first = fake._commit_counter
    alpha_md_after_first = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"]

    second = _run_migration()
    assert second["migrated"] == []
    assert second["committed"] is False
    # No new commit; files untouched.
    assert fake._commit_counter == commits_after_first
    assert fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"] == alpha_md_after_first


def test_migration_one_commit_for_whole_collection(app_with_fake_gitea):
    _app, fake = app_with_fake_gitea
    _seed_entries(fake)
    before = fake._commit_counter
    _run_migration()
    # Two entries migrated in exactly one commit (ChangeFiles batch).
    assert fake._commit_counter == before + 1


def test_migration_dual_read_equivalence_after_migrate(app_with_fake_gitea):
    """An entry reads identically before and after migration (INV-6)."""
    _app, fake = app_with_fake_gitea
    _seed_entries(fake)

    before, _ = metadata.read_entry(ALPHA_MD, None)
    _run_migration()
    md = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.md")]["content"]
    sc = fake.files[("wiggleverse", "meta", "main", "rfcs/alpha.meta.yaml")]["content"]
    after, malformed = metadata.read_entry(md, sc)

    assert malformed is False
    assert after.slug == before.slug
    assert after.title == before.title
    assert after.state == before.state
    assert after.tags == before.tags
    assert after.extra == before.extra
    assert after.body.strip() == before.body.strip()
