"""Compare frozen synthetic servicing evidence without submitting any writes."""

import argparse
import json
from pathlib import Path


IDENTITY_FIELDS = ("account_id", "event_key", "source_revision", "policy_revision", "cutoff")
RECEIPT_FIELDS = ("destination", "operation_key", "output_digest")


def decide(field, case):
    source, candidate = case["source"], case["candidate"]
    if any(source.get(key) is None or candidate.get(key) is None for key in IDENTITY_FIELDS):
        return "hold", "MISSING_SCOPE"
    if any(source[key] != candidate[key] for key in IDENTITY_FIELDS):
        return "hold", "SCOPE_MISMATCH"

    observed = candidate["facts"].get(field)
    expected = source["facts"].get(field)
    if observed is None or expected is None:
        return "hold", "MISSING_FIELD"
    if type(observed) is not type(expected) or observed != expected:
        return "hold", "FIELD_MISMATCH"

    if attempt := case.get("uncertain_attempt"):
        receipt = case.get("receipt")
        if receipt is None:
            return "hold", "RECEIPT_UNKNOWN"
        if any(attempt.get(key) is None or attempt[key] != receipt.get(key) for key in RECEIPT_FIELDS):
            return "hold", "RECEIPT_MISMATCH"
        return "reconciled_no_retry", "MATCHING_RECEIPT"

    if prior := case.get("previous_decision"):
        if prior["event_key"] != candidate["event_key"]:
            return "hold", "PRIOR_EVENT_MISMATCH"
        if prior["source_revision"] == candidate["source_revision"]:
            if prior["input_digest"] == candidate["input_digest"]:
                return "duplicate", "IDENTICAL_REPLAY"
            return "hold", "REUSED_REVISION"
        if candidate.get("supersedes") != prior["source_revision"]:
            return "hold", "UNLINKED_CORRECTION"
        return "ready_for_review", "MATCHED_CORRECTION"

    return "ready_for_review", "MATCHED"


def run_process(process):
    field = process["field"]
    for case in process["cases"]:
        decision, reason = decide(field, case)
        yield {
            "case": case["name"],
            "field": field,
            "observed": case["candidate"]["facts"].get(field),
            "expected": case["source"]["facts"].get(field),
            "decision": decision,
            "reason": reason,
            "exception_owner": process["exception_owner"] if decision == "hold" else None,
            "write_count": 0,
        }


def main():
    directory = Path(__file__).resolve().parent
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--process", help="Run one process slug. Omit to run the complete fixture set.")
    parser.add_argument("--fixtures", type=Path, default=directory / "fixtures.json")
    parser.add_argument("--expected", type=Path, default=directory / "expected.json")
    args = parser.parse_args()

    fixtures = json.loads(args.fixtures.read_text(encoding="utf-8"))
    expected = json.loads(args.expected.read_text(encoding="utf-8"))
    if args.process and args.process not in fixtures:
        parser.error(f"unknown process {args.process!r}")
    selected = [args.process] if args.process else fixtures
    actual = {process: list(run_process(fixtures[process])) for process in selected}
    mismatches = [process for process, rows in actual.items() if rows != expected.get(process)]
    if mismatches:
        parser.exit(1, f"Unexpected fixture output for: {', '.join(mismatches)}\n")
    print(json.dumps(actual, indent=2, ensure_ascii=False))


if __name__ == "__main__":
    main()
