"""seedlock.py — Deterministic RNG wrappers for all SilverSight shims.

Usage:
    from seedlock import SeededRNG, lock
    
    lock(42)  # called once at program start
    rng = SeededRNG(42)
    rng.random()    # reproducible Python random
    rng.np_random() # reproducible NumPy random

Enforcement:
    After lock(), direct random.seed() and np.random.seed() raise RuntimeError.
"""

from __future__ import annotations

import os
import random
import sys
from typing import Optional
import numpy as np

_LOCKED = False
_GLOBAL_SEED: int = 0


class SeededRNG:
    """Deterministic RNG using Python's random.Random as the canonical source.

    Python's random.Random is specified by CPython and stable across platforms.
    NumPy's Generator may differ by version — NumPy is only used for array ops.
    """

    def __init__(self, seed: int = 0):
        self._seed = seed
        self._py_rng = random.Random(seed)
        self._np_rng = np.random.default_rng(seed)

    def random(self) -> float:
        return self._py_rng.random()

    def randint(self, a: int, b: int) -> int:
        return self._py_rng.randint(a, b)

    def choice(self, seq):
        return self._py_rng.choice(seq)

    def shuffle(self, lst: list) -> None:
        self._py_rng.shuffle(lst)

    def np_random(self) -> np.random.Generator:
        return self._np_rng

    def seed(self, new_seed: int) -> None:
        self._seed = new_seed
        self._py_rng = random.Random(new_seed)
        self._np_rng = np.random.default_rng(new_seed)


def lock(seed: int = 0):
    """Lock global RNG. After this call, direct unseeded RNG calls raise RuntimeError."""
    global _LOCKED, _GLOBAL_SEED
    _GLOBAL_SEED = seed
    _LOCKED = True
    random.seed = _forbidden_seed  # type: ignore
    # NumPy's random module may not have seed attribute in newer versions
    try:
        np.random.seed = _forbidden_seed  # type: ignore
    except AttributeError:
        pass


def is_locked() -> bool:
    return _LOCKED


def global_seed() -> int:
    return _GLOBAL_SEED


def _forbidden_seed(*args, **kwargs):
    raise RuntimeError(
        "Direct random.seed() call detected. "
        "Use seedlock.SeededRNG(seed) for explicit seeding. "
        "Layer 0 determinism violation."
    )
