#!/usr/bin/env python3
"""Bench: compare SuperGold Bingo 0x30 wire card blocks to operator reference grids."""

from __future__ import annotations

import argparse
import itertools
import sys
from pathlib import Path

from pog422_fixture_core import (
    BINGO_CARD_CODEC_STATUS,
    BINGO_OPERATOR_REFERENCE_CARDS,
    BINGO_WAGER_MSG,
    bingo_card_bytes_to_grid_legacy_cgs,
    bingo_card_is_off,
    bingo_card_raw_blocks_from_wager,
    bingo_calls_from_result,
    bingo_grid_number_set,
    bingo_grids_equal,
    bingo_operator_reference_overlap_score,
    bingo_play_card_count_from_wager,
    bingo_wager_byte8_play_credits,
)


def _parse_packets_raw(path: Path) -> list[bytes]:
    out: list[bytes] = []
    for line in path.read_text(encoding="utf-8").splitlines():
        line = line.strip()
        if not line:
            continue
        out.append(bytes(int(x, 16) for x in line.split(",")))
    return out


def _best_slot_assignment(
    blocks: tuple[bytes, ...],
    reference: tuple,
) -> tuple[int, tuple[int, ...]]:
    active = [(i, b) for i, b in enumerate(blocks) if not bingo_card_is_off(b)]
    if len(active) != len(reference):
        return 0, tuple()
    sets = [
        bingo_grid_number_set(bingo_card_bytes_to_grid_legacy_cgs(block))
        for _, block in active
    ]
    ref_sets = [bingo_grid_number_set(grid) for grid in reference]
    best_score = 0
    best_perm: tuple[int, ...] = tuple()
    for perm in itertools.permutations(range(len(sets))):
        score = sum(len(sets[perm[i]] & ref_sets[i]) for i in range(len(ref_sets)))
        if score > best_score:
            best_score = score
            best_perm = perm
    return best_score, best_perm


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "packets_raw",
        type=Path,
        help="Path to pos2/pos3 data/packets.raw from a bingo bench session",
    )
    parser.add_argument(
        "--wager-index",
        type=int,
        default=0,
        help="Nth 0x30 wager in file (default: first / 4-card round)",
    )
    args = parser.parse_args(argv)

    wagers = [p for p in _parse_packets_raw(args.packets_raw) if len(p) > 1 and p[1] == BINGO_WAGER_MSG]
    if not wagers:
        print(f"No 0x30 wagers in {args.packets_raw}", file=sys.stderr)
        return 2
    if args.wager_index >= len(wagers):
        print(f"Only {len(wagers)} wagers; index {args.wager_index} out of range", file=sys.stderr)
        return 2

    wager = wagers[args.wager_index]
    blocks = bingo_card_raw_blocks_from_wager(wager)
    play = bingo_play_card_count_from_wager(wager)
    credits = bingo_wager_byte8_play_credits(wager)

    print(f"codec_status: {BINGO_CARD_CODEC_STATUS}")
    print(f"wager: len={len(wager)} seq={wager[7]} byte8=0x{credits:02x} play_cards={play}")
    print()

    for i, block in enumerate(blocks):
        off = 29 + i * 12
        state = "OFF" if bingo_card_is_off(block) else "ON "
        print(f"slot {i + 1} @{off}: {state}  {block.hex()}")

    score, perm = _best_slot_assignment(blocks, BINGO_OPERATOR_REFERENCE_CARDS)
    max_score = 24 * min(play, len(BINGO_OPERATOR_REFERENCE_CARDS))
    print()
    print(f"legacy_cgs overlap (best slot→ref perm): {score}/{max_score}")
    if perm:
        print(f"  assignment: wire slot {[i + 1 for i in perm]} → ref card {[i + 1 for i in range(len(perm))]}")

    print()
    print("per-slot legacy decode vs reference (same index):")
    for i, (block, ref) in enumerate(zip(blocks, BINGO_OPERATOR_REFERENCE_CARDS)):
        if bingo_card_is_off(block):
            print(f"  slot {i + 1}: OFF")
            continue
        grid = bingo_card_bytes_to_grid_legacy_cgs(block)
        matches = sum(
            1
            for r in range(5)
            for c in range(5)
            if not (r == 2 and c == 2) and grid[r][c] == ref[r][c]
        )
        exact = bingo_grids_equal(grid, ref)
        print(f"  slot {i + 1}: {matches}/24 spots match ref{i + 1}  exact={exact}")

    results = [p for p in _parse_packets_raw(args.packets_raw) if len(p) > 1 and p[1] == 0x39]
    if results:
        calls = bingo_calls_from_result(results[min(args.wager_index, len(results) - 1)])
        ref_hits = [
            len(bingo_grid_number_set(ref) & set(calls))
            for ref in BINGO_OPERATOR_REFERENCE_CARDS[:play]
        ]
        print()
        print(f"0x39 calls @ offset 21: {len(calls)} balls — ref card hit counts: {ref_hits}")

    if score >= max_score * 0.9:
        print("\nPASS: legacy codec matches operator reference")
        return 0
    print("\nFAIL: legacy BINGO.C codec does not decode operator grids from this wire")
    return 1


if __name__ == "__main__":
    raise SystemExit(main())
