#!/usr/bin/env python3
"""
Parity checker · Phase 2a+ Python port vs Phase 2a JS reference.

Loads evaluator_examples.json and runs every test vector through the Python
evaluator. Exits 0 on full parity (19/19 pass), 1 otherwise.

Run:
    python3 docs/runtime/feature-flags-service/verify_parity.py

Expected output:
    ✅ 19 pass · 0 fail · parity OK

Any failure here means the Python port has drifted from evaluator.js and
MUST be corrected before the service is considered safe to run.
"""
from __future__ import annotations

import json
import os
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)

from evaluator import check_example, hash_bucket, evaluate  # noqa: E402


def _load(path: str):
    with open(path, encoding="utf-8") as f:
        return json.load(f)


def main() -> int:
    ff_dir = os.path.abspath(os.path.join(HERE, "..", "feature-flags"))
    registry = _load(os.path.join(ff_dir, "registry.json"))
    examples = _load(os.path.join(ff_dir, "evaluator_examples.json"))

    passes = 0
    fails = 0
    failures: list[str] = []

    # Real-registry examples
    for ex in examples["real_registry_examples"]:
        r = check_example(registry, ex)
        mark = "✅" if r["pass"] else "❌"
        print(f"{mark} {ex['id']} · {ex['name']}")
        if r["pass"]:
            passes += 1
        else:
            fails += 1
            failures.append(
                f"  {ex['id']}: expected={r.get('expected')} got={r.get('got')}"
            )

    # Synthetic-registry examples · mirror JS runner: source match counts
    syn_reg = examples["synthetic_registry"]
    for ex in examples["synthetic_examples"]:
        r = check_example(syn_reg, ex)
        # JS runner accepts strict pass OR source-only match for synthetic
        got_source = r.get("got", {}).get("source")
        matches_source = got_source == ex["expected"]["source"]
        ok = r["pass"] or matches_source
        mark = "✅" if ok else "❌"
        print(f"{mark} {ex['id']} · {ex['name']}")
        if ok:
            passes += 1
        else:
            fails += 1
            failures.append(
                f"  {ex['id']}: expected={ex['expected']} got={r.get('got')}"
            )

    # Cross-check hash determinism · same input twice → same bucket
    b1 = hash_bucket("bucket-low-1|syn.staged_25")
    b2 = hash_bucket("bucket-low-1|syn.staged_25")
    determ_ok = b1 == b2
    print(f"{'✅' if determ_ok else '❌'} hash determinism · {b1} == {b2}")
    if not determ_ok:
        fails += 1
        failures.append("  hash_bucket non-deterministic")

    # Cross-check evaluate idempotency · determinism_guarantees pr
    req = {"flag_key": "syn.staged_25", "user_id": "U-determ", "env": "prod", "tier": "member"}
    r1 = evaluate(syn_reg, req)
    r2 = evaluate(syn_reg, req)
    eval_determ = (
        r1["value"] == r2["value"]
        and r1["source"] == r2["source"]
        and r1["bucket"] == r2["bucket"]
    )
    print(f"{'✅' if eval_determ else '❌'} evaluate determinism · value/source/bucket match")
    if not eval_determ:
        fails += 1
        failures.append("  evaluate() non-deterministic")

    print()
    print(f"{passes} pass · {fails} fail · parity {'OK' if fails == 0 else 'BROKEN'}")
    if failures:
        print("\nFailures:")
        for f in failures:
            print(f)
    return 0 if fails == 0 else 1


if __name__ == "__main__":
    sys.exit(main())
