"""Hard-stop terminal mode after reviewer capability denial (#197)."""

from __future__ import annotations

import re

TERMINAL_REPORT_HEADING = (
    "Cannot perform reviewer task under current profile. "
    "No reviewer mutations performed."
)

REVIEWER_CAPABILITY_TASKS = frozenset({
    "review_pr",
    "merge_pr",
    "blind_pr_queue_review",
    "pr_queue_cleanup",
    "pr-queue-cleanup",
    "request_changes_pr",
    "approve_pr",
})

BLOCKED_QUEUE_TOOLS = frozenset({
    "list_prs",
    "check_pr_eligibility",
    "view_pr",
    "submit_pr_review",
    "dry_run_pr_review",
    "merge_pr",
    "review_pr",
})

_session_terminal: dict | None = None


def enter_from_capability_result(capability: dict) -> dict | None:
    """Enter terminal mode when a reviewer/merge task is denied."""
    global _session_terminal
    task = (capability or {}).get("requested_task", "")
    required_role = (capability or {}).get("required_role_kind")
    if not capability.get("stop_required"):
        return None
    if required_role != "reviewer" and task not in REVIEWER_CAPABILITY_TASKS:
        return None
    record = {
        "active": True,
        "requested_task": task,
        "required_role_kind": required_role,
        "active_profile": capability.get("active_profile"),
        "active_identity": capability.get("active_identity"),
        "stop_required": True,
        "exact_safe_next_action": capability.get("exact_safe_next_action"),
        "terminal_message": TERMINAL_REPORT_HEADING,
    }
    _session_terminal = record
    return dict(record)


def _is_reviewer_denial(capability: dict) -> bool:
    task = (capability or {}).get("requested_task", "")
    required_role = (capability or {}).get("required_role_kind")
    return (
        required_role == "reviewer"
        or task in REVIEWER_CAPABILITY_TASKS
    )


def sync_from_capability_result(capability: dict) -> dict | None:
    """Enter or clear terminal mode from a capability resolution (#238).

    Reviewer denials activate terminal mode for the denied operation only.
    A later allowed task route clears stale denial state so author read-only
    tools (e.g. ``list_prs``) are not permanently blocked.
    """
    if (capability or {}).get("stop_required") and _is_reviewer_denial(capability):
        return enter_from_capability_result(capability)
    clear()
    return None


def enter_from_route_result(route: dict) -> dict | None:
    """Enter terminal mode from a role router wrong_role_stop (#206 compat)."""
    if (route or {}).get("route_result") != "wrong_role_stop":
        return None
    if route.get("required_role") != "reviewer":
        return None
    return enter_from_capability_result({
        "requested_task": route.get("task_type"),
        "required_role_kind": "reviewer",
        "stop_required": True,
        "active_profile": route.get("active_profile"),
        "active_identity": None,
        "exact_safe_next_action": route.get("message"),
    })


def is_active() -> bool:
    return bool(_session_terminal and _session_terminal.get("active"))


def active_record() -> dict | None:
    if not is_active():
        return None
    return dict(_session_terminal)


def clear():
    global _session_terminal
    _session_terminal = None


def check_reviewer_queue_tool(tool_name: str) -> tuple[bool, list[str]]:
    """Return (allowed, reasons). False when terminal mode blocks queue work."""
    if not is_active():
        return True, []
    name = (tool_name or "").strip().lower().removeprefix("gitea_")
    if name in BLOCKED_QUEUE_TOOLS:
        denied_task = (_session_terminal or {}).get("requested_task") or "unknown"
        return False, [
            TERMINAL_REPORT_HEADING,
            f"Reviewer queue tool '{tool_name}' is blocked by the current "
            f"capability denial for task '{denied_task}' (fail closed).",
            "Resolve or route an allowed author task to clear stale denial "
            "state, or relaunch a reviewer MCP namespace for reviewer work.",
        ]
    return True, []


def validate_eligibility_wording(text: str) -> tuple[bool, list[str]]:
    """Reject session-based eligibility reasoning (#197)."""
    lower = (text or "").lower()
    violations = []
    if "not authored by this session" in lower:
        violations.append(
            "eligibility must use authenticated account identity, not "
            "'this session' wording"
        )
    if re.search(r"not (?:self-)?authored by (?:the )?session", lower):
        violations.append("session-based eligibility reasoning is invalid")
    return (len(violations) == 0), violations


def assess_capability_stop_report(
    report_text: str,
    *,
    trust_gate_status: str | None = None,
    capability_denied: bool = True,
) -> dict:
    """Validate final report purity after reviewer capability denial."""
    text = report_text or ""
    lower = text.lower()
    violations = []

    if capability_denied and TERMINAL_REPORT_HEADING.lower() not in lower:
        violations.append("missing required terminal report heading")

    forbidden_patterns = [
        ("pr selection", re.compile(
            r"selected pr|pr #\d+ (?:to review|selected)|eligible pr|"
            r"next pr to review", re.I)),
        ("sibling repo inventory", re.compile(
            r"sibling repo|other repo|mcp-control-plane|gitea-tools and", re.I)),
        ("author fallback", re.compile(
            r"rebase conflicted|author-side fallback|have me rebase|"
            r"implement the fix|push a branch|open a pr for", re.I)),
        ("invalid session eligibility", re.compile(
            r"not authored by this session", re.I)),
    ]
    for label, pattern in forbidden_patterns:
        if pattern.search(text):
            violations.append(f"forbidden after hard stop: {label}")

    empty_queue_patterns = re.compile(
        r"\b0 open pr|\bno open pr|\bno eligible pr|\bempty (?:review )?queue|"
        r"inventory empty",
        re.I,
    )
    parsed_status = None
    for line in text.splitlines():
        if "pr_inventory_trust_gate.status:" in line.lower():
            parsed_status = line.split(":", 1)[1].strip()
            break
    effective_status = trust_gate_status or parsed_status
    if empty_queue_patterns.search(text):
        if effective_status != "trusted_empty":
            violations.append(
                "empty-queue claim after capability stop without "
                "pr_inventory_trust_gate.status == trusted_empty"
            )

    ok, elig_violations = validate_eligibility_wording(text)
    violations.extend(elig_violations)

    if violations:
        return {
            "pure": False,
            "downgraded": True,
            "violations": violations,
            "reasons": violations,
        }
    return {
        "pure": True,
        "downgraded": False,
        "violations": [],
        "reasons": [],
    }


def build_terminal_report(capability: dict) -> dict:
    """Minimal allowed report fields after hard stop."""
    return {
        "terminal_mode": True,
        "heading": TERMINAL_REPORT_HEADING,
        "authenticated_profile": capability.get("active_profile"),
        "authenticated_identity": capability.get("active_identity"),
        "denied_task": capability.get("requested_task"),
        "required_role_kind": capability.get("required_role_kind"),
        "stop_required": capability.get("stop_required"),
        "required_action": capability.get("exact_safe_next_action"),
        "mutations_performed": False,
        "allowed_sections": [
            "authenticated identity/profile",
            "denied capability result",
            "reason task cannot proceed",
            "required reviewer profile/identity",
            "mutation confirmation (none)",
        ],
        "forbidden_sections": [
            "PR selection",
            "sibling-repo queue recommendations",
            "author-side fallback suggestions",
            "empty-queue claims without trusted_empty",
            "session-based eligibility wording",
        ],
    }