#!/usr/bin/env python3
import argparse
import json
from pathlib import Path

import yaml


ROOT = Path(__file__).resolve().parent.parent


def load_json(path: Path) -> dict:
    return json.loads(path.read_text(encoding="utf-8"))


def load_yaml(path: Path) -> dict:
    return yaml.safe_load(path.read_text(encoding="utf-8")) or {}


def find_ir(root: Path) -> tuple[dict, str]:
    candidates = [
        root / "reports" / "skill-ir.json",
        root / "skill-ir" / "examples" / f"{root.name}.json",
    ]
    examples_dir = root / "skill-ir" / "examples"
    if examples_dir.exists():
        for path in sorted(examples_dir.glob("*.json")):
            if path not in candidates:
                candidates.append(path)
    for path in candidates:
        if path.exists():
            return load_json(path), str(path.relative_to(root))
    return {}, "missing"


def band_for(score: int) -> str:
    if score >= 97:
        return "world-class"
    if score >= 93:
        return "excellent"
    if score >= 85:
        return "strong"
    return "developing"


def build_report(root: Path) -> dict:
    interface_doc = load_yaml(root / "agents" / "interface.yaml")
    compatibility = interface_doc.get("compatibility", {})
    activation = compatibility.get("activation", {})
    execution = compatibility.get("execution", {})
    trust = compatibility.get("trust", {})
    degradation = compatibility.get("degradation", {})
    targets = compatibility.get("adapter_targets", [])
    expectations = load_json(root / "evals" / "packaging_expectations.json")
    contracts_doc = (root / "references" / "packaging-contracts.md").exists()
    matrix_doc = (root / "references" / "platform-capability-matrix.md").exists()
    snapshot_files = list((root / "tests" / "snapshots").glob("*_adapter.json"))
    ir, ir_source = find_ir(root)
    has_neutral_metadata = (root / "SKILL.md").exists() and (root / "agents" / "interface.yaml").exists()
    has_skill_ir = ir.get("schema_version") == "2.0.0" and bool(ir.get("job_to_be_done"))

    breakdown = {
        "platform_neutral_semantic_source": 15 if has_neutral_metadata and has_skill_ir else 8 if has_neutral_metadata else 0,
        "adapter_target_coverage": 15 if len(targets) >= 3 else 10 if len(targets) >= 2 else 5 if len(targets) >= 1 else 0,
        "activation_metadata": 10
        if activation.get("mode") and activation.get("paths") is not None
        else 5
        if activation.get("mode")
        else 0,
        "execution_metadata": 10
        if execution.get("context") and execution.get("shell")
        else 0,
        "trust_boundary_metadata": 15
        if trust.get("source_tier") and trust.get("remote_inline_execution") and trust.get("remote_metadata_policy")
        else 0,
        "degradation_strategy_coverage": 10 if all(degradation.get(target) for target in targets) else 0,
        "contract_and_expectation_coverage": 15
        if contracts_doc and expectations.get("required_targets") == targets
        else 5
        if contracts_doc
        else 0,
    }

    snapshot_score = 0
    if matrix_doc:
        snapshot_score += 5
    if len(snapshot_files) >= len(targets):
        snapshot_score += 5
    breakdown["consumer_validation_and_snapshots"] = snapshot_score

    score = sum(breakdown.values())
    return {
        "ok": score >= 95,
        "score": score,
        "band": band_for(score),
        "summary": {
            "adapter_target_count": len(targets),
            "activation_mode": activation.get("mode"),
            "execution_context": execution.get("context"),
            "shell": execution.get("shell"),
            "trust_level": trust.get("source_tier"),
            "remote_inline_execution": trust.get("remote_inline_execution"),
            "degradation_coverage": sum(1 for target in targets if degradation.get(target)),
            "snapshot_count": len(snapshot_files),
            "ir_source": ir_source,
            "ir_schema_version": ir.get("schema_version", "missing"),
        },
        "breakdown": breakdown,
    }


def render_markdown(report: dict) -> str:
    lines = [
        "# Portability Score",
        "",
        f"- score: `{report['score']}/100`",
        f"- band: `{report['band']}`",
        f"- adapter targets: `{report['summary']['adapter_target_count']}`",
        f"- activation mode: `{report['summary']['activation_mode']}`",
        f"- execution context: `{report['summary']['execution_context']}`",
        f"- shell: `{report['summary']['shell']}`",
        f"- trust level: `{report['summary']['trust_level']}`",
        f"- remote inline execution: `{report['summary']['remote_inline_execution']}`",
        f"- Skill IR source: `{report['summary']['ir_source']}`",
        f"- Skill IR schema: `{report['summary']['ir_schema_version']}`",
        "",
        "| Component | Score |",
        "| --- | ---: |",
    ]
    for name, value in report["breakdown"].items():
        lines.append(f"| `{name}` | {value} |")
    return "\n".join(lines) + "\n"


def main() -> None:
    parser = argparse.ArgumentParser(description="Render a portability score from neutral metadata, contracts, and snapshots.")
    parser.add_argument("skill_dir", nargs="?", default=str(ROOT), help="Skill directory to inspect. Defaults to this repo root.")
    parser.add_argument("--output-json", default="reports/portability_score.json")
    parser.add_argument("--output-md", default="reports/portability_score.md")
    args = parser.parse_args()

    skill_root = Path(args.skill_dir).resolve()
    report = build_report(skill_root)
    output_json = Path(args.output_json)
    output_md = Path(args.output_md)
    if not output_json.is_absolute():
        output_json = skill_root / output_json
    if not output_md.is_absolute():
        output_md = skill_root / 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))
    if not report["ok"]:
        raise SystemExit(2)


if __name__ == "__main__":
    main()
