#!/usr/bin/env python3
import hashlib
import json
import os
from datetime import datetime, timezone
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
LOCK_PATH = ROOT / "log/run-002/audit-set-lock-v4.json"
OUTPUT_PATH = ROOT / "log/run-002/semantic-review-v4.json"
REPORTS = [
    ROOT / "log/run-002/auditor-A-structural-v4.json",
    ROOT / "log/run-002/auditor-B-semantic-v4.json",
]
EXPECTED_IDS = ["blocking-A-structural", "blocking-B-semantic"]


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


def audit_set_digest(rows):
    payload = json.dumps(
        {row["path"]: row["sha256"] for row in rows},
        ensure_ascii=False,
        sort_keys=True,
        separators=(",", ":"),
    ).encode("utf-8")
    return hashlib.sha256(payload).hexdigest()


def atomic_json(path, value):
    path.parent.mkdir(parents=True, exist_ok=True)
    part = path.with_suffix(path.suffix + ".part")
    data = (json.dumps(value, ensure_ascii=False, indent=2) + "\n").encode("utf-8")
    with open(part, "xb") as handle:
        handle.write(data)
        handle.flush()
        os.fsync(handle.fileno())
    os.replace(part, path)


def fail(message):
    raise RuntimeError(message)


def main():
    if OUTPUT_PATH.exists():
        fail("semantic aggregate must be virgin")
    lock_hash = sha256(LOCK_PATH)
    lock = json.loads(LOCK_PATH.read_text(encoding="utf-8"))
    if lock.get("status") != "FROZEN" or lock.get("verified") is not True:
        fail("audit-set lock not terminal FROZEN")
    current_rows = []
    for expected in lock.get("locked_files", []):
        path = ROOT / expected["path"]
        current = {
            "key": expected["key"],
            "path": expected["path"],
            "bytes": path.stat().st_size,
            "mtime_ns": path.stat().st_mtime_ns,
            "sha256": sha256(path),
        }
        current_rows.append(current)
    if current_rows != lock.get("locked_files"):
        fail("frozen snapshot drift before aggregate")
    audit_set = audit_set_digest(current_rows)
    if audit_set != lock.get("audit_set_sha256"):
        fail("audit-set digest not reproducible")

    reports = []
    observed_hashes = {row["path"]: row["sha256"] for row in current_rows}
    for index, path in enumerate(REPORTS):
        if not path.exists():
            fail(f"auditor report missing: {path}")
        report = json.loads(path.read_text(encoding="utf-8"))
        expected_id = EXPECTED_IDS[index]
        checks = {
            "auditor_id": report.get("auditor_id") == expected_id,
            "run_id": report.get("run_id") == "run-002",
            "revision": report.get("revision") == "v4",
            "stale_false": report.get("stale") is False,
            "terminal": report.get("terminal") is True,
            "full_read": report.get("full_read") is True,
            "candidate": report.get("candidate_sha256") == lock.get("candidate_sha256"),
            "audit_set": report.get("audit_set_sha256") == audit_set,
            "lock_receipt": report.get("audit_lock_receipt_sha256") == lock_hash,
            "observed_hashes": report.get("observed_hashes") == observed_hashes,
            "blockers_zero": report.get("blocker_count") == 0,
            "highs_zero": report.get("high_count") == 0,
            "verdict": report.get("verdict") == "PASS",
            "promotion_eligible": report.get("promotion_eligible") is True,
        }
        if not all(checks.values()):
            fail(f"auditor report failed validation: {expected_id}: {checks}")
        reports.append({
            "auditor_id": expected_id,
            "path": str(path.relative_to(ROOT)),
            "report_sha256": sha256(path),
            "terminal": True,
            "stale": False,
            "full_read": True,
            "blocker_count": 0,
            "high_count": 0,
            "verdict": "PASS",
            "findings": report.get("findings", []),
        })

    now = datetime.now(timezone.utc).isoformat()
    aggregate = {
        "status": "PASS",
        "verified": True,
        "run_id": "run-002",
        "revision": "v4",
        "candidate_sha256": lock["candidate_sha256"],
        "audit_set_sha256": audit_set,
        "audit_lock_receipt_sha256": lock_hash,
        "stale": False,
        "provenance_verdict": "PASS",
        "semantic_verdict": "PASS",
        "blocker_count": 0,
        "high_count": 0,
        "independent_auditor_count": 2,
        "auditor_disagreement": False,
        "auditor_reports": reports,
        "observed_hashes": observed_hashes,
        "promotion_eligible": True,
        "completed_at": now,
    }
    atomic_json(OUTPUT_PATH, aggregate)
    print(json.dumps({
        "status": "PASS",
        "semantic_review_sha256": sha256(OUTPUT_PATH),
        "candidate_sha256": aggregate["candidate_sha256"],
        "audit_set_sha256": audit_set,
        "auditors": EXPECTED_IDS,
    }, ensure_ascii=False))


if __name__ == "__main__":
    main()
