from __future__ import annotations

import secrets
import string
import uuid
from dataclasses import dataclass


@dataclass(frozen=True, slots=True)
class RandomConfig:
    alphabet: str = string.ascii_letters + string.digits
    urlsafe_default_bytes: int = 32

class Random:
    def __init__(self, config: RandomConfig | None = None) -> None:
        self.config = config or RandomConfig()

    def bytes(self, n: int) -> bytes:
        if not isinstance(n, int) or n <= 0:
            raise ValueError("n must be a positive integer")
        return secrets.token_bytes(n)

    def hex(self, n: int) -> str:
        if not isinstance(n, int) or n <= 0:
            raise ValueError("n must be a positive integer")
        return secrets.token_hex(n)

    def token(self, n: int | None = None) -> str:
        if n is None:
            n = self.config.urlsafe_default_bytes
        if not isinstance(n, int) or n <= 0:
            raise ValueError("n must be a positive integer")
        return secrets.token_urlsafe(n)

    def string(self, length: int, *, alphabet: str | None = None) -> str:
        if not isinstance(length, int) or length <= 0:
            raise ValueError("length must be a positive integer")
        alphabet = alphabet or self.config.alphabet
        if not alphabet:
            raise ValueError("alphabet must not be empty")
        return "".join(secrets.choice(alphabet) for _ in range(length))

    def integer(self, min_value: int, max_value: int) -> int:
        if not isinstance(min_value, int) or not isinstance(max_value, int):
            raise TypeError("min_value and max_value must be integers")
        if min_value > max_value:
            raise ValueError("min_value cannot be greater than max_value")
        return min_value + secrets.randbelow(max_value - min_value + 1)

    def uuid4(self) -> str:
        return str(uuid.uuid4())