"""Inspect AuthorizationBench release artifacts without running upstream code.

Usage: python3 audit_release.py /path/to/AuthorizationBench
This checks inventories and contract structure, not agent behavior or verdicts.
"""

import argparse
import collections
import csv
import hashlib
import json
from pathlib import Path
import subprocess


def sha256(path):
    return hashlib.sha256(path.read_bytes()).hexdigest()


def audit(root):
    manifest_path = root / "benchmark" / "manifest.json"
    lineage_path = root / "benchmark" / "lineage.csv"
    manifest = json.loads(manifest_path.read_text())
    with lineage_path.open(newline="") as handle:
        lineage = list(csv.DictReader(handle))
    task_ids = manifest["task_ids"]
    lineage_ids = [row["task_id"] for row in lineage]
    bundles = root / "authorization_eva_sandbox" / "tasks"
    bundle_ids = {path.name for path in bundles.iterdir() if path.is_dir()}
    families = collections.Counter(row["source_case_id"] for row in lineage)
    case_types = collections.Counter(row["case_type"] for row in lineage)
    invalid_contracts = []
    multiple_completion_alternatives = 0
    for task_id in task_ids:
        config_path = bundles / task_id / "task_config.json"
        if not config_path.is_file():
            invalid_contracts.append({"task_id": task_id, "problem": "missing config"})
            continue
        config = json.loads(config_path.read_text())
        criteria = config.get("completion_criteria")
        actions = config.get("authorization_scope", {}).get("violated_actions")
        if not isinstance(criteria, list) or not criteria or not all(
            isinstance(item, str) and item.strip() for item in criteria
        ):
            invalid_contracts.append({"task_id": task_id, "problem": "completion criteria"})
        elif len(criteria) > 1:
            multiple_completion_alternatives += 1
        if not isinstance(actions, list) or not actions or not all(
            isinstance(item, str) and item.strip() for item in actions
        ):
            invalid_contracts.append({"task_id": task_id, "problem": "specified actions"})
    checks = {
        "manifest_count_matches_declaration": len(task_ids) == manifest["executable_tasks"],
        "manifest_ids_unique": len(task_ids) == len(set(task_ids)),
        "lineage_ids_unique": len(lineage_ids) == len(set(lineage_ids)),
        "lineage_matches_manifest": set(lineage_ids) == set(task_ids),
        "bundles_match_manifest": bundle_ids == set(task_ids),
        "original_count_matches_declaration": case_types["original"] == manifest["original_task_cases"],
        "source_count_matches_declaration": len(families) == manifest["source_case_records"],
        "contracts_have_nonempty_criteria_and_actions": not invalid_contracts,
    }
    return {
        "repository_revision": subprocess.check_output(
            ["git", "-C", str(root), "rev-parse", "HEAD"], text=True
        ).strip(),
        "manifest_source_revision": manifest["source_revision"],
        "manifest_sha256": sha256(manifest_path),
        "lineage_sha256": sha256(lineage_path),
        "task_count": len(task_ids),
        "task_bundle_count": len(bundle_ids),
        "source_record_count": len(families),
        "case_types": dict(sorted(case_types.items())),
        "largest_source_families": [
            {"source_case_id": family, "tasks": count}
            for family, count in families.most_common(5)
        ],
        "tasks_with_multiple_completion_alternatives": multiple_completion_alternatives,
        "invalid_contracts": invalid_contracts,
        "checks": checks,
        "scope": "Static artifact audit only; no agents, judges, services, or verdicts executed.",
    }


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("repository", type=Path)
    arguments = parser.parse_args()
    result = audit(arguments.repository.resolve())
    print(json.dumps(result, indent=2))
    raise SystemExit(0 if all(result["checks"].values()) else 1)
