#!/usr/bin/env python3
import argparse
import json
import re
from datetime import date
from pathlib import Path
from typing import Any

from render_world_class_evidence_ledger import build_ledger


ROOT = Path(__file__).resolve().parent.parent
SCRIPT_INTERFACE = "cli"
SCRIPT_INTERFACE_REASON = "Scans public claim surfaces so world-class completion is not claimed before accepted evidence exists."

FORBIDDEN_WHEN_PENDING = [
    {
        "key": "ready-to-claim-true",
        "pattern": re.compile(r"ready[\s_-]*to[\s_-]*claim[\s_-]*world[\s_-]*class\s*[:=]\s*`?true`?", re.IGNORECASE),
        "reason": "ready_to_claim_world_class cannot be true while ledger evidence is pending.",
    },
    {
        "key": "world-class-ready-true",
        "pattern": re.compile(r"(public\s+)?world[\s_-]*class[\s_-]*ready\s*[:=]\s*`?true`?", re.IGNORECASE),
        "reason": "world-class readiness cannot be claimed before accepted external and human evidence exists.",
    },
    {
        "key": "json-world-class-ready-true",
        "pattern": re.compile(r'"(?:ready_to_claim_world_class|world_class_ready|public_world_class_ready)"\s*:\s*true', re.IGNORECASE),
        "reason": "machine-readable claim fields must remain false until the ledger is ready.",
    },
    {
        "key": "completion-phrase",
        "pattern": re.compile(r"(public\s+)?world[\s_-]*class\s+(?:complete|completed|achieved|certified)", re.IGNORECASE),
        "reason": "completion language is blocked until the world-class ledger is accepted.",
    },
    {
        "key": "zh-completion-phrase",
        "pattern": re.compile(r"世界级(?:已完成|完成|已达成|达成|准备就绪|可声明|认证通过)"),
        "reason": "中文完成态表述必须等到 ledger ready 后才能出现。",
    },
]


def load_json(path: Path) -> dict[str, Any]:
    if not path.exists():
        return {}
    try:
        payload = json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        return {}
    return payload if isinstance(payload, dict) else {}


def rel_path(path: Path, root: Path) -> str:
    try:
        return str(path.resolve().relative_to(root.resolve()))
    except ValueError:
        return str(path.resolve())


def resolve_claim_surface(raw_path: str, skill_dir: Path) -> Path:
    path = Path(raw_path)
    if not path.is_absolute():
        path = skill_dir / path
    return path.resolve()


def default_claim_surfaces(skill_dir: Path) -> list[Path]:
    paths: list[Path] = []
    for rel in ["README.md", "SKILL.md"]:
        path = skill_dir / rel
        if path.exists():
            paths.append(path)
    for directory, patterns in [
        (skill_dir / "docs", ["*.md"]),
        (skill_dir / "reports", ["*.md", "*.html"]),
    ]:
        if not directory.exists():
            continue
        for pattern in patterns:
            paths.extend(
                path
                for path in directory.rglob(pattern)
                if path.is_file()
                and "release_snapshots" not in path.parts
                and path.name not in {"world_class_claim_guard.md", "world_class_claim_guard.html"}
            )
    return sorted(set(paths), key=lambda item: rel_path(item, skill_dir))


def line_matches(path: Path, ledger_ready: bool) -> list[dict[str, Any]]:
    if ledger_ready:
        return []
    violations: list[dict[str, Any]] = []
    try:
        lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
    except OSError:
        return violations
    for lineno, line in enumerate(lines, start=1):
        for rule in FORBIDDEN_WHEN_PENDING:
            if rule["pattern"].search(line):
                violations.append(
                    {
                        "line": lineno,
                        "rule": rule["key"],
                        "reason": rule["reason"],
                        "excerpt": line.strip()[:240],
                    }
                )
    return violations


def escape_markdown_table_cell(value: Any) -> str:
    return str(value).replace("|", "\\|")


