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

try:
    import yaml
except ImportError:  # pragma: no cover
    yaml = None


ROOT = Path(__file__).resolve().parent.parent
DEFAULT_REGISTRY_DIR = ROOT / "registry"
REQUIRED_PACKAGE_FIELDS = [
    "name",
    "version",
    "description",
    "targets",
    "maturity",
    "owner",
    "review_cadence",
    "trust_level",
    "license",
]


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


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 load_yaml(path: Path) -> dict[str, Any]:
    if not path.exists() or yaml is None:
        return {}
    payload = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
    return payload if isinstance(payload, dict) else {}


def read_frontmatter(path: Path) -> dict[str, Any]:
    if not path.exists():
        return {}
    text = path.read_text(encoding="utf-8", errors="replace")
    if not text.startswith("---"):
        return {}
    parts = text.split("---", 2)
    if len(parts) < 3:
        return {}
    if yaml is not None:
        payload = yaml.safe_load(parts[1]) or {}
        return payload if isinstance(payload, dict) else {}
    data: dict[str, Any] = {}
    for line in parts[1].splitlines():
        if ":" not in line:
            continue
        key, value = line.split(":", 1)
        data[key.strip()] = value.strip().strip('"')
    return data


def find_skill_ir(skill_dir: Path, name: str) -> tuple[dict[str, Any], str]:
    candidates = [
        skill_dir / "reports" / "skill-ir.json",
        skill_dir / "skill-ir" / "examples" / f"{name}.json",
        skill_dir / "skill-ir" / "examples" / f"{skill_dir.name}.json",
    ]
    for path in candidates:
        payload = load_json(path)
        if payload:
            return payload, display_path(path, skill_dir)
    return {}, "missing"


def license_id(skill_dir: Path) -> str:
    for name in ("LICENSE", "LICENSE.md", "LICENSE.txt"):
        path = skill_dir / name
        if not path.exists():
            continue
        text = path.read_text(encoding="utf-8", errors="replace")
        if re.search(r"\bMIT License\b", text, re.IGNORECASE):
            return "MIT"
        for line in text.splitlines():
            value = line.strip()
            if value:
                return value[:80]
    return ""


def compatibility_matrix(skill_dir: Path, declared_targets: list[str]) -> dict[str, str]:
    conformance = load_json(skill_dir / "reports" / "conformance_matrix.json")
    matrix = {}
    for target in conformance.get("targets", []):
        name = str(target.get("target", ""))
        if name:
            matrix[name] = str(target.get("status", "unknown"))
    if "agent-skills-compatible" in declared_targets and "agent-skills" in matrix:
        matrix["agent-skills-compatible"] = matrix["agent-skills"]
    for target in declared_targets:
        matrix.setdefault(str(target), "missing")
    return matrix


def package_hash(skill_dir: Path) -> str:
    trust = load_json(skill_dir / "reports" / "security_trust_report.json")
    return str(trust.get("summary", {}).get("package_sha256", ""))


def package_verification(skill_dir: Path) -> dict[str, Any]:
    return load_json(skill_dir / "reports" / "package_verification.json")


def install_simulation(skill_dir: Path) -> dict[str, Any]:
    return load_json(skill_dir / "reports" / "install_simulation.json")


def adoption_drift(skill_dir: Path) -> dict[str, Any]:
    return load_json(skill_dir / "reports" / "adoption_drift_report.json")


def review_waivers(skill_dir: Path) -> dict[str, Any]:
    return load_json(skill_dir / "reports" / "review_waivers.json")


def review_annotations(skill_dir: Path) -> dict[str, Any]:
    return load_json(skill_dir / "reports" / "review_annotations.json")


def compiled_targets(skill_dir: Path) -> dict[str, Any]:
    return load_json(skill_dir / "reports" / "compiled_targets.json")


def package_targets(manifest: dict[str, Any], interface_doc: dict[str, Any], ir: dict[str, Any]) -> list[str]:
    candidates = (
        manifest.get("target_platforms")
        or ir.get("targets")
        or interface_doc.get("compatibility", {}).get("adapter_targets")
        or []
    )
    targets = []
    for item in candidates:
        value = str(item).strip()
        if value and value not in targets:
            targets.append(value)
    return targets


