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_supported_intermediate"]

def m(**kw):
    x=copy.deepcopy(b); x.update(kw); return x

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

t("baseline",b,"UPGRADE_PATH_REVIEW_READY")
t("mutation_untested_database",cases["mutation_untested_database"],"HOLD_DATABASE_VERSION_UNTESTED")
t("transfer_untested_db",cases["transfer_security_patch_untested_db"],"HOLD_PATCH_ON_UNTESTED_DATABASE")
t("runtime_untested",m(runtime_version="9.0.0"),"HOLD_RUNTIME_VERSION_UNTESTED")
t("compat_unproven",m(compatibility_state="UNTESTED"),"HOLD_COMPATIBILITY_NOT_PROVEN")
t("direct_skip",m(direct_upgrade_attempted=True),"HOLD_DIRECT_UPGRADE_UNSUPPORTED")
t("hop_missing",m(upgrade_hop_state="DIRECT_SUPPORTED"),"HOLD_REQUIRED_INTERMEDIATE_HOP_MISSING")
t("hop_invalid",m(intermediate_hop_version="3.4.9"),"HOLD_INTERMEDIATE_HOP_INVALID")
t("migration_false",m(migration_required=False),"HOLD_REQUIRED_MIGRATION_NOT_DECLARED")
t("migration_ref_missing",m(migration_ref="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:migration_ref")
t("backup_false",m(backup_required=False),"HOLD_REQUIRED_BACKUP_NOT_DECLARED")
t("backup_ref_none",m(backup_evidence_ref="NONE"),"HOLD_BACKUP_EVIDENCE_MISSING")
t("rollback_ref_none",m(rollback_ref="NONE"),"HOLD_ROLLBACK_EVIDENCE_MISSING")
t("support_expired",m(support_start="2025-01-01",support_end="2026-01-01"),"HOLD_SUPPORT_WINDOW_EXPIRED")
t("support_window_inverted",m(support_start="2027-01-02",support_end="2027-01-01"),"HOLD_SUPPORT_WINDOW_INVALID")
t("eol_patch_conflict",m(eol_state="EOL",patch_eligible=True),"HOLD_EOL_PATCH_CONFLICT")
t("eol_tier_state_mismatch",m(support_tier="EOL",eol_state="ACTIVE"),"HOLD_SUPPORT_EOL_STATE_MISMATCH")
t("operator_ack",m(operator_ack_state="PENDING"),"HOLD_OPERATOR_ACK_MISSING")
t("known_incompat",m(known_incompatibility_ref="BLOCKING_RUNTIME_ABI"),"HOLD_KNOWN_INCOMPATIBILITY")
t("invalid_product_semver",m(product_version="3.4"),"HOLD_VERSION_FORMAT_INVALID")
t("invalid_target_semver",m(target_product_version="latest"),"HOLD_VERSION_FORMAT_INVALID")
t("matrix_missing",m(tested_matrix_id="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:tested_matrix_id")
t("fingerprint_missing",m(deployment_fingerprint="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:deployment_fingerprint")
t("compat_evidence_missing",m(compatibility_evidence_ref="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:compatibility_evidence_ref")
t("tested_runtime_set_missing",m(tested_runtime_versions=[]),"HOLD_TESTED_RUNTIME_SET_MISSING")
t("tested_db_set_missing",m(tested_database_versions=[]),"HOLD_TESTED_DATABASE_SET_MISSING")
t("upgrade_source_set_invalid",m(supported_upgrade_from_versions="3.5.4"),"HOLD_UPGRADE_SOURCE_SET_INVALID")
t("upgrade_identity_from",m(upgrade_from_version="3.4.1"),"HOLD_UPGRADE_IDENTITY_MISMATCH")
t("upgrade_identity_to",m(upgrade_to_version="3.6.1"),"HOLD_UPGRADE_IDENTITY_MISMATCH")
t("upgrade_decision_mismatch",m(decision_state="PATCH_CANDIDATE"),"HOLD_UPGRADE_DECISION_STATE_MISMATCH")
# Valid direct upgrade case
direct=m(supported_upgrade_from_versions=["3.4.2"],upgrade_hop_state="DIRECT_SUPPORTED",intermediate_hop_version="NOT_REQUIRED",decision_state="SUPPORTED_DIRECT_UPGRADE",direct_upgrade_attempted=True)
t("direct_supported",direct,"UPGRADE_PATH_REVIEW_READY")
# Transfer with added evidence becomes patch-ready
p=copy.deepcopy(cases["transfer_security_patch_untested_db"]); p["tested_database_versions"].append("14.10"); p["compatibility_state"]="TESTED_SUPPORTED"
t("transfer_after_evidence",p,"PATCH_REVIEW_READY")
p2=copy.deepcopy(p); p2["baseline_decision_reused"]=True
t("transfer_oracle_reuse",p2,"HOLD_TRANSFER_BASELINE_ORACLE_REUSE")
p3=copy.deepcopy(p); p3["transfer_recomputed"]=False
t("transfer_not_recomputed",p3,"HOLD_TRANSFER_NOT_RECOMPUTED")
p4=copy.deepcopy(p); p4["patch_eligible"]=False
t("patch_not_eligible",p4,"HOLD_PATCH_NOT_ELIGIBLE")
p5=copy.deepcopy(p); p5["support_tier"]="LIMITED"
t("patch_tier",p5,"HOLD_PATCH_TIER_NOT_ELIGIBLE")
p6=copy.deepcopy(p); p6["decision_state"]="CURRENT_RELEASE_SUPPORTED"
t("patch_state_mismatch",p6,"HOLD_PATCH_DECISION_STATE_MISMATCH")
# Stay case
stay=m(decision_kind="STAY",target_release_id="REL-3.4.2",target_product_version="3.4.2",upgrade_to_version="3.4.2",upgrade_hop_state="NOT_APPLICABLE",migration_required=False,migration_ref="NOT_REQUIRED",backup_required=False,backup_evidence_ref="NOT_REQUIRED",decision_state="CURRENT_RELEASE_SUPPORTED",direct_upgrade_attempted=False)
t("stay_supported",stay,"CURRENT_RELEASE_SUPPORT_REVIEW_READY")
t("stay_state_mismatch",{**stay,"decision_state":"PATCH_CANDIDATE"},"HOLD_STAY_DECISION_STATE_MISMATCH")
t("decision_kind_invalid",m(decision_kind="MAGIC"),"HOLD_DECISION_KIND_INVALID")
t("compat_state_invalid",m(compatibility_state="SUPPORTED_BY_VERSION_ORDER"),"HOLD_COMPATIBILITY_STATE_INVALID")
t("support_tier_invalid",m(support_tier="FOREVER"),"HOLD_SUPPORT_TIER_INVALID")
t("eol_state_invalid",m(eol_state="MAYBE"),"HOLD_EOL_STATE_INVALID")
t("decision_state_invalid",m(decision_state="AUTO_APPROVED"),"HOLD_DECISION_STATE_INVALID")
t("release_id_missing",m(release_id="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:release_id")
t("audit_missing",m(audit_ref="UNKNOWN"),"HOLD_REQUIRED_EVIDENCE_MISSING:audit_ref")

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)