def build_guard(skill_dir: Path, generated_at: str, claim_surfaces: list[Path] | None = None) -> dict[str, Any]:
    ledger = build_ledger(skill_dir, generated_at)
    ledger_ready = ledger.get("summary", {}).get("ready_to_claim_world_class") is True
    surfaces = claim_surfaces or default_claim_surfaces(skill_dir)
    scanned = []
    violations = []
    for path in surfaces:
        matches = line_matches(path, ledger_ready)
        rel = rel_path(path, skill_dir)
        scanned.append({"path": rel, "violation_count": len(matches)})
        for match in matches:
            violations.append({"path": rel, **match})
    ok = len(violations) == 0
    return {
        "schema_version": "1.0",
        "ok": ok,
        "generated_at": generated_at,
        "skill_dir": rel_path(skill_dir, ROOT),
        "summary": {
            "ledger_ready_to_claim_world_class": ledger_ready,
            "ledger_pending_count": ledger.get("summary", {}).get("pending_count", 0),
            "claim_surface_count": len(scanned),
            "violation_count": len(violations),
            "overclaim_guard_active": True,
            "decision": (
                "claim-ready"
                if ledger_ready and ok
                else ("claim-blocked-overclaim" if violations else "claim-guard-pass-evidence-pending")
            ),
        },
        "rules": [
            {"key": item["key"], "reason": item["reason"], "pattern": item["pattern"].pattern}
            for item in FORBIDDEN_WHEN_PENDING
        ],
        "scanned_surfaces": scanned,
        "violations": violations,
        "source_ledger": {
            "json": "reports/world_class_evidence_ledger.json",
            "markdown": "reports/world_class_evidence_ledger.md",
        },
        "artifacts": {
            "json": "reports/world_class_claim_guard.json",
            "markdown": "reports/world_class_claim_guard.md",
        },
    }


def render_markdown(report: dict[str, Any]) -> str:
    summary = report["summary"]
    lines = [
        "# World-Class Claim Guard",
        "",
        f"Generated at: `{report['generated_at']}`",
        "",
        "## Summary",
        "",
        f"- decision: `{summary['decision']}`",
        f"- ledger ready to claim world-class: `{str(summary['ledger_ready_to_claim_world_class']).lower()}`",
        f"- ledger pending evidence: `{summary['ledger_pending_count']}`",
        f"- claim surfaces scanned: `{summary['claim_surface_count']}`",
        f"- violations: `{summary['violation_count']}`",
        f"- overclaim guard active: `{str(summary['overclaim_guard_active']).lower()}`",
        "",
        "This guard scans public claim surfaces for completion language that would contradict the world-class evidence ledger. It allows evidence planning and pending-state language, but blocks completion claims until the ledger is ready.",
        "",
        "## Violations",
        "",
        "| Path | Line | Rule | Excerpt |",
        "| --- | ---: | --- | --- |",
    ]
    if report["violations"]:
        for item in report["violations"]:
            excerpt = str(item["excerpt"]).replace("|", "\\|")
            lines.append(f"| `{item['path']}` | {item['line']} | `{item['rule']}` | {excerpt} |")
    else:
        lines.append("| `none` | 0 | `none` | none |")
    lines.extend(
        [
            "",
            "## Rules",
            "",
            "| Rule | Reason |",
            "| --- | --- |",
        ]
    )
    for rule in report["rules"]:
        reason = escape_markdown_table_cell(rule["reason"])
        lines.append(f"| `{rule['key']}` | {reason} |")
    return "\n".join(lines).rstrip() + "\n"


def main() -> None:
    parser = argparse.ArgumentParser(description="Guard public world-class completion claims against the evidence ledger.")
    parser.add_argument("skill_dir", nargs="?", default=".")
    parser.add_argument("--claim-surface", action="append", default=[])
    parser.add_argument("--output-json", default="reports/world_class_claim_guard.json")
    parser.add_argument("--output-md", default="reports/world_class_claim_guard.md")
    parser.add_argument("--generated-at", default=date.today().isoformat())
    args = parser.parse_args()

    skill_dir = Path(args.skill_dir).resolve()
    claim_surfaces = [resolve_claim_surface(item, skill_dir) for item in args.claim_surface] if args.claim_surface else None
    report = build_guard(skill_dir, args.generated_at, claim_surfaces)
    output_json = Path(args.output_json)
    output_md = Path(args.output_md)
    if not output_json.is_absolute():
        output_json = skill_dir / output_json
    if not output_md.is_absolute():
        output_md = skill_dir / output_md
    output_json.parent.mkdir(parents=True, exist_ok=True)
    output_md.parent.mkdir(parents=True, exist_ok=True)
    output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    output_md.write_text(render_markdown(report), encoding="utf-8")
    print(json.dumps(report, ensure_ascii=False, indent=2))
    raise SystemExit(0 if report["ok"] else 2)


if __name__ == "__main__":
    main()
