#!/usr/bin/env python3
"""API & Permission Drift Guard.
Checks whether packages/contracts and packages/permissions are in sync
with apps/backend/docs/openapi.yaml and apps/backend/internal/auth/catalog.go.
Exits with 0 if clean, non-zero with informative diff if out-of-sync.
"""
import os
import re
import sys
import yaml

def check_contracts():
    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    openapi_path = os.path.join(root, "apps", "backend", "docs", "openapi.yaml")
    txt_path = os.path.join(root, "packages", "contracts", "src", "routes.txt")

    if not os.path.exists(openapi_path):
        print(f"FAIL: OpenAPI spec not found: {openapi_path}")
        return False
    if not os.path.exists(txt_path):
        print(f"FAIL: Generated routes.txt not found: {txt_path}. Run 'pnpm contracts:generate'.")
        return False

    with open(openapi_path, encoding="utf-8") as f:
        spec = yaml.safe_load(f)

    verbs = ("get", "post", "put", "patch", "delete")
    actual_routes = set()
    for path, ops in (spec.get("paths") or {}).items():
        if isinstance(ops, dict):
            for verb in ops:
                if verb.lower() in verbs:
                    actual_routes.add(f"{verb.lower()} {path}")

    with open(txt_path, encoding="utf-8") as f:
        recorded_routes = set(line.strip() for line in f if line.strip())

    if actual_routes != recorded_routes:
        missing = actual_routes - recorded_routes
        extra = recorded_routes - actual_routes
        print(f"FAIL: API Contract drift detected!")
        if missing:
            print(f"  Missing in contracts ({len(missing)}): {sorted(missing)[:5]}")
        if extra:
            print(f"  Extra in contracts ({len(extra)}): {sorted(extra)[:5]}")
        print("Run 'pnpm contracts:generate' to synchronize.")
        return False

    print(f"PASS: Contracts drift check clean ({len(actual_routes)} routes).")
    return True

def check_permissions():
    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    catalog_path = os.path.join(root, "apps", "backend", "internal", "auth", "catalog.go")
    txt_path = os.path.join(root, "packages", "permissions", "src", "permissions.txt")

    if not os.path.exists(catalog_path):
        print(f"FAIL: Backend catalog not found: {catalog_path}")
        return False
    if not os.path.exists(txt_path):
        print(f"FAIL: Generated permissions.txt not found: {txt_path}. Run 'pnpm permissions:generate'.")
        return False

    key_re = re.compile(r'"([a-z_][a-z0-9_]*(?:\.[a-z0-9_-]+)+)"')
    with open(catalog_path, encoding="utf-8") as f:
        source = f.read()

    actual_keys = {m.group(1) for m in key_re.finditer(source) if not m.group(1).endswith(".")}

    with open(txt_path, encoding="utf-8") as f:
        recorded_keys = set(line.strip() for line in f if line.strip())

    if actual_keys != recorded_keys:
        missing = actual_keys - recorded_keys
        extra = recorded_keys - actual_keys
        print(f"FAIL: Permissions drift detected!")
        if missing:
            print(f"  Missing in permissions ({len(missing)}): {sorted(missing)}")
        if extra:
            print(f"  Extra in permissions ({len(extra)}): {sorted(extra)}")
        print("Run 'pnpm permissions:generate' to synchronize.")
        return False

    print(f"PASS: Permissions drift check clean ({len(actual_keys)} keys).")
    return True

def main():
    ok1 = check_contracts()
    ok2 = check_permissions()
    if not (ok1 and ok2):
        sys.exit(1)
    print("ALL DRIFT CHECKS PASSED.")

if __name__ == "__main__":
    main()
