#!/usr/bin/env python3
"""Independent standard-library verifier for the pocket-pair power table."""

import csv
import json
import math
from pathlib import Path

ROOT = Path(__file__).resolve().parent
JSON_PATH = ROOT / "poker-shuffle-pocket-pair-power.json"
CSV_PATH = ROOT / "poker-shuffle-pocket-pair-power.csv"


def distribution(n, p):
    if not (0 < p < 1):
        raise ValueError("this verifier expects 0 < p < 1")
    mode = math.floor((n + 1) * p)
    weights = [0.0] * (n + 1)
    weights[mode] = 1.0
    for k in range(mode, 0, -1):
        weights[k - 1] = weights[k] * (k / (n - k + 1)) * ((1 - p) / p)
    for k in range(mode, n):
        weights[k + 1] = weights[k] * ((n - k) / (k + 1)) * (p / (1 - p))
    total = math.fsum(weights)
    return [value / total for value in weights]


def critical_region(n, p, family_alpha=0.05, planned_tests=1):
    pmf = distribution(n, p)
    tail_budget = family_alpha / (2 * planned_tests)
    lower = -1
    lower_tail = 0.0
    running = 0.0
    for k, value in enumerate(pmf):
        running += value
        if running <= tail_budget + 1e-14:
            lower = k
            lower_tail = running
        else:
            break
    upper = n + 1
    upper_tail = 0.0
    running = 0.0
    for k in range(n, -1, -1):
        running += pmf[k]
        if running <= tail_budget + 1e-14:
            upper = k
            upper_tail = running
        else:
            break
    return lower, upper, lower_tail + upper_tail


def power(n, p, lower, upper):
    pmf = distribution(n, p)
    return math.fsum(pmf[: lower + 1]) + math.fsum(pmf[upper:])


def close(actual, expected, tolerance=6e-12):
    return math.isclose(actual, expected, rel_tol=tolerance, abs_tol=tolerance)


def main():
    model = json.loads(JSON_PATH.read_text(encoding="utf-8"))
    assert model["schemaVersion"] == 1
    assert model["generatedOn"] == "2026-09-03"
    assert model["testRule"]["familyAlpha"] == 0.05
    assert model["testRule"]["plannedTestsForPublishedRows"] == 1
    assert model["plantedAlternatives"] == [0.04, 0.08]
    assert len(model["sources"]) == 4
    assert any("cannot certify randomness" in item.lower() for item in model["limitations"])
    assert model["nullModel"]["favorableTwoCardCombinations"] == 78
    assert model["nullModel"]["totalTwoCardCombinations"] == math.comb(52, 2)
    p0 = model["nullModel"]["probability"]
    assert close(p0, 1 / 17)

    expected_sizes = [100, 250, 500, 1000, 2500, 5000, 10000, 50000]
    assert [row["deals"] for row in model["rows"]] == expected_sizes

    for row in model["rows"]:
        n = row["deals"]
        lower, upper, actual_alpha = critical_region(n, p0)
        assert lower == row["lowerFlagAtOrBelow"], (n, lower)
        assert upper == row["upperFlagAtOrAbove"], (n, upper)
        assert close(actual_alpha, row["actualSingleTestFalseFlagRate"]), n
        assert close(power(n, 0.04, lower, upper), row["powerIfTrueRateIs4Percent"]), n
        assert close(power(n, 0.08, lower, upper), row["powerIfTrueRateIs8Percent"]), n

    with CSV_PATH.open(newline="", encoding="utf-8") as handle:
        reader = csv.DictReader(handle)
        csv_rows = list(reader)
    assert reader.fieldnames == [
        "deals", "expected_pocket_pairs", "lower_flag_at_or_below",
        "upper_flag_at_or_above", "actual_false_flag_rate",
        "power_true_rate_0_04", "power_true_rate_0_08",
    ]
    assert len(csv_rows) == len(model["rows"])
    for json_row, csv_row in zip(model["rows"], csv_rows):
        assert int(csv_row["deals"]) == json_row["deals"]
        assert int(csv_row["lower_flag_at_or_below"]) == json_row["lowerFlagAtOrBelow"]
        assert int(csv_row["upper_flag_at_or_above"]) == json_row["upperFlagAtOrAbove"]
        assert close(float(csv_row["power_true_rate_0_08"]), json_row["powerIfTrueRateIs8Percent"])

    print(f"Verified {len(model['rows'])} pocket-pair power rows independently.")


if __name__ == "__main__":
    main()
