"""Final-report audit preview for the web UI (#431)."""

from __future__ import annotations

import re
from typing import Any

from final_report_validator import FINAL_REPORT_TASK_KINDS, assess_final_report_validator

_TASK_KIND_PATTERNS: tuple[tuple[str, re.Pattern[str]], ...] = (
    ("review_pr", re.compile(r"\b(?:review\s+pr|task\s*:\s*review|review-merge-pr)\b", re.I)),
    (
        "reconcile_already_landed",
        re.compile(
            r"\b(?:reconcile\s+already[- ]landed|already[- ]landed\s+pr|"
            r"reconcile-landed-pr)\b",
            re.I,
        ),
    ),
    ("work_issue", re.compile(r"\b(?:work[- ]issue|task\s*:\s*work|selected\s+issue\s*:)\b", re.I)),
    ("issue_filing", re.compile(r"\b(?:issue\s+filing|create\s+issue|task\s*:\s*file)\b", re.I)),
    ("issue_selection", re.compile(r"\b(?:issue\s+selection|select\s+next\s+issue)\b", re.I)),
    ("inventory", re.compile(r"\b(?:issue\s+inventory|queue\s+inventory)\b", re.I)),
)

_SUGGESTED_PROMPTS: dict[str, str] = {
    "review_pr": (
        "Review the next eligible open PR in this project. Merge it only if every "
        "gate passes. Post a final report matching review-merge-pr schema."
    ),
    "work_issue": (
        "Find the next eligible issue in this project, work on it only if all gates "
        "pass, and create a PR when complete."
    ),
    "reconcile_already_landed": (
        "Reconcile the oldest eligible already-landed open PR. Post read-only audit "
        "first; authorize cleanup only when required."
    ),
    "issue_filing": "File a new Gitea issue using the canonical issue-filing workflow.",
    "issue_selection": "Build a complete open-issue inventory and select the next eligible issue.",
    "inventory": "Produce a proof-backed inventory of open PRs or issues without mutating state.",
}


def infer_task_kind(report_text: str, explicit: str | None = None) -> str:
    """Infer workflow task kind from explicit selection or report content."""
    if explicit:
        normalized = explicit.strip().lower().replace(" ", "_")
        if normalized in FINAL_REPORT_TASK_KINDS:
            return normalized
    text = report_text or ""
    for kind, pattern in _TASK_KIND_PATTERNS:
        if pattern.search(text):
            return kind
    if re.search(r"\brole\s*:\s*reviewer\b", text, re.I):
        return "review_pr"
    if re.search(r"\brole\s*:\s*author\b", text, re.I):
        return "work_issue"
    return "review_pr"


def _merge_findings(*result_sets: dict[str, Any]) -> list[dict[str, str]]:
    seen: set[tuple[str, str, str]] = set()
    merged: list[dict[str, str]] = []
    for result in result_sets:
        for finding in result.get("findings") or []:
            key = (
                str(finding.get("rule_id", "")),
                str(finding.get("field", "")),
                str(finding.get("reason", "")),
            )
            if key in seen:
                continue
            seen.add(key)
            merged.append(finding)
    return merged


def _aggregate_result(task_kind: str, findings: list[dict[str, str]], base: dict[str, Any]) -> dict[str, Any]:
    blocked = any(f.get("severity") == "block" for f in findings)
    downgraded = any(f.get("severity") == "downgrade" for f in findings)
    warning = any(f.get("severity") == "warning" for f in findings)
    if blocked:
        grade = "blocked"
    elif downgraded:
        grade = "downgraded"
    elif warning:
        grade = "warning"
    else:
        grade = base.get("grade") or "A"
    safe_next_action = base.get("safe_next_action") or "proceed"
    for finding in findings:
        if finding.get("severity") in {"block", "downgrade"}:
            safe_next_action = finding.get("safe_next_action") or safe_next_action
            break
    return {
        "task_kind": task_kind,
        "inferred_task_kind": task_kind,
        "grade": grade,
        "blocked": blocked or bool(base.get("blocked")),
        "downgraded": downgraded or bool(base.get("downgraded")),
        "findings": findings,
        "reasons": [f"{f.get('rule_id')}: {f.get('reason')}" for f in findings],
        "safe_next_action": safe_next_action,
        "complete": grade == "A" and not findings,
        "suggested_next_prompt": _SUGGESTED_PROMPTS.get(task_kind, ""),
        "suggested_issue_comment": _build_issue_comment(findings, safe_next_action),
    }


def _build_issue_comment(findings: list[dict[str, str]], safe_next_action: str) -> str:
    if not findings:
        return ""
    lines = [
        "## Report audit preview (local validator)",
        "",
        "Automated findings from pasted final report:",
        "",
    ]
    for finding in findings[:12]:
        severity = finding.get("severity", "info")
        rule_id = finding.get("rule_id", "unknown")
        reason = finding.get("reason", "")
        lines.append(f"- **{severity}** `{rule_id}` — {reason}")
    if len(findings) > 12:
        lines.append(f"- …and {len(findings) - 12} more finding(s)")
    lines.extend(["", f"Safe next action: {safe_next_action}", ""])
    return "\n".join(lines)


def audit_report(report_text: str, *, task_kind: str | None = None) -> dict[str, Any]:
    """Run composable final-report validation on pasted text."""
    text = (report_text or "").strip()
    if not text:
        return {
            "task_kind": task_kind or "review_pr",
            "inferred_task_kind": task_kind or "review_pr",
            "grade": "blocked",
            "blocked": True,
            "downgraded": False,
            "findings": [],
            "reasons": ["empty report text"],
            "safe_next_action": "paste a non-empty final report",
            "complete": False,
            "suggested_next_prompt": "",
            "suggested_issue_comment": "",
        }

    resolved_kind = infer_task_kind(text, task_kind)
    base = assess_final_report_validator(text, resolved_kind)
    findings = list(base.get("findings") or [])

    if resolved_kind == "review_pr":
        from review_final_report_schema import assess_review_final_report_schema

        schema = assess_review_final_report_schema(text)
        findings = _merge_findings(base, schema)

    return _aggregate_result(resolved_kind, findings, base)


def audit_to_dict(result: dict[str, Any]) -> dict[str, Any]:
    """JSON-safe export for /api/audit."""
    return {
        "task_kind": result.get("task_kind"),
        "inferred_task_kind": result.get("inferred_task_kind"),
        "grade": result.get("grade"),
        "blocked": result.get("blocked"),
        "downgraded": result.get("downgraded"),
        "complete": result.get("complete"),
        "safe_next_action": result.get("safe_next_action"),
        "findings": result.get("findings") or [],
        "reasons": result.get("reasons") or [],
        "suggested_next_prompt": result.get("suggested_next_prompt") or "",
        "suggested_issue_comment": result.get("suggested_issue_comment") or "",
    }