import html
import os
from pathlib import Path
from typing import Any

from review_studio_action_evidence import render_action_evidence_steps, world_class_action_steps
from review_studio_gates import status_label


SCRIPT_INTERFACE = "internal-module"
SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to keep Review Studio action guidance and source refs out of HTML rendering."


def link_from(output_html: Path, target: Path) -> str:
    return os.path.relpath(target.resolve(), output_html.parent.resolve())


def find_line(path: Path, patterns: list[str] | None = None) -> int | None:
    if not path.exists():
        return None
    if not patterns:
        return 1
    try:
        lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
    except OSError:
        return 1
    for pattern in patterns:
        for index, line in enumerate(lines, start=1):
            if pattern in line:
                return index
    return 1


def source_refs(
    skill_dir: Path,
    output_html: Path,
    specs: list[dict[str, Any]],
) -> list[dict[str, Any]]:
    refs: list[dict[str, Any]] = []
    for spec in specs:
        rel_path = str(spec.get("path", "")).strip()
        if not rel_path:
            continue
        path = skill_dir / rel_path
        exists = path.exists()
        line = find_line(path, spec.get("patterns", []))
        refs.append(
            {
                "path": rel_path,
                "label": str(spec.get("label", rel_path)),
                "kind": str(spec.get("kind", "source")),
                "line": line,
                "exists": exists,
                "link": link_from(output_html, path) if exists else "",
            }
        )
    return refs


