"""Regression tests for install-time instruction strings.

These strings live in graphify/__main__.py and are written into project-local
files (CLAUDE.md, AGENTS.md, GEMINI.md, .cursor/rules/, .kiro/steering/, etc.)
or into in-process hook payloads. Earlier versions of graphify told every
assistant to "ALWAYS read graphify-out/GRAPH_REPORT.md before answering" —
which silently increased per-question token usage in Claude Code sessions
(issue #580). This file locks in the query-first policy so a future revert
or partial change is caught by CI.
"""
from __future__ import annotations
import json

from graphify.__main__ import (
    _SEARCH_NUDGE,
    _READ_NUDGE,
    _skill_registration,
    _CLAUDE_MD_SECTION,
    _AGENTS_MD_SECTION,
    _GEMINI_MD_SECTION,
    _GEMINI_NUDGE_TEXT,
    _VSCODE_INSTRUCTIONS_SECTION,
    _ANTIGRAVITY_RULES,
    _KIRO_STEERING,
    _CURSOR_RULE,
    _OPENCODE_PLUGIN_JS,
    _DEVIN_RULES,
)


# All install-surface text rendered as plain strings, in one place.
# Hook constants are dicts/JSON; serialize them so we can do substring checks
# against the actual payload text the assistant will receive.
_INSTALL_TEXTS: dict[str, str] = {
    "_SEARCH_NUDGE": _SEARCH_NUDGE,
    "_READ_NUDGE": _READ_NUDGE,
    "_CLAUDE_MD_SECTION": _CLAUDE_MD_SECTION,
    "_AGENTS_MD_SECTION": _AGENTS_MD_SECTION,
    "_GEMINI_MD_SECTION": _GEMINI_MD_SECTION,
    "_GEMINI_NUDGE_TEXT": _GEMINI_NUDGE_TEXT,
    "_VSCODE_INSTRUCTIONS_SECTION": _VSCODE_INSTRUCTIONS_SECTION,
    "_ANTIGRAVITY_RULES": _ANTIGRAVITY_RULES,
    "_KIRO_STEERING": _KIRO_STEERING,
    "_CURSOR_RULE": _CURSOR_RULE,
    "_OPENCODE_PLUGIN_JS": _OPENCODE_PLUGIN_JS,
    "_DEVIN_RULES": _DEVIN_RULES,
}


def test_every_install_surface_recommends_graphify_query():
    """All ten install surfaces must point the assistant at `graphify query`
    as the first action for codebase questions. This is the load-bearing
    fix for issue #580 — the alternative (reading GRAPH_REPORT.md) costs
    ~10x more tokens per question and made the project worse-than-baseline
    in real Claude Code sessions."""
    missing: list[str] = []
    for name, text in _INSTALL_TEXTS.items():
        if "graphify query" not in text:
            missing.append(name)
    assert not missing, (
        f"these install surfaces no longer mention `graphify query`: {missing}. "
        f"If you removed it intentionally, consider whether issue #580 is back."
    )


def test_no_install_surface_demands_reading_the_full_report_first():
    """The pre-fix instructions told assistants to read GRAPH_REPORT.md as
    their first action for codebase questions. The new policy demotes the
    report to a fallback; any phrasing that puts reading the report BEFORE
    other actions for codebase questions is a regression of issue #580.

    Uses regex patterns instead of literal strings so a future revert that
    rephrases ("MUST read", "Always consult", "first task is to open ...")
    is also caught. Note: bare 'ALWAYS' is NOT banned because
    ``alwaysApply: true`` (Cursor) and ``trigger: always_on`` (Antigravity)
    are legitimate platform metadata, not the bug.
    """
    import re
    banned = [
        # "read ... GRAPH_REPORT.md ... before"
        re.compile(r"read[^.\n]{0,80}GRAPH_REPORT\.md[^.\n]{0,80}before", re.IGNORECASE),
        # "first tool call ... GRAPH_REPORT" (VS Code variant)
        re.compile(r"first\s+tool\s+call[^.\n]{0,80}GRAPH_REPORT", re.IGNORECASE),
        # "ALWAYS read ... GRAPH_REPORT" (catches the literal old text and minor variants)
        re.compile(r"always\s+read[^.\n]{0,80}GRAPH_REPORT", re.IGNORECASE),
    ]
    hits: list[tuple[str, str]] = []
    for name, text in _INSTALL_TEXTS.items():
        for pattern in banned:
            m = pattern.search(text)
            if m:
                hits.append((name, m.group(0)))
    assert not hits, (
        f"banned report-first phrasing reappeared: {hits}. "
        f"This regresses issue #580."
    )


def test_report_is_still_referenced_as_fallback():
    """The fix demotes GRAPH_REPORT.md, it doesn't delete the reference.
    Most install surfaces should still mention the report as the deep-dive
    artifact so users know it exists for broad architecture review.
    (Hook payloads may or may not name the report; check the MD sections
    explicitly — those are the rule lists assistants follow.)"""
    md_section_texts = {
        "_CLAUDE_MD_SECTION": _CLAUDE_MD_SECTION,
        "_AGENTS_MD_SECTION": _AGENTS_MD_SECTION,
        "_GEMINI_MD_SECTION": _GEMINI_MD_SECTION,
        "_VSCODE_INSTRUCTIONS_SECTION": _VSCODE_INSTRUCTIONS_SECTION,
        "_ANTIGRAVITY_RULES": _ANTIGRAVITY_RULES,
        "_KIRO_STEERING": _KIRO_STEERING,
        "_CURSOR_RULE": _CURSOR_RULE,
        "_DEVIN_RULES": _DEVIN_RULES,
    }
    missing: list[str] = []
    for name, text in md_section_texts.items():
        if "GRAPH_REPORT.md" not in text:
            missing.append(name)
    assert not missing, (
        f"these install sections no longer mention GRAPH_REPORT.md at all: {missing}. "
        f"The fix should demote the report, not delete the reference — users need to know "
        f"it's available for broad-architecture queries."
    )


def test_agents_section_does_not_skip_dirty_graph_output():
    assert "Dirty graphify-out/ files are expected" in _AGENTS_MD_SECTION
    assert "not a reason to skip graphify" in _AGENTS_MD_SECTION


def test_agents_section_uses_generic_graphify_instruction():
    assert "`skill` tool" not in _AGENTS_MD_SECTION
    assert 'skill: "graphify"' not in _AGENTS_MD_SECTION
    assert "use the installed graphify skill" in _AGENTS_MD_SECTION


def test_skill_registration_uses_host_generic_instruction():
    reg = _skill_registration()
    assert 'skill: "graphify"' not in reg
    assert "Skill tool" not in reg
    assert "use the installed graphify skill or instructions" in reg


def test_how_it_works_clarifies_code_only_semantic_extraction():
    from pathlib import Path
    doc = (Path(__file__).parent.parent / "docs" / "how-it-works.md").read_text(encoding="utf-8")
    assert "Code files are not sent to the LLM semantic extractor" in doc
    assert "code files, Pass 3 is skipped entirely" in doc
    assert "docs, papers, images, and transcripts" in doc
