import json,pathlib,copy,sys
from evaluator import evaluate
root=pathlib.Path(__file__).parent
cases={x["case"]:x for x in json.loads((root/"fixtures/cases.json").read_text())["cases"]}
b=cases["baseline_duplicate_and_stale_mapping_review"]
tf=cases["transfer_provider_b_recomputed"]
def m(base=b,**kw):
    x=copy.deepcopy(base); x.update(kw); return x

tests=[]
def t(name,obj,expected): tests.append((name,obj,expected))

t("baseline",b,"RECONCILIATION_REVIEW_REQUIRED")
t("invoice_mapping_conflict",cases["mutation_invoice_mapping_conflict"],"HOLD_INVOICE_MAPPING_CONFLICT")
t("transfer",tf,"TRANSFER_RECONCILIATION_REVIEW_READY")
# Authority shortcuts and missing evidence
t("provider_status_authority",m(business_state_authority="PROVIDER_STATUS"),"HOLD_PROVIDER_STATUS_CANONICAL_SHORTCUT")
t("source_rule_provider",m(source_of_truth_rule_ref="PROVIDER-STATUS"),"HOLD_PROVIDER_STATUS_CANONICAL_SHORTCUT")
t("audit_missing",m(audit_ref="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:audit_ref")
t("idempotency_missing",m(idempotency_key="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:idempotency_key")
t("mapping_missing",m(mapping_version="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:mapping_version")
t("api_version_missing",m(provider_api_version="UNKNOWN"),"HOLD_AUX_EVIDENCE_MISSING:provider_api_version")
# Dedupe/idempotency
t("replay_marked_new",m(dedupe_state="NEW"),"HOLD_DUPLICATE_EVENT_NOT_DEDUPED")
t("replay_transition_applied",m(state_transition_applied=True),"HOLD_DUPLICATE_STATE_TRANSITION")
t("false_duplicate",m(same_provider_event_replayed=False),"HOLD_FALSE_DUPLICATE_CLASSIFICATION")
t("dedupe_invalid",m(dedupe_state="MAYBE"),"HOLD_DEDUPE_STATE_INVALID")
# Identity/reference
t("payment_mapping_conflict",m(provider_payment_internal_target="PAY-INT-OTHER"),"HOLD_PAYMENT_MAPPING_CONFLICT")
t("provider_ref_collision",m(provider_reference_collision=True),"HOLD_PROVIDER_REFERENCE_COLLISION")
# Amount/currency
t("amount_mismatch",m(amount_observed=12899),"HOLD_AMOUNT_MISMATCH")
t("currency_mismatch",m(currency_observed="EUR"),"HOLD_CURRENCY_MISMATCH")
t("amount_negative",m(amount_observed=-1),"HOLD_AMOUNT_INVALID")
# Time evidence
t("created_invalid",m(event_created_at="yesterday"),"HOLD_EVENT_TIME_INVALID")
t("received_before_created",m(event_received_at="2026-09-14T07:59:59"),"HOLD_EVENT_TIME_ORDER_INVALID")
# Mapping/stale
t("current_mapping_stale",m(mapping_version="MAP-STRIPE-v1",stale_mapping_event_present=False,dedupe_state="NEW",same_provider_event_replayed=False,state_transition_applied=True,reconciliation_state="MATCHED",manual_review_required=False,business_state="PAID"),"HOLD_CURRENT_EVENT_MAPPING_STALE")
t("stale_mapping_entitlement",m(mapping_version="MAP-STRIPE-v1",entitlement_effect_ref="ENT-AUTO"),"HOLD_STALE_MAPPING_ENTITLEMENT_SHORTCUT")
t("stale_unreviewed",m(manual_review_required=False),"HOLD_STALE_MAPPING_UNREVIEWED")
t("stale_marked_matched",m(reconciliation_state="MATCHED"),"HOLD_STALE_MAPPING_UNREVIEWED")
# Status semantics
t("status_mapping_unverified",m(status_mapping_verified=False),"HOLD_PROVIDER_STATUS_MAPPING_UNVERIFIED")
t("invoice_semantic_invalid",m(provider_invoice_semantic="MAGIC"),"HOLD_PROVIDER_INVOICE_SEMANTIC_INVALID")
t("payment_semantic_invalid",m(provider_payment_semantic="MAGIC"),"HOLD_PROVIDER_PAYMENT_SEMANTIC_INVALID")
# Non-final paid shortcut
clean=m(stale_mapping_event_present=False,same_provider_event_replayed=False,dedupe_state="NEW",state_transition_applied=True,reconciliation_state="MATCHED",manual_review_required=False,business_state="PAID",reconciliation_reason="ALL_MATCH")
t("nonfinal_paid",m(clean,provider_payment_semantic="NOT_FINAL",provider_payment_status="processing"),"HOLD_NONFINAL_PROVIDER_PAYMENT_BUSINESS_PAID")
# Recon state consistency
t("matched_manual_review",m(clean,manual_review_required=True),"HOLD_MATCHED_WITH_MANUAL_REVIEW_CONFLICT")
t("review_no_manual",m(clean,reconciliation_state="REVIEW_REQUIRED",manual_review_required=False),"HOLD_REVIEW_STATE_WITHOUT_MANUAL_REVIEW")
t("mismatch_no_manual",m(clean,reconciliation_state="MISMATCH",manual_review_required=False),"HOLD_REVIEW_STATE_WITHOUT_MANUAL_REVIEW")
t("recon_invalid",m(clean,reconciliation_state="MAGIC"),"HOLD_RECONCILIATION_STATE_INVALID")
t("duplicate_recon_conflict",m(clean,reconciliation_state="DUPLICATE_IGNORED",dedupe_state="NEW"),"HOLD_DUPLICATE_RECONCILIATION_CONFLICT")
# Valid clean baseline without stale/replay
t("clean_matched",clean,"RECONCILIATION_MATCHED_REVIEW_READY")
# Valid duplicate without stale mapping
dup=m(clean,same_provider_event_replayed=True,dedupe_state="DUPLICATE_IGNORED",state_transition_applied=False,reconciliation_state="DUPLICATE_IGNORED",manual_review_required=False,business_state="BILLING_PENDING",reconciliation_reason="REPLAY_IGNORED")
t("valid_duplicate_ignored",dup,"DUPLICATE_IGNORED_REVIEW_READY")
# Transfer gates
t("transfer_not_neutral",m(tf,provider_neutral_transfer=False),"HOLD_TRANSFER_NOT_PROVIDER_NEUTRAL")
t("transfer_not_recomputed",m(tf,transfer_recomputed=False),"HOLD_TRANSFER_NOT_RECOMPUTED")
t("transfer_baseline_decision",m(tf,baseline_decision_reused=True),"HOLD_TRANSFER_BASELINE_ORACLE_REUSE")
t("transfer_status_map_reuse",m(tf,baseline_status_mapping_reused=True),"HOLD_TRANSFER_BASELINE_STATUS_MAPPING_REUSE")
t("transfer_status_unverified",m(tf,status_mapping_verified=False),"HOLD_PROVIDER_STATUS_MAPPING_UNVERIFIED")
t("transfer_currency_mismatch",m(tf,currency_observed="USD"),"HOLD_CURRENCY_MISMATCH")
t("transfer_invoice_mapping",m(tf,provider_invoice_internal_target="INV-X"),"HOLD_INVOICE_MAPPING_CONFLICT")
# Provider failure cannot be matched paid
t("failure_matched_paid",m(tf,provider_payment_semantic="FAILURE_ACTION_REQUIRED",provider_payment_status="REFUSED"),"HOLD_PROVIDER_FAILURE_MATCHED_PAID_CONFLICT")
# More required evidence checks
t("provider_invoice_missing",m(provider_invoice_id="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:provider_invoice_id")
t("recon_reason_missing",m(reconciliation_reason="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:reconciliation_reason")
t("currency_missing",m(currency="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:currency")
t("status_map_ref_missing",m(provider_status_mapping_ref="UNKNOWN"),"HOLD_AUX_EVIDENCE_MISSING:provider_status_mapping_ref")
t("currency_observed_missing",m(currency_observed="UNKNOWN"),"HOLD_AUX_EVIDENCE_MISSING:currency_observed")

passed=0
for name,obj,expected in tests:
    got=evaluate(obj); ok=got==expected
    print(("PASS" if ok else "FAIL"),name,"=>",got)
    if not ok: print(" expected",expected)
    passed+=int(ok)
print(f"RESULT {passed}/{len(tests)} PASS")
sys.exit(0 if passed==len(tests) else 1)
