"""
CrashMath Labs — Open-Source Provably Fair & Monte Carlo Audit Engine
License: MIT | https://crashmath.org/papers/
"""

import hmac
import hashlib
import math
import csv
import sys
import logging

def compute_multiplier(server_seed: str, client_seed: str, nonce: int, house_edge: float = 0.03) -> float:
    msg = f"{client_seed}:{nonce}".encode("utf-8")
    h = hmac.new(server_seed.encode("utf-8"), msg, hashlib.sha256).hexdigest()
    int_val = int(h[:13], 16)
    x = int_val / (2 ** 52)
    raw = math.floor(((1.0 - house_edge) * 100.0) / (1.0 - x)) / 100.0
    return max(1.00, raw)

def audit_dataset(csv_path: str):
    logger = logging.getLogger(__name__)
    with open(csv_path, "r", encoding="utf-8", newline="") as f:
        rows = list(csv.DictReader(f))
    if not rows:
        raise ValueError("Dataset is empty")
    multipliers = [float(row["multiplier"]) for row in rows]
    if not all(math.isfinite(value) and value >= 1 for value in multipliers):
        raise ValueError("Invalid multiplier")
    digest_mismatches = multiplier_mismatches = 0
    for row in rows:
        digest = hmac.new(row["server_seed"].encode("utf-8"),
                          f'{row["client_seed"]}:{row["nonce"]}'.encode("utf-8"), hashlib.sha256).hexdigest()
        digest_mismatches += digest != row["hmac_sha256"]
        multiplier_mismatches += compute_multiplier(row["server_seed"], row["client_seed"], int(row["nonce"])) != float(row["multiplier"])
    if digest_mismatches or multiplier_mismatches:
        raise ValueError(f"Reproduction mismatch: digests={digest_mismatches}, multipliers={multiplier_mismatches}")
    n = len(multipliers)
    logger.info("Synthetic dataset rows: %s; reproduced digests and multipliers: %s", n, n)
    
    # Instant crash check (1.00x)
    instant_crashes = sum(1 for m in multipliers if m == 1.00)
    logger.info("Displayed 1.00x: %s (%.2f%%); rounded-model mass approximately 4/101 = 3.960396%%", instant_crashes, instant_crashes / n * 100)
    
    # Median and percentiles
    s = sorted(multipliers)
    median = (s[(n - 1) // 2] + s[n // 2]) / 2
    p10 = sum(1 for m in multipliers if m >= 10.0)
    p100 = sum(1 for m in multipliers if m >= 100.0)
    logger.info("Median: %.2fx; mean: %.5fx; maximum: %.2fx", median, sum(multipliers) / n, max(multipliers))
    logger.info("Multipliers >= 10x: %s (%.2f%%); >= 100x: %s (%.2f%%)", p10, p10 / n * 100, p100, p100 / n * 100)
    
    # Pearson Autocorrelation Lag 1
    mean_m = sum(multipliers) / n
    num = sum((multipliers[i] - mean_m) * (multipliers[i+1] - mean_m) for i in range(n - 1))
    den = sum((m - mean_m) ** 2 for m in multipliers)
    r1 = num / den if den != 0 else 0.0
    logger.info("Sample lag-1 autocorrelation: %.8f; descriptive statistic, not proof of independence", r1)
    
    # Longest losing streak for target 2.00x
    curr_streak = 0
    max_streak = 0
    for m in multipliers:
        if m < 2.00:
            curr_streak += 1
            max_streak = max(max_streak, curr_streak)
        else:
            curr_streak = 0
    logger.info("Longest consecutive results below 2x: %s", max_streak)
    logger.info("File reproduction passed. No live operator, payment or secrecy certification is established.")

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, format="%(message)s")
    path = "crashmath_10000_rounds_dataset.csv" if len(sys.argv) < 2 else sys.argv[1]
    audit_dataset(path)