ACTION_GUIDANCE: dict[str, dict[str, Any]] = {
    "intent-canvas": {
        "summary": "收紧真实任务、输入、输出、排除项和成功标准。",
        "why": "低 intent confidence 会让后续 Skill IR、输出评测和 Review Studio 结论建立在模糊意图上。",
        "source_fix": "reports/intent-dialogue.md + reports/intent-confidence.md",
        "source_paths": [
            {"path": "reports/intent-dialogue.md", "label": "intent dialogue", "kind": "report", "patterns": ["# Intent"]},
            {"path": "reports/intent-confidence.md", "label": "intent confidence", "kind": "report", "patterns": ["# Intent"]},
        ],
        "verification": "python3 scripts/yao.py intent-confidence .",
    },
    "trigger-lab": {
        "summary": "修正 route scorecard 中的误触发、漏触发或 ambiguous case。",
        "why": "触发错误会让正确 Skill 失活，或让相邻 Skill 被错误调用。",
        "source_fix": "SKILL.md frontmatter description + evals/*/trigger_cases.json",
        "source_paths": [
            {"path": "SKILL.md", "label": "frontmatter description", "kind": "source", "patterns": ["description:"]},
            {"path": "evals/trigger_cases.json", "label": "trigger eval cases", "kind": "eval", "patterns": ["should_trigger"]},
            {"path": "reports/route_scorecard.md", "label": "route scorecard", "kind": "report", "patterns": ["# Route"]},
        ],
        "verification": "python3 scripts/build_confusion_matrix.py",
    },
    "output-lab": {
        "summary": "补足 output eval 覆盖、execution evidence、blind A/B 和 reviewer adjudication。",
        "why": "没有输出质量和人工盲评证据时，Skill 只能证明会触发，不能证明输出真的更好且经得起审查。",
        "source_fix": "evals/output/cases.jsonl + reports/output_quality_scorecard.md + reports/output_review_kit.html + reports/output_review_adjudication.md",
        "source_paths": [
            {"path": "evals/output/cases.jsonl", "label": "output eval cases", "kind": "eval", "patterns": ["case_id"]},
            {"path": "reports/output_quality_scorecard.md", "label": "output scorecard", "kind": "report", "patterns": ["# Output"]},
            {"path": "reports/output_execution_runs.md", "label": "output execution runs", "kind": "report", "patterns": ["# Output Execution"]},
            {"path": "reports/output_blind_review_pack.md", "label": "blind A/B review pack", "kind": "report", "patterns": ["# Output Blind"]},
            {"path": "reports/output_review_kit.html", "label": "reviewer cockpit", "kind": "report", "patterns": ["Output Review Kit", "Variant A"]},
            {"path": "reports/output_review_adjudication.md", "label": "review adjudication", "kind": "report", "patterns": ["# Output Review"]},
        ],
        "verification": "python3 scripts/adjudicate_output_review.py --write-template && python3 scripts/yao.py output-review",
    },
    "context-budget": {
        "summary": "压缩或拆分高成本 deferred resources，保留最小可路由上下文。",
        "why": "初始加载可以安全，但后续 references、scripts、evals 体量过大时，reviewer 仍需要看到维护和读取成本。",
        "source_fix": "SKILL.md + references/ + scripts/ + evals/",
        "source_paths": [
            {"path": "SKILL.md", "label": "entrypoint", "kind": "source", "patterns": ["# Yao Meta Skill"]},
            {"path": "reports/context_budget.md", "label": "context budget", "kind": "report", "patterns": ["# Context"]},
            {"path": "scripts/resource_boundary_check.py", "label": "resource boundary checker", "kind": "source", "patterns": ["DEFERRED_RESOURCE"]},
            {"path": "references/skill-engineering-method.md", "label": "skill engineering method", "kind": "method", "patterns": ["Design Principle"]},
        ],
        "verification": "python3 scripts/render_context_reports.py",
    },
    "runtime-matrix": {
        "summary": "修复目标端结构、metadata、相对路径、fallback 或 adapter target 声明。",
        "why": "runtime conformance 失败意味着包可能被目标客户端错误加载或静默降级。",
        "source_fix": "agents/interface.yaml + reports/conformance_matrix.md",
        "source_paths": [
            {"path": "agents/interface.yaml", "label": "portable interface", "kind": "source", "patterns": ["adapter_targets"]},
            {"path": "reports/conformance_matrix.md", "label": "conformance matrix", "kind": "report", "patterns": ["# Runtime"]},
        ],
        "verification": "python3 scripts/run_conformance_suite.py .",
    },
    "trust-report": {
        "summary": "处理脚本 help surface、依赖 pin、network policy、secret 和权限声明。",
        "why": "团队分发时，脚本和依赖是主要供应链风险面，warning 必须有明确处置。",
        "source_fix": "reports/security_trust_report.md + security/*.md + scripts/",
        "source_paths": [
            {"path": "reports/security_trust_report.md", "label": "trust report", "kind": "report", "patterns": ["# Security"]},
            {"path": "security/script_policy.md", "label": "script policy", "kind": "policy", "patterns": ["# Script"]},
            {"path": "security/network_policy.md", "label": "network policy", "kind": "policy", "patterns": ["# Network"]},
        ],
        "verification": "python3 scripts/trust_check.py .",
    },
    "python-compat": {
        "summary": "修复 Python 3.11 语法兼容问题，尤其是 f-string 表达式内的反斜杠转义。",
        "why": "目标运行环境可能仍停留在 Python 3.11，语法漂移会让 CLI、报告生成和 CI 在发布后直接失败。",
        "source_fix": "reports/python_compatibility.md + scripts/*.py + tests/*.py",
        "source_paths": [
            {"path": "reports/python_compatibility.md", "label": "Python compatibility", "kind": "report", "patterns": ["# Python"]},
            {"path": "scripts/python_compat_check.py", "label": "compatibility checker", "kind": "source", "patterns": ["SCRIPT_INTERFACE"]},
            {"path": ".github/workflows/test.yml", "label": "CI test workflow", "kind": "ci", "patterns": ["python"]},
        ],
        "verification": "python3 scripts/yao.py python-compat .",
    },
    "architecture-maintainability": {
        "summary": "处理大文件和 CLI command surface 的维护性热点，优先拆分稳定职责边界。",
        "why": "Meta Skill 的门禁、报告和 CLI 会持续增长；如果不把架构债纳入审查，后续能力会越来越难验证和迁移。",
        "source_fix": "reports/architecture_maintainability.md + scripts/yao.py + scripts/render_review_studio.py",
        "source_paths": [
            {"path": "reports/architecture_maintainability.md", "label": "architecture maintainability", "kind": "report", "patterns": ["# Architecture"]},
            {"path": "scripts/yao.py", "label": "Yao CLI orchestrator", "kind": "source", "patterns": ["def command_"]},
            {"path": "scripts/render_review_studio.py", "label": "Review Studio renderer", "kind": "source", "patterns": ["def render_html"]},
            {"path": "scripts/review_studio_actions.py", "label": "Review Studio actions", "kind": "source", "patterns": ["ACTION_GUIDANCE"]},
            {"path": "scripts/render_review_viewer.py", "label": "review viewer renderer", "kind": "source", "patterns": ["def "]},
        ],
        "verification": "python3 scripts/yao.py architecture-audit .",
    },
    "permission-gates": {
        "summary": "补齐高权限能力的 reviewer、scope、reason、expires_at 和目标端 enforcement 说明。",
        "why": "权限契约只有在批准人、有效期和目标端处置方式明确时，才能支撑 governed release。",
        "source_fix": "security/permission_policy.json + security/permission_policy.md",
        "source_paths": [
            {"path": "security/permission_policy.json", "label": "permission approvals", "kind": "policy", "patterns": ["approved"]},
            {"path": "security/permission_policy.md", "label": "permission method", "kind": "policy", "patterns": ["# Permission"]},
        ],
        "verification": "python3 scripts/trust_check.py .",
    },
    "permission-runtime": {
        "summary": "生成并修复目标包的 runtime permission probe 报告。",
        "why": "目标端即使只能提供 metadata fallback，也必须明确 native enforcement 缺口、表示位置和 operator note。",
        "source_fix": "dist/targets/*/adapter.json + reports/runtime_permission_probes.md",
        "source_paths": [
            {"path": "reports/runtime_permission_probes.md", "label": "runtime permission probes", "kind": "report", "patterns": ["# Runtime"]},
            {"path": "dist/targets/openai/adapter.json", "label": "OpenAI adapter", "kind": "package", "patterns": ["target_permission_contract"]},
            {"path": "dist/targets/claude/adapter.json", "label": "Claude adapter", "kind": "package", "patterns": ["target_permission_contract"]},
            {"path": "dist/targets/generic/adapter.json", "label": "generic adapter", "kind": "package", "patterns": ["target_permission_contract"]},
        ],
        "verification": "python3 scripts/probe_runtime_permissions.py . --package-dir dist",
    },
    "skill-atlas": {
        "summary": "处理 portfolio 里的路由冲突、owner 缺口、stale skill 和重复能力。",
        "why": "单个 Skill 质量很高仍可能在团队 skill library 中被相邻 Skill 冲突削弱。",
        "source_fix": "reports/skill_atlas.html + skill_atlas/catalog.json",
        "source_paths": [
            {"path": "skill_atlas/catalog.json", "label": "skill atlas catalog", "kind": "atlas", "patterns": ["summary"]},
            {"path": "skill_atlas/policy.json", "label": "atlas scope policy", "kind": "policy", "patterns": ["scope"]},
            {"path": "reports/skill_atlas.html", "label": "skill atlas report", "kind": "report", "patterns": ["Skill Atlas"]},
        ],
        "verification": "python3 scripts/build_skill_atlas.py --workspace-root .",
    },
    "operations-loop": {
        "summary": "记录 metadata-only 使用事件，或明确当前 release 缺少真实使用信号。",
        "why": "没有运营回路时，reviewer 无法判断采用率、误触发、坏输出和 review overdue 的真实影响。",
        "source_fix": "reports/adoption_drift_report.md",
        "source_paths": [
            {"path": "reports/adoption_drift_report.md", "label": "adoption drift report", "kind": "report", "patterns": ["# Adoption"]},
            {"path": "references/telemetry-drift-method.md", "label": "telemetry method", "kind": "method", "patterns": ["# Telemetry"]},
        ],
        "verification": "python3 scripts/render_adoption_drift_report.py . --record-event skill_activation --activation-type explicit --outcome accepted",
    },
    "review-waivers": {
        "summary": "对保留的 warning 写入 reviewer、理由、范围和到期时间，或修掉 warning。",
        "why": "warning 可以被接受，但必须可审计、会过期，并且不能掩盖 blocker。",
        "source_fix": "reports/review_waivers.md",
        "source_paths": [
            {"path": "reports/review_waivers.md", "label": "waiver ledger", "kind": "report", "patterns": ["# Review"]},
            {"path": "references/review-waiver-method.md", "label": "waiver method", "kind": "method", "patterns": ["# Review"]},
        ],
        "verification": "python3 scripts/render_review_waivers.py .",
    },
    "world-class-evidence": {
        "summary": "补齐 provider、真人盲评、原生权限执行和真实客户端遥测证据，或明确本次发布不声明 world-class 完成。",
        "why": "世界级结论必须来自已接受的外部/人工证据；计划、metadata fallback、待评审和本地命令都不能替代完成证据。",
        "source_fix": "reports/world_class_operator_runbook.html + reports/world_class_evidence_ledger.md + reports/world_class_evidence_intake.md + reports/world_class_submission_review.md",
        "source_paths": [
            {"path": "reports/world_class_evidence_ledger.md", "label": "world-class evidence ledger", "kind": "report", "patterns": ["# World-Class Evidence Ledger"]},
            {"path": "reports/world_class_evidence_plan.md", "label": "world-class evidence plan", "kind": "report", "patterns": ["# World-Class Evidence Plan"]},
            {"path": "reports/world_class_evidence_intake.md", "label": "world-class evidence intake", "kind": "report", "patterns": ["# World-Class Evidence Intake"]},
            {"path": "reports/world_class_submission_review.md", "label": "world-class submission review", "kind": "report", "patterns": ["# World-Class Submission Review"]},
            {"path": "reports/world_class_claim_guard.md", "label": "world-class claim guard", "kind": "report", "patterns": ["# World-Class Claim Guard"]},
            {"path": "evidence/world_class/intake.schema.json", "label": "evidence intake schema", "kind": "schema", "patterns": ["Yao World-Class Evidence Intake"]},
            {"path": "evidence/world_class/templates/provider-holdout.intake.json", "label": "provider intake template", "kind": "template", "patterns": ["provider-holdout"]},
            {"path": "evidence/world_class/templates/human-adjudication.intake.json", "label": "human intake template", "kind": "template", "patterns": ["human-adjudication"]},
            {"path": "evidence/world_class/templates/native-permission-enforcement.intake.json", "label": "permission intake template", "kind": "template", "patterns": ["native-permission-enforcement"]},
            {"path": "evidence/world_class/templates/native-client-telemetry.intake.json", "label": "telemetry intake template", "kind": "template", "patterns": ["native-client-telemetry"]},
            {"path": "reports/skill_os2_audit.md", "label": "Skill OS 2.0 audit", "kind": "report", "patterns": ["# Skill OS"]},
            {"path": "reports/output_review_decisions.json", "label": "human review decisions", "kind": "report", "patterns": ["winner_variant"]},
            {"path": "reports/runtime_permission_probes.md", "label": "runtime permission probes", "kind": "report", "patterns": ["# Runtime"]},
            {"path": "reports/adoption_drift_report.md", "label": "adoption drift", "kind": "report", "patterns": ["# Adoption"]},
        ],
        "verification": (
            "python3 scripts/yao.py world-class-runbook . --submissions-dir evidence/world_class/submissions "
            "&& python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions "
            "&& python3 scripts/yao.py review-studio ."
        ),
    },
    "registry-audit": {
        "summary": "补齐 registry package metadata、checksum、license、owner、review cadence 和 install evidence。",
        "why": "分发元数据不完整时，团队无法安全安装、升级或追溯包体来源。",
        "source_fix": "registry/ + reports/registry_audit.md",
        "source_paths": [
            {"path": "registry/packages/yao-meta-skill.json", "label": "registry package", "kind": "registry", "patterns": ["version"]},
            {"path": "reports/registry_audit.md", "label": "registry audit", "kind": "report", "patterns": ["# Registry"]},
            {"path": "reports/install_simulation.md", "label": "install simulation", "kind": "report", "patterns": ["# Install"]},
        ],
        "verification": "python3 scripts/registry_audit.py .",
    },
    "release-notes": {
        "summary": "确认 promotion、upgrade diff、breaking changes、migration guide 和 known limitations。",
        "why": "发布说明不完整会让使用者无法判断升级风险和迁移动作。",
        "source_fix": "reports/upgrade_check.md + docs/migration-v2.md",
        "source_paths": [
            {"path": "reports/upgrade_check.md", "label": "upgrade check", "kind": "report", "patterns": ["# Upgrade"]},
            {"path": "docs/migration-v2.md", "label": "migration guide", "kind": "docs", "patterns": ["# Migration"]},
            {"path": "reports/promotion_decisions.md", "label": "promotion decisions", "kind": "report", "patterns": ["# Promotion"]},
        ],
        "verification": "python3 scripts/upgrade_check.py . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json",
    },
}


