"""Provably fair verification using SHA3-384 + AES-CTR.

The roll is determined by:
1. HMAC = SHA3-384(server_secret, client_secret + ":" + str(nonce))
2. The first 8 bytes of the HMAC are used as a big-endian integer
3. Roll = integer / (2^64) → float in [0, 1)

The server_seed_hash is SHA3-384(server_secret), published before any bets,
so the server cannot change the secret after seeing the client_secret.
"""

import hashlib
import hmac
import struct


def compute_roll(server_secret: str, client_secret: str, nonce: int) -> float:
    """Compute the provably fair roll from the given seeds and nonce.

    Args:
        server_secret: The revealed server secret.
        client_secret: The client-provided secret for this bet.
        nonce: The bet nonce (increments per bet).

    Returns:
        A float in [0, 1) representing the dice roll.
    """
    message = f"{client_secret}:{nonce}".encode("utf-8")
    h = hmac.new(
        server_secret.encode("utf-8"),
        message,
        hashlib.sha3_384,
    ).digest()

    # First 8 bytes → unsigned 64-bit big-endian integer
    value = struct.unpack(">Q", h[:8])[0]
    return value / (2**64)


def verify_server_seed(server_secret: str, expected_hash: str) -> bool:
    """Verify that a server secret matches its published hash.

    Args:
        server_secret: The revealed server secret.
        expected_hash: The SHA3-384 hash that was published before betting.

    Returns:
        True if the hash matches.
    """
    actual = hashlib.sha3_384(server_secret.encode("utf-8")).hexdigest()
    return actual == expected_hash


def verify_bet(
    server_secret: str,
    client_secret: str,
    nonce: int,
    server_seed_hash: str,
    expected_roll: float,
    tolerance: float = 1e-6,
) -> bool:
    """Full verification of a bet's fairness.

    Args:
        server_secret: The revealed server secret.
        client_secret: The client secret used for this bet.
        nonce: The bet nonce.
        server_seed_hash: The hash published before betting began.
        expected_roll: The roll value returned by the server.
        tolerance: Floating point comparison tolerance.

    Returns:
        True if the bet is provably fair.
    """
    if not verify_server_seed(server_secret, server_seed_hash):
        return False

    computed = compute_roll(server_secret, client_secret, nonce)
    return abs(computed - expected_roll) < tolerance
