package sd.util;

import java.util.List;
import java.util.Map;

import sd.config.SimulationConfig;
import sd.model.Vehicle;
import sd.model.VehicleType;
import sd.routing.RouteSelector;

/**
 * Motor de injeção de carga (Load Injector) para a simulação de tráfego.
 * <p>
 * Esta classe atua como uma fábrica estocástica de veículos, sendo responsável por:
 * <ol>
 * <li><b>Modelagem Temporal:</b> Determinar os instantes de chegada (Inter-arrival times)
 * usando processos de Poisson (estocástico) ou intervalos determinísticos.</li>
 * <li><b>Caracterização da Entidade:</b> Atribuir tipos de veículo (Bike, Light, Heavy)
 * baseado numa Distribuição de Probabilidade Cumulativa (CDF).</li>
 * <li><b>Inicialização Espacial:</b> Distribuir a carga uniformemente entre os pontos de entrada (E1-E3).</li>
 * <li><b>Atribuição de Rota:</b> Delegar a escolha do percurso à estratégia {@link RouteSelector} ativa.</li>
 * </ol>
 */
public class VehicleGenerator {
    
    private final SimulationConfig config;
    private final String arrivalModel;
    
    /** Parâmetro Lambda (λ) para a distribuição de Poisson (taxa de chegada). */
    private final double arrivalRate;
    
    /** Intervalo determinístico para geração constante (modo debug/teste). */
    private final double fixedInterval;
    
    /** * Estratégia de roteamento atual. 
     * Não é final para permitir Hot-Swapping durante a execução.
     */
    private RouteSelector routeSelector;
    
    /**
     * Inicializa o gerador com as configurações de simulação e estratégia de roteamento.
     *
     * @param config A configuração global contendo as taxas e probabilidades.
     * @param routeSelector A estratégia inicial de seleção de rotas.
     */
    public VehicleGenerator(SimulationConfig config, RouteSelector routeSelector) {
        this.config = config;
        this.routeSelector = routeSelector;
        
        // Cache de valores de configuração para evitar lookups repetitivos em hot-path
        this.arrivalModel = config.getArrivalModel();
        this.arrivalRate = config.getArrivalRate();
        this.fixedInterval = config.getFixedArrivalInterval();
    }
    
    /**
     * Calcula o timestamp absoluto para a próxima injeção de veículo.
     * <p>
     * Se o modelo for "POISSON", utiliza a técnica de <i>Inverse Transform Sampling</i>
     * (via {@link RandomGenerator}) para gerar intervalos exponencialmente distribuídos,
     * simulando a aleatoriedade natural do tráfego.
     * * @param currentTime O tempo atual da simulação (base de cálculo).
     * @return O instante futuro (t + delta) para agendamento do evento de geração.
     */
    public double getNextArrivalTime(double currentTime) {
        if ("POISSON".equalsIgnoreCase(arrivalModel)) {
            double interval = RandomGenerator.generateExponentialInterval(arrivalRate);
            return currentTime + interval;
        } else {
            return currentTime + fixedInterval;
        }
    }
    
    /**
     * Instancia (Spawn) um novo veículo configurado e roteado.
     * <p>
     * O processo de criação segue um pipeline:
     * <ol>
     * <li>Seleção de Tipo (Roda da Fortuna / CDF).</li>
     * <li>Seleção de Entrada (Uniforme).</li>
     * <li>Cálculo de Rota (Delegado ao Strategy).</li>
     * </ol>
     *
     * @param vehicleId O identificador único sequencial (ex: "V104").
     * @param entryTime O timestamp de criação.
     * @param queueSizes Snapshot atual das filas (usado apenas por estratégias dinâmicas como LEAST_CONGESTED).
     * @return A entidade {@link Vehicle} pronta para inserção na malha.
     */
    public Vehicle generateVehicle(String vehicleId, double entryTime, Map<String, Integer> queueSizes) {
        VehicleType type = selectVehicleType();
        String entryPoint = selectRandomEntryPoint();
        List<String> route = routeSelector.selectRoute(entryPoint, queueSizes);
        
        return new Vehicle(vehicleId, type, entryTime, route);
    }
    
    /**
     * Seleciona o tipo de veículo usando Amostragem por Probabilidade Cumulativa.
     * <p>
     * Normaliza as probabilidades configuradas e mapeia um número aleatório [0, 1)
     * para o intervalo correspondente ao tipo de veículo.
     *
     * @return O tipo enumerado {@link VehicleType} selecionado.
     */
    private VehicleType selectVehicleType() {
        double bikeProbability = config.getBikeVehicleProbability();
        double lightProbability = config.getLightVehicleProbability();
        double heavyProbability = config.getHeavyVehicleProbability();
        
        double total = bikeProbability + lightProbability + heavyProbability;
        if (total == 0) return VehicleType.LIGHT; // Fallback de segurança
        
        // Normalização
        bikeProbability /= total;
        lightProbability /= total;
        
        double rand = Math.random();
        
        if (rand < bikeProbability) {
            return VehicleType.BIKE;
        } else if (rand < bikeProbability + lightProbability) {
            return VehicleType.LIGHT;
        } else {
            return VehicleType.HEAVY;
        }
    }
    
    /**
     * Seleciona um ponto de injeção na borda da rede (Edge Node).
     * Distribuição Uniforme: ~33.3% para cada entrada (E1, E2, E3).
     *
     * @return O ID da interseção de entrada.
     */
    private String selectRandomEntryPoint() {
        double rand = Math.random();
        
        if (rand < 0.333) {
            return "E1";
        } else if (rand < 0.666) {
            return "E2";
        } else {
            return "E3";
        }
    }
    
    /**
     * Atualiza a estratégia de roteamento em tempo de execução (Hot-Swap).
     * <p>
     * Permite que o Coordenador altere o comportamento da frota (ex: de RANDOM para SHORTEST_PATH)
     * sem necessidade de reiniciar a simulação.
     * * @param newRouteSelector A nova implementação de estratégia a utilizar.
     */
    public void setRouteSelector(RouteSelector newRouteSelector) {
        this.routeSelector = newRouteSelector;
    }

    /**
     * Retorna uma representação textual do estado interno do gerador.
     * Útil para logs de auditoria e debugging.
     */
    public String getInfo() {
        return String.format(
            "VehicleGenerator{model=%s, rate=%.2f, interval=%.2f, routeSelector=%s}",
            arrivalModel, arrivalRate, fixedInterval, routeSelector.getClass().getSimpleName()
        );
    }
}