def build_package_metadata(skill_dir: Path, generated_at: str) -> dict[str, Any]:
    frontmatter = read_frontmatter(skill_dir / "SKILL.md")
    manifest = load_json(skill_dir / "manifest.json")
    interface_doc = load_yaml(skill_dir / "agents" / "interface.yaml")
    compatibility = interface_doc.get("compatibility", {})
    trust = compatibility.get("trust", {})
    name = str(frontmatter.get("name") or manifest.get("name") or skill_dir.name)
    ir, ir_source = find_skill_ir(skill_dir, name)
    verification = package_verification(skill_dir)
    install = install_simulation(skill_dir)
    adoption = adoption_drift(skill_dir)
    waivers = review_waivers(skill_dir)
    annotations = review_annotations(skill_dir)
    compiled = compiled_targets(skill_dir)
    archive_sha = str(verification.get("summary", {}).get("archive_sha256", ""))
    checksums = {"package_sha256": package_hash(skill_dir)}
    if archive_sha:
        checksums["archive_sha256"] = archive_sha
    description = str(
        ir.get("trigger_surface", {}).get("description")
        or frontmatter.get("description")
        or manifest.get("description")
        or ""
    )
    targets = package_targets(manifest, interface_doc, ir)
    package = {
        "schema_version": "2.0",
        "name": name,
        "version": str(manifest.get("version") or frontmatter.get("version") or ""),
        "description": description,
        "targets": targets,
        "maturity": str(manifest.get("maturity_tier") or manifest.get("lifecycle_stage") or "scaffold"),
        "owner": str(manifest.get("owner") or ""),
        "review_cadence": str(manifest.get("review_cadence") or ""),
        "trust_level": str(trust.get("source_tier") or ""),
        "license": license_id(skill_dir),
        "checksums": checksums,
        "compatibility": compatibility_matrix(skill_dir, targets),
        "source": {
            "skill_dir": display_path(skill_dir),
            "skill_ir": ir_source,
            "ir_schema_version": str(ir.get("schema_version") or "missing"),
            "canonical_metadata": "agents/interface.yaml" if (skill_dir / "agents" / "interface.yaml").exists() else "missing",
        },
        "artifacts": {
            "overview_html": "reports/skill-overview.html",
            "review_studio_html": "reports/review-studio.html",
            "trust_report": "reports/security_trust_report.md",
            "conformance_matrix": "reports/conformance_matrix.md",
            "compiled_targets": "reports/compiled_targets.md" if compiled else "",
            "package_verification": "reports/package_verification.md" if verification else "",
            "install_simulation": "reports/install_simulation.md" if install else "",
            "adoption_drift": "reports/adoption_drift_report.md" if adoption else "",
            "review_waivers": "reports/review_waivers.md" if waivers else "",
            "review_annotations": "reports/review_annotations.md" if annotations else "",
            "package_metadata": f"registry/packages/{name}.json",
        },
        "distribution": {
            "archive_verified": bool(verification.get("ok")) if verification else False,
            "archive_sha256": archive_sha,
            "package_verification": "reports/package_verification.json" if verification else "",
            "install_simulated": bool(install.get("ok")) if install else False,
            "install_simulation": "reports/install_simulation.json" if install else "",
        },
        "generated_at": generated_at,
    }
    return package


def audit_package(package: dict[str, Any], skill_dir: Path) -> tuple[list[str], list[str]]:
    failures = []
    warnings = []
    for field in REQUIRED_PACKAGE_FIELDS:
        if not package.get(field):
            failures.append(f"Missing package metadata field: {field}")
    if not package.get("checksums", {}).get("package_sha256"):
        failures.append("Missing package checksum: checksums.package_sha256")
    if package.get("source", {}).get("ir_schema_version") != "2.0.0":
        failures.append("Missing valid Skill IR source for registry package")
    compatibility = package.get("compatibility", {})
    for target in package.get("targets", []):
        if compatibility.get(target) not in {"pass", "warn"}:
            failures.append(f"Compatibility is not passing for target: {target}")
    for rel in (
        "reports/skill-overview.html",
        "reports/review-studio.html",
        "reports/compiled_targets.md",
        "reports/security_trust_report.md",
        "reports/review_waivers.md",
        "reports/review_annotations.md",
    ):
        if not (skill_dir / rel).exists():
            warnings.append(f"Recommended registry evidence is missing: {rel}")
    verification = package.get("distribution", {})
    if verification.get("package_verification") and not verification.get("archive_verified"):
        failures.append("Package verification report exists but archive verification did not pass")
    if verification.get("install_simulation") and not verification.get("install_simulated"):
        failures.append("Install simulation report exists but install simulation did not pass")
    return failures, warnings


