#!/usr/bin/env python3
"""Continuity receipt gate — dependency-free, fail-closed.

CONTRACT: continuity.receipt.v2

Changes from v1 (c101042 audit):
- expected_head is NO LONGER optional: single-file invocation without a manifest
  now REFUSEs (head_unverified), closing the gap where any document could carry
  any expected_head and PASS as long as no manifest was supplied.
"""

import json
import sys

ALLOWED_CONTRACTS = {"continuity.receipt.v2"}

REQUIRED = [
    "contract",
    "provenance",        # must be present; INHERITED/None/"" refused
    "work_line",
    "evidence_ids",
    "expected_head",
]


def refuse(reason: str) -> None:
    print(f"REFUSE {reason}")
    sys.exit(1)


def validate(doc_path: str, manifest_path: str | None) -> None:
    with open(doc_path) as fh:
        doc = json.load(fh)

    # 1. missing-key check BEFORE any indexing (fail-closed)
    missing = [k for k in REQUIRED if k not in doc]
    if missing:
        refuse(f"unknown_contract (missing keys: {missing})")

    # 2. contract allowlist
    if doc["contract"] not in ALLOWED_CONTRACTS:
        refuse(f"contract_mismatch (got {doc['contract']!r}, "
               f"allowed {sorted(ALLOWED_CONTRACTS)})")

    # 3. provenance — must be OBSERVED (refuses None, "", INHERITED)
    provenance = doc["provenance"]
    if provenance in (None, "", "INHERITED"):
        refuse(f"provenance_not_observed (got {provenance!r})")

    # 4. evidence_ids — guard empty before indexing
    evidence_ids = doc["evidence_ids"]
    if not evidence_ids:
        refuse("evidence_empty")

    # 5. evidence contiguity
    expected = evidence_ids[0]
    for eid in evidence_ids[1:]:
        if eid != expected + 1:
            refuse(f"noncontiguous_evidence (expected {expected + 1}, "
                   f"got {eid})")
        expected = eid

    # 6. expected_head — manifest is REQUIRED (was optional in v1; c101042)
    if manifest_path is None:
        refuse("head_unverified (no manifest supplied)")

    with open(manifest_path) as fh:
        manifest = json.load(fh)

    if doc["expected_head"] != manifest.get("expected_head"):
        refuse(f"head_mismatch "
               f"(carrier={doc['expected_head']!r}, "
               f"manifest={manifest.get('expected_head')!r})")

    print(f"PASS {doc_path}")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("USAGE: python3 continuity_gate.py DOCUMENT.json MANIFEST.json")
        sys.exit(1)

    manifest = sys.argv[2] if len(sys.argv) > 2 else None
    validate(sys.argv[1], manifest)