#!/usr/bin/env python3
"""Drift check: verify each .contract.md with a `prd:` block still matches its source.

Reads every `docs/contracts/**/*.contract.md` that has a `prd:` frontmatter block,
fetches the live issue body and (if specified) the lock-in comment from Gitea,
re-hashes them, and compares to the recorded `body_sha256_16` / `lock_in_sha256_16`.

Exits 0 if all pinned contracts are clean. Exits 1 with a diff report if any contract
has drifted. Skips contracts without a `prd:` block.

Usage:
    python scripts/contract_drift_check.py
    python scripts/contract_drift_check.py --contract docs/contracts/issues/138.contract.md
    python scripts/contract_drift_check.py --json

Requires GITEA_TOKEN in environment (and GITEA_URL/OWNER/REPO if not in env.sh).
"""
from __future__ import annotations

import argparse
import hashlib
import json
import os
import sys
from pathlib import Path
from typing import Any

import httpx
import yaml

PROJECT_ROOT = Path(__file__).parent.parent
CONTRACTS_GLOB = "docs/contracts/**/*.contract.md"


def sha16(s: str) -> str:
    return hashlib.sha256(s.encode("utf-8")).hexdigest()[:16]


def load_frontmatter(text: str) -> dict[str, Any] | None:
    if not text.startswith("---\n"):
        return None
    end = text.find("\n---\n", 4)
    if end == -1:
        return None
    return yaml.safe_load(text[4:end])


def fetch_issue_state(token: str, base_url: str, owner: str, repo: str, n: int, comment_id: int | None) -> dict[str, Any]:
    hdr = {"Authorization": f"token {token}"}
    issue_url = f"{base_url}/api/v1/repos/{owner}/{repo}/issues/{n}"
    issue = httpx.get(issue_url, headers=hdr, timeout=15)
    issue.raise_for_status()
    body = issue.json()["body"]
    out: dict[str, Any] = {"body_sha256_16": sha16(body)}
    if comment_id:
        comment = httpx.get(
            f"{base_url}/api/v1/repos/{owner}/{repo}/issues/comments/{comment_id}",
            headers=hdr, timeout=15,
        )
        comment.raise_for_status()
        out["lock_in_sha256_16"] = sha16(comment.json()["body"])
    return out


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--contract", help="check a single contract file (default: all)")
    ap.add_argument("--json", action="store_true", help="emit machine-readable report")
    args = ap.parse_args()

    token = os.environ.get("GITEA_TOKEN", "")
    base_url = os.environ.get("GITEA_URL", "https://gitea.phasefinal.com")
    owner = os.environ.get("GITEA_OWNER", "vh")
    repo = os.environ.get("GITEA_REPO", "Worldtree")
    if not token:
        print("error: GITEA_TOKEN not set", file=sys.stderr)
        return 2

    if args.contract:
        files = [Path(args.contract).resolve()]
    else:
        files = sorted(PROJECT_ROOT.glob(CONTRACTS_GLOB))

    findings: list[dict[str, Any]] = []
    for path in files:
        text = path.read_text()
        fm = load_frontmatter(text)
        if not fm or "prd" not in fm:
            continue
        prd = fm["prd"]
        n = prd["issue"]
        live = fetch_issue_state(token, base_url, owner, repo, n, prd.get("lock_in_comment_id"))
        body_drift = live["body_sha256_16"] != prd["body_sha256_16"]
        lock_drift = (
            prd.get("lock_in_comment_id") is not None
            and live.get("lock_in_sha256_16") != prd.get("lock_in_sha256_16")
        )
        status = "drift" if body_drift or lock_drift else "clean"
        findings.append({
            "contract": str(path.relative_to(PROJECT_ROOT)),
            "issue": n,
            "status": status,
            "body_drift": body_drift,
            "lock_in_drift": lock_drift,
            "pinned_at": prd.get("pinned_at"),
            "live": live,
            "pinned": {
                "body_sha256_16": prd["body_sha256_16"],
                "lock_in_sha256_16": prd.get("lock_in_sha256_16"),
            },
        })

    if args.json:
        print(json.dumps({"findings": findings}, indent=2))
    else:
        clean = [f for f in findings if f["status"] == "clean"]
        drift = [f for f in findings if f["status"] == "drift"]
        for f in findings:
            mark = "OK" if f["status"] == "clean" else "DRIFT"
            print(f"  [{mark}] {f['contract']} (issue #{f['issue']})")
            if f["body_drift"]:
                print(f"      body: pinned={f['pinned']['body_sha256_16']} live={f['live']['body_sha256_16']}")
            if f["lock_in_drift"]:
                print(f"      lock-in: pinned={f['pinned']['lock_in_sha256_16']} live={f['live'].get('lock_in_sha256_16')}")
        print()
        print(f"{len(clean)} clean, {len(drift)} drifted")

    return 1 if any(f["status"] == "drift" for f in findings) else 0


if __name__ == "__main__":
    sys.exit(main())