def build_index(package: dict[str, Any], generated_at: str) -> dict[str, Any]:
    return {
        "schema_version": "2.0",
        "generated_at": generated_at,
        "package_count": 1,
        "packages": [
            {
                "name": package["name"],
                "version": package["version"],
                "maturity": package["maturity"],
                "owner": package["owner"],
                "targets": package["targets"],
                "package_metadata": package["artifacts"]["package_metadata"],
                "package_sha256": package["checksums"]["package_sha256"],
            }
        ],
    }


def render_markdown(report: dict[str, Any]) -> str:
    package = report["package"]
    lines = [
        "# Registry Audit",
        "",
        f"- OK: `{report['ok']}`",
        f"- Package: `{package['name']}`",
        f"- Version: `{package['version']}`",
        f"- Maturity: `{package['maturity']}`",
        f"- Owner: `{package['owner']}`",
        f"- License: `{package['license']}`",
        f"- Package SHA256: `{package['checksums']['package_sha256']}`",
        f"- Archive SHA256: `{package['checksums'].get('archive_sha256', 'n/a')}`",
        f"- Install simulated: `{package.get('distribution', {}).get('install_simulated', False)}`",
        "",
        "## Compatibility",
        "",
        "| Target | Status |",
        "| --- | --- |",
    ]
    for target, status in package["compatibility"].items():
        lines.append(f"| `{target}` | `{status}` |")
    lines.extend(["", "## Failures", ""])
    lines.extend([f"- {item}" for item in report["failures"]] or ["- None"])
    lines.extend(["", "## Warnings", ""])
    lines.extend([f"- {item}" for item in report["warnings"]] or ["- None"])
    lines.extend(["", "## Artifacts", ""])
    for key, value in report["artifacts"].items():
        lines.append(f"- {key}: `{value}`")
    return "\n".join(lines) + "\n"


def run_registry_audit(
    skill_dir: Path,
    registry_dir: Path,
    output_json: Path,
    output_md: Path,
    generated_at: str,
) -> dict[str, Any]:
    skill_dir = skill_dir.resolve()
    registry_dir = registry_dir.resolve()
    package = build_package_metadata(skill_dir, generated_at)
    failures, warnings = audit_package(package, skill_dir)
    index = build_index(package, generated_at)

    package_dir = registry_dir / "packages"
    package_path = package_dir / f"{package['name']}.json"
    index_path = registry_dir / "index.json"
    package_dir.mkdir(parents=True, exist_ok=True)
    output_json.parent.mkdir(parents=True, exist_ok=True)
    output_md.parent.mkdir(parents=True, exist_ok=True)
    package_path.write_text(json.dumps(package, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    index_path.write_text(json.dumps(index, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")

    report = {
        "ok": not failures,
        "schema_version": "2.0",
        "skill_dir": display_path(skill_dir),
        "registry_dir": display_path(registry_dir),
        "package": package,
        "index": index,
        "failures": failures,
        "warnings": warnings,
        "artifacts": {
            "index": display_path(index_path),
            "package": display_path(package_path),
            "json": display_path(output_json),
            "markdown": display_path(output_md),
        },
    }
    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")
    return report


def main() -> None:
    parser = argparse.ArgumentParser(description="Build and audit Skill OS registry package metadata.")
    parser.add_argument("skill_dir", nargs="?", default=".")
    parser.add_argument("--registry-dir", default=str(DEFAULT_REGISTRY_DIR))
    parser.add_argument("--output-json", default="reports/registry_audit.json")
    parser.add_argument("--output-md", default="reports/registry_audit.md")
    parser.add_argument("--generated-at", default=str(date.today()))
    args = parser.parse_args()

    payload = run_registry_audit(
        Path(args.skill_dir),
        Path(args.registry_dir),
        Path(args.output_json),
        Path(args.output_md),
        args.generated_at,
    )
    print(json.dumps(payload, ensure_ascii=False, indent=2))
    raise SystemExit(0 if payload["ok"] else 2)


if __name__ == "__main__":
    main()
