package sd.util;

import java.util.Random;

/**
 * Utilitário central de geração estocástica para a simulação.
 * <p>
 * Esta classe fornece primitivas para geração de números pseudo-aleatórios, abstraindo
 * a complexidade de distribuições estatísticas.
 * <p>
 * <b>Funcionalidades Principais:</b>
 * <ul>
 * <li><b>Modelagem de Poisson:</b> Geração de tempos entre chegadas usando distribuição exponencial inversa.</li>
 * <li><b>Amostragem Uniforme:</b> Geração de inteiros e doubles em intervalos fechados/abertos.</li>
 * <li><b>Decisão Probabilística:</b> Avaliação de eventos booleanos baseados em pesos (Bernoulli trials).</li>
 * <li><b>Determinismo:</b> Suporte a sementes (seeds) manuais para reprodutibilidade exata de cenários de teste.</li>
 * </ul>
 */
public class RandomGenerator {

    /** * Instância singleton estática do gerador PRNG (Pseudo-Random Number Generator).
     * Thread-safe (java.util.Random é sincronizado), embora possa haver contenção em alta concorrência.
     */
    private static final Random random = new Random();

    /**
     * Gera um intervalo de tempo seguindo uma Distribuição Exponencial.
     * <p>
     * Este método implementa o algoritmo de <i>Inverse Transform Sampling</i> para simular
     * um Processo de Poisson homogêneo. É fundamental para modelar a chegada natural de
     * veículos, onde eventos independentes ocorrem a uma taxa média constante.
     * <p>
     * <b>Fórmula Matemática:</b> {@code T = -ln(1 - U) / λ}
     * <br>Onde:
     * <ul>
     * <li>{@code U}: Variável aleatória uniforme no intervalo [0, 1).</li>
     * <li>{@code λ (lambda)}: Taxa média de eventos por unidade de tempo (ex: veículos/segundo).</li>
     * </ul>
     *
     * @param lambda A taxa média de chegada (λ > 0).
     * @return O intervalo de tempo (delta t) até o próximo evento, em segundos.
     */
    public static double generateExponentialInterval(double lambda) {
        return Math.log(1 - random.nextDouble()) / -lambda;
    }

    /**
     * Gera um número inteiro uniformemente distribuído no intervalo fechado {@code [min, max]}.
     *
     * @param min Limite inferior (inclusivo).
     * @param max Limite superior (inclusivo).
     * @return Um inteiro aleatório I tal que {@code min <= I <= max}.
     */
    public static int generateRandomInt(int min, int max) {
        return random.nextInt(max - min + 1) + min;
    }

    /**
     * Gera um número de ponto flutuante uniformemente distribuído no intervalo semi-aberto {@code [min, max)}.
     *
     * @param min Limite inferior (inclusivo).
     * @param max Limite superior (exclusivo).
     * @return Um double aleatório D tal que {@code min <= D < max}.
     */
    public static double generateRandomDouble(double min, double max) {
        return min + (max - min) * random.nextDouble();
    }

    /**
     * Realiza um teste de Bernoulli (Sim/Não) com uma probabilidade de sucesso especificada.
     * <p>
     * Utilizado para decisões de ramificação estocástica (ex: "Este veículo é um camião?").
     *
     * @param probability A probabilidade de retorno {@code true} (0.0 a 1.0).
     * @return {@code true} se o evento ocorrer, {@code false} caso contrário.
     */
    public static boolean occursWithProbability(double probability) {
        return random.nextDouble() < probability;
    }

    /**
     * Seleciona aleatoriamente um elemento de um array genérico (Amostragem Uniforme Discreta).
     *
     * @param <T> O tipo dos elementos no array.
     * @param array A população de onde escolher.
     * @return O elemento selecionado.
     * @throws IllegalArgumentException Se o array for nulo ou vazio.
     */
    public static <T> T chooseRandom(T[] array) {
        if (array == null || array.length == 0) {
            throw new IllegalArgumentException("Array cannot be null or empty.");
        }
        return array[random.nextInt(array.length)];
    }

    /**
     * Reinicializa a semente (seed) do gerador global.
     * <p>
     * <b>Importância Crítica:</b> Permite tornar a simulação determinística. Ao fixar a seed,
     * a sequência de números "aleatórios" gerada será idêntica em execuções subsequentes,
     * facilitando a depuração de race conditions ou lógica complexa.
     *
     * @param seed O valor da semente inicial (ex: timestamp ou constante).
     */
    public static void setSeed(long seed) {
        random.setSeed(seed);
    }
}