"""Verifier handlers (#184–#187, #192).

Thin MCP-tool wrappers over the pure verifiers in
``active_claude_github_mcp.validators``. The deterministic verifiers
(plan / diff) are sync (pure-CPU, no IO) and live separately so they
can be unit-tested without dragging the MCP / handler / context
surface in. The LLM verifier (#187) is async — it makes an Anthropic
API call — but follows the same shape: pure validator core, thin
handler wrapper.
"""
from __future__ import annotations

from pathlib import Path

from mcp.types import TextContent

from ...context import ServerContext
from ...models import GithubConfig
from ...validators.diff import verify_diff_covers_plan as _verify_diff
from ...validators.llm import (
    verify_existing_code_assumptions as _verify_assumptions,
)
from ...validators.plan import verify_plan_completeness as _verify_plan
from ..helpers import ok


# Path patterns the built-in skill-prose rule (#192) flags as
# requiring a paired edit to the project's running-log Discussion.
# Matched against repo-relative paths; a hit anywhere fires the rule.
_SKILL_PROSE_PATTERN = r"\.claude/skills/[^/]+/[^/]+\.md$"


def default_project_rules(config: GithubConfig) -> list[dict]:
    """Compose the default `project_rules` set for a given GithubConfig.

    Currently produces the single skill-prose ↔ running-log rule
    (#192) when `running_log_discussion_number` is configured. When
    the field is None the surface is a no-op — callers receive an
    empty list and the verifier behaves as in 2.1.
    """
    rules: list[dict] = []
    if config.running_log_discussion_number is not None:
        rules.append({
            "name": "skill_prose_requires_running_log_update",
            "if_diff_touches_pattern": _SKILL_PROSE_PATTERN,
            "must_also_touch_discussion": config.running_log_discussion_number,
            "severity": "warning",
            "message": (
                f"Discussion #{config.running_log_discussion_number} "
                "(running-log map) must be edited alongside skill-prose "
                "changes. Add a `## Edits to Discussion "
                f"#{config.running_log_discussion_number}` section to "
                "the PR description, or post a "
                f"`Updated Discussion #{config.running_log_discussion_number}"
                " with: …` comment."
            ),
        })
    return rules


async def verify_plan_completeness(
    ctx: ServerContext,
    plan_body: str,
) -> list[TextContent]:
    """Run the deterministic plan-frontmatter verifier (#184).

    Returns the structured report from
    ``validators.plan.verify_plan_completeness`` wrapped in an MCP
    ``TextContent`` payload. Handler is async by MCP convention even
    though the underlying verifier is pure-CPU sync.
    """
    return ok(_verify_plan(plan_body))


async def verify_existing_code_assumptions(
    ctx: ServerContext,
    plan_body: str,
    pr_diff: str,
    repo_root: str | None = None,
    model: str = "claude-sonnet-4-6",
    token_budget: int = 100_000,
) -> list[TextContent]:
    """Run the LLM-backed assumption verifier (#187).

    Reads files fresh from `repo_root` (defaults to `ctx.project_dir`)
    so the verifier works against the current head, not whatever
    Claude already had cached. Skips gracefully when
    `ANTHROPIC_API_KEY` is not configured — the surface is advisory
    by default per Discussion #182.
    """
    root = Path(repo_root) if repo_root else ctx.project_dir
    return ok(
        await _verify_assumptions(
            plan_body=plan_body,
            pr_diff=pr_diff,
            repo_root=root,
            api_key=ctx.anthropic_api_key,
            model=model,
            token_budget=token_budget,
        )
    )


async def verify_diff_covers_plan(
    ctx: ServerContext,
    plan_body: str,
    diff_text: str,
    evidence_text: str = "",
    project_rules: list[dict] | None = None,
) -> list[TextContent]:
    """Run the deterministic plan-vs-diff comparator (#185, #192).

    Sibling of ``verify_plan_completeness``. Pure-CPU sync verifier
    wrapped in an async handler per MCP convention.

    Rule resolution (#192): when `project_rules` is explicitly passed
    the caller's list is used verbatim. When omitted (the typical
    skill flow), `default_project_rules(ctx.config)` constructs the
    project's rule set from configured fields — currently just the
    skill-prose ↔ running-log dependency, conditional on
    `running_log_discussion_number`.
    """
    if project_rules is None:
        project_rules = default_project_rules(ctx.config)
    return ok(
        _verify_diff(
            plan_body,
            diff_text,
            evidence_text,
            project_rules=project_rules,
        )
    )
