"""Generate 3-word IDs using compressed-index sampling."""

import random
import secrets
from typing import Callable

from three_dice_words.wordlist import load_wordlist


def _generate_indices(
    n: int,
    randbelow: Callable[[int], int],
) -> tuple[int, int, int]:
    """Generate 3 distinct indices in [0, n) using compressed-index remap.

    Properties: uniform over ordered triples; collision-free within the 3-word
    output; no retry loop; faster than set/rejection approaches.
    """
    i1 = randbelow(n)

    i2 = randbelow(n - 1)
    if i2 >= i1:
        i2 += 1

    a, b = min(i1, i2), max(i1, i2)

    i3 = randbelow(n - 2)
    if i3 >= a:
        i3 += 1
    if i3 >= b:
        i3 += 1

    return (i1, i2, i3)


def _randbelow(rng: random.Random, n: int) -> int:
    """Return random int in [0, n) using rng."""
    return rng.randrange(n)


def generate(
    words: list[str],
    *,
    rng: random.Random | None = None,
) -> str:
    """Generate a single 3-word hyphenated ID (no repeated words).

    Default uses secrets.randbelow (OS CSPRNG). Pass rng for tests or custom RNG.
    """
    randbelow: Callable[[int], int] = (
        secrets.randbelow if rng is None else lambda k: _randbelow(rng, k)
    )
    n = len(words)
    if n < 3:
        raise ValueError("wordlist must have at least 3 words")
    i1, i2, i3 = _generate_indices(n, randbelow)
    return f"{words[i1]}-{words[i2]}-{words[i3]}"


def generate_batch(
    words: list[str],
    count: int,
    *,
    rng: random.Random | None = None,
) -> list[str]:
    """Generate `count` 3-word IDs. Returns a list of strings in order.

    Default uses secrets.randbelow (OS CSPRNG). Pass rng for tests or custom RNG.
    """
    if count < 0:
        raise ValueError("count must be non-negative")
    if count == 0:
        return []
    randbelow: Callable[[int], int] = (
        secrets.randbelow if rng is None else lambda k: _randbelow(rng, k)
    )
    n = len(words)
    if n < 3:
        raise ValueError("wordlist must have at least 3 words")
    result: list[str] = []
    for _ in range(count):
        i1, i2, i3 = _generate_indices(n, randbelow)
        result.append(f"{words[i1]}-{words[i2]}-{words[i3]}")
    return result