def build_review_actions(
    gates: list[dict[str, str]],
    skill_dir: Path,
    output_html: Path,
    data: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
    actions: list[dict[str, Any]] = []
    for gate_item in gates:
        if gate_item["status"] == "pass":
            continue
        guidance = ACTION_GUIDANCE.get(
            gate_item["key"],
            {
                "summary": "打开证据报告，修复当前 gate 暴露的问题。",
                "why": "该 gate 未通过，release reviewer 需要明确处置动作。",
                "source_fix": gate_item.get("evidence", ""),
                "source_paths": [],
                "verification": "python3 scripts/render_review_studio.py .",
            },
        )
        refs = source_refs(skill_dir, output_html, guidance.get("source_paths", []))
        actions.append(
            {
                "gate_key": gate_item["key"],
                "label": gate_item["label"],
                "status": gate_item["status"],
                "priority": "blocker" if gate_item["status"] == "block" else "warning",
                "summary": guidance["summary"],
                "why": guidance["why"],
                "source_fix": guidance["source_fix"],
                "source_refs": refs,
                "evidence": gate_item.get("evidence", ""),
                "evidence_link": gate_item.get("link", ""),
                "verification_command": guidance["verification"],
                "evidence_steps": world_class_action_steps(data or {})
                if gate_item["key"] == "world-class-evidence"
                else [],
            }
        )
    return actions


def render_action_source_refs(refs: list[dict[str, Any]]) -> str:
    if not refs:
        return "<p class='muted'>暂无结构化 source refs；请先打开证据报告。</p>"
    items = []
    for ref in refs:
        line_suffix = f":{ref['line']}" if ref.get("line") else ""
        label = f"{ref['path']}{line_suffix}"
        if ref.get("exists") and ref.get("link"):
            path_html = f"<a href='{html.escape(ref['link'])}'>{html.escape(label)}</a>"
        else:
            path_html = f"<span>{html.escape(label)} · missing</span>"
        items.append(
            "<li>"
            f"{path_html}"
            f"<small>{html.escape(ref.get('label', 'source'))} · {html.escape(ref.get('kind', 'source'))}</small>"
            "</li>"
        )
    return "<ul class='source-ref-list'>" + "".join(items) + "</ul>"


def render_review_actions(actions: list[dict[str, Any]]) -> str:
    if not actions:
        return "<p class='muted'>当前没有 blocker 或 warning。保持现有证据链即可。</p>"
    cards = []
    for item in actions:
        link_html = f"<a href='{html.escape(item['evidence_link'])}'>打开证据</a>" if item.get("evidence_link") else ""
        source_refs_html = render_action_source_refs(item.get("source_refs", []))
        evidence_steps_html = render_action_evidence_steps(item.get("evidence_steps", []))
        card_class = "action-card " + html.escape(item["status"])
        if item.get("evidence_steps"):
            card_class += " with-evidence"
        cards.append(
            "<article class='" + card_class + "'>"
            f"<div><span>{html.escape(status_label(item['status']))}</span><h3>{html.escape(item['label'])}</h3></div>"
            f"<p>{html.escape(item['summary'])}</p>"
            f"<small>{html.escape(item['why'])}</small>"
            f"<dl><dt>修复位置</dt><dd>{html.escape(item['source_fix'])}</dd>"
            f"<dt>验证命令</dt><dd><code>{html.escape(item['verification_command'])}</code></dd></dl>"
            f"{evidence_steps_html}"
            f"{source_refs_html}"
            f"<footer>{html.escape(item['evidence'])} {link_html}</footer>"
            "</article>"
        )
    return "".join(cards)
