package sd.routing;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Implementação da política de roteamento aleatória (baseline).
 * 
 * <p>Esta política seleciona rotas com base em probabilidades predefinidas,
 * sem considerar o estado atual da rede. É a implementação de referência
 * para comparação com outras políticas.</p>
 * 
 * <p>As rotas são organizadas por ponto de entrada (E1, E2, E3) e cada rota
 * tem uma probabilidade de seleção associada.</p>
 */
public class RandomRouteSelector implements RouteSelector {
    
    /** Rotas possíveis a partir do ponto de entrada E1 */
    private final List<RouteWithProbability> e1Routes;
    /** Rotas possíveis a partir do ponto de entrada E2 */
    private final List<RouteWithProbability> e2Routes;
    /** Rotas possíveis a partir do ponto de entrada E3 */
    private final List<RouteWithProbability> e3Routes;
    
    /**
     * Cria um novo seletor de rotas aleatórias com rotas predefinidas.
     */
    public RandomRouteSelector() {
        this.e1Routes = new ArrayList<>();
        this.e2Routes = new ArrayList<>();
        this.e3Routes = new ArrayList<>();
        initializePossibleRoutes();
    }
    
    /**
     * Define todas as rotas possíveis que os veículos podem tomar.
     * As rotas são organizadas por ponto de entrada (E1, E2, E3).
     * Cada rota tem uma probabilidade que determina a frequência com que é escolhida.
     */
    private void initializePossibleRoutes() {
        // Rotas de E1 (entrada Norte)
        e1Routes.add(new RouteWithProbability(
            Arrays.asList("Cr1", "Cr4", "Cr5", "S"), 0.34));
        e1Routes.add(new RouteWithProbability(
            Arrays.asList("Cr1", "Cr2", "Cr5", "S"), 0.33));
        e1Routes.add(new RouteWithProbability(
            Arrays.asList("Cr1", "Cr2", "Cr3", "S"), 0.33));
        
        // Rotas de E2 (entrada Oeste)
        e2Routes.add(new RouteWithProbability(
            Arrays.asList("Cr2", "Cr5", "S"), 0.34));
        e2Routes.add(new RouteWithProbability(
            Arrays.asList("Cr2", "Cr3", "S"), 0.33));
        e2Routes.add(new RouteWithProbability(
            Arrays.asList("Cr2", "Cr1", "Cr4", "Cr5", "S"), 0.33));
        
        // Rotas de E3 (entrada Sul)
        e3Routes.add(new RouteWithProbability(
            Arrays.asList("Cr3", "S"), 0.34));
        e3Routes.add(new RouteWithProbability(
            Arrays.asList("Cr3", "Cr2", "Cr5", "S"), 0.33));
        e3Routes.add(new RouteWithProbability(
            Arrays.asList("Cr3", "Cr2", "Cr1", "Cr4", "Cr5", "S"), 0.33));
    }
    
    @Override
    public List<String> selectRoute(String entryPoint, Map<String, Integer> queueSizes) {
        // Ignora queueSizes - seleção aleatória não depende do estado da rede
        
        List<RouteWithProbability> selectedRoutes = getRoutesForEntryPoint(entryPoint);
        
        // Seleciona uma rota baseada em probabilidades cumulativas
        double rand = Math.random();
        double cumulative = 0.0;
        
        for (RouteWithProbability routeWithProb : selectedRoutes) {
            cumulative += routeWithProb.probability;
            if (rand <= cumulative) {
                // Retorna uma cópia da rota para prevenir modificações
                return new ArrayList<>(routeWithProb.route);
            }
        }
        
        // Fallback: retorna a primeira rota
        return new ArrayList<>(selectedRoutes.get(0).route);
    }
    
    /**
     * Obtém as rotas disponíveis para um ponto de entrada.
     * 
     * @param entryPoint ponto de entrada (E1, E2 ou E3)
     * @return lista de rotas com probabilidades
     */
    private List<RouteWithProbability> getRoutesForEntryPoint(String entryPoint) {
        switch (entryPoint.toUpperCase()) {
            case "E1":
                return e1Routes;
            case "E2":
                return e2Routes;
            case "E3":
                return e3Routes;
            default:
                System.err.printf("Unknown entry point: %s, defaulting to E1%n", entryPoint);
                return e1Routes;
        }
    }
    
    /**
     * Classe interna para associar uma rota com sua probabilidade de seleção.
     */
    private static class RouteWithProbability {
        final List<String> route;
        final double probability;
        
        RouteWithProbability(List<String> route, double probability) {
            this.route = route;
            this.probability = probability;
        }
    }
}
