"""Tests for agent frontmatter validation — top-level vs permission-nested fields.

Covers issue #48 acceptance criteria:
- ``reviewer.md`` and ``memory-syncer.md`` frontmatter does NOT contain a
  top-level ``doom_loop`` field (outside the ``permission:`` block).
  A top-level ``doom_loop`` is invalid — it is not in the opencode ``AgentV2.Info``
  schema and gets forwarded to the provider as a model param, causing
  ``AI_APICallError: Extra inputs are not permitted`` on strict providers.
- ``doom_loop`` INSIDE the ``permission:`` block IS present in all 2 files
  (valid — it is the real permission guard).
- ``steps: 150`` is present and unchanged in all 2 files.

PyYAML is not a project dependency, so frontmatter is parsed manually by
splitting on ``---`` delimiters and tracking indentation (top-level fields have
no indent; ``permission:``-nested fields have 2-space indent). This mirrors the
parsing approach used in ``.opencode/scripts/check-permissions.py``.
"""

from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parent.parent
AGENTS_DIR = REPO_ROOT / ".opencode" / "agents"

AGENT_FILES = [
    AGENTS_DIR / "reviewer.md",
    AGENTS_DIR / "memory-syncer.md",
]


def _parse_frontmatter(filepath: Path) -> dict:
    """Parse YAML frontmatter into a nested dict (manual, no pyyaml).

    Returns a dict where top-level keys map to either a scalar string or a
    nested dict (for indented blocks like ``permission:``).
    """
    content = filepath.read_text()
    parts = content.split("---", 2)
    assert len(parts) >= 3, f"{filepath.name}: no frontmatter block found"
    frontmatter = parts[1]
    return _parse_yaml_block(frontmatter)


def _parse_yaml_block(text: str) -> dict:
    """Minimal YAML parser for the subset used in agent frontmatter.

    Handles: top-level ``key: value``, nested ``key:`` blocks (indented),
    and nested ``key: value`` (indented). Values are strings; nested blocks
    become dicts. Does NOT handle lists, quoting, or complex YAML — agent
    frontmatter only uses the simple key-value subset.
    """
    result: dict = {}
    current_dict: dict | None = None
    for line in text.split("\n"):
        if not line.strip() or line.strip().startswith("#"):
            continue
        stripped = line.lstrip()
        indent = len(line) - len(stripped)
        if indent == 0:
            current_dict = None
            if ":" in stripped:
                key, _, value = stripped.partition(":")
                key = key.strip()
                value = value.strip()
                if value:
                    result[key] = value
                else:
                    current_dict = {}
                    result[key] = current_dict
        elif current_dict is not None and indent >= 2:
            if ":" in stripped:
                key, _, value = stripped.partition(":")
                key = key.strip()
                value = value.strip()
                if value:
                    current_dict[key] = value
    return result


# ── all agent files exist ───────────────────────────────────────────────────


def test_agent_files_exist():
    """All 2 agent files exist in .opencode/agents/."""
    for f in AGENT_FILES:
        assert f.exists(), f"agent file missing: {f}"


# ── no top-level doom_loop (issue #48 — the invalid field) ──────────────────


def test_no_top_level_doom_loop():
    """Frontmatter of all 2 agents has NO top-level ``doom_loop`` key.

    A top-level ``doom_loop`` is invalid — outside ``permission:`` it is not
    a recognised agent field and gets forwarded to the provider.
    """
    for f in AGENT_FILES:
        fm = _parse_frontmatter(f)
        assert "doom_loop" not in fm, (
            f"{f.name}: top-level 'doom_loop' field must be removed — it is "
            "invalid outside the permission: block"
        )


def test_no_top_level_doom_loop_raw():
    """Raw text check: no line ``doom_loop:`` at indent 0 in any agent file.

    Belt-and-suspenders alongside the parsed check — catches edge cases where
    the parser might miss something.
    """
    for f in AGENT_FILES:
        content = f.read_text()
        parts = content.split("---", 2)
        assert len(parts) >= 3, f"{f.name}: no frontmatter"
        fm = parts[1]
        for line in fm.split("\n"):
            if line.startswith("doom_loop:"):
                raise AssertionError(
                    f"{f.name}: top-level 'doom_loop:' line found at indent 0 — "
                    f"must be removed: {line!r}"
                )


# ── permission.doom_loop present (the valid guard) ──────────────────────────


def test_permission_doom_loop_present():
    """``doom_loop: deny`` INSIDE ``permission:`` is present in all 2 files.

    This is the valid permission guard — it must NOT be removed.
    """
    for f in AGENT_FILES:
        fm = _parse_frontmatter(f)
        assert "permission" in fm, f"{f.name}: no 'permission' block"
        permission = fm["permission"]
        assert isinstance(permission, dict), f"{f.name}: 'permission' is not a block"
        assert "doom_loop" in permission, (
            f"{f.name}: 'doom_loop' missing from permission: block — must be kept"
        )
        assert permission["doom_loop"] == "deny", (
            f"{f.name}: permission.doom_loop must be 'deny', got {permission['doom_loop']!r}"
        )


# ── steps: 150 present and unchanged ─────────────────────────────────────────


def test_steps_150_present():
    """``steps: 150`` is present in all 2 agent files (must not be changed)."""
    for f in AGENT_FILES:
        fm = _parse_frontmatter(f)
        assert "steps" in fm, f"{f.name}: 'steps' field missing"
        assert fm["steps"] == "150", f"{f.name}: steps must be '150', got {fm['steps']!r}"


# ── frontmatter is well-formed (parses without error) ───────────────────────


def test_frontmatter_parseable():
    """All 2 agent files have parseable frontmatter (split by ---)."""
    for f in AGENT_FILES:
        content = f.read_text()
        parts = content.split("---", 2)
        assert len(parts) >= 3, f"{f.name}: frontmatter not delimited by ---"
        fm = _parse_frontmatter(f)
        assert "description" in fm, f"{f.name}: 'description' field missing"
        assert "mode" in fm, f"{f.name}: 'mode' field missing"


if __name__ == "__main__":
    import pytest

    pytest.main([__file__, "-v"])
