from collections import defaultdict
from pathlib import Path
from statistics import mean
from typing import Optional, Dict, Tuple

import numpy as np
import pandas as pd
from gym.spaces import Dict as GymDict
from numpy import ndarray

from containers.config import Config
from containers.constants import *
from containers.task_context import TaskContext
from containers.nammed_arrays import NamedArrays
from sumo.sumo import start_sumo
from sumo.traffic_state import TrafficState
from sumo.utils import is_rl


class Env:
    """
    Offers a similar reinforcement learning environment interface as gym.Env
    Wraps around a TrafficState (traffic_state) and the SUMO traci (traci)
    """

    def __init__(self, config: Config, obs_space: GymDict):
        self.config = config
        self.obs_space = obs_space
        self.traci = None
        self.traffic_state: Optional[TrafficState] = None
        self.rollout_info = NamedArrays()
        self._step = 0
        self.vehicle_metrics = defaultdict(lambda: defaultdict(lambda: []))
        self.uncontrolled_region_metrics = defaultdict(lambda: [])
        self.warmup_vehicles = set()

    def reset(self, prefix: str, task_context: TaskContext) -> Tuple[ndarray, ndarray]:
        """
        Custom Env class reset method.
        """
        self.task_context = task_context
        self.traci = start_sumo(self.config, self.traci, prefix, task_context)
        self.traffic_state = TrafficState(
            self.config,
            self.traci,
            self.task_context,
            self.config.working_dir / f'sumo/net{prefix}.net.xml')

        self.rollout_info = NamedArrays()
        self._step = 0
        self.vehicle_metrics = defaultdict(lambda: defaultdict(lambda: []))
        self.uncontrolled_region_metrics = defaultdict(lambda: [])
        self.warmup_vehicles = set()

        # IMPORTANT: one step in the simulation at the beginning.
        ids, obs, reward, _ = self.step(np.array([]), np.array([]), warmup=True)
        for i in range(self.config.warmup_steps):
            ids, obs, reward, _ = self.step(np.array([]), np.array([]), warmup=True)

        return ids, obs

    def step(self, ids: ndarray, action: ndarray, warmup: bool = False) -> Tuple[ndarray, ndarray, ndarray, bool]:
        """
        Override this with additional code which applies acceleration and measures observation and reward
        """
        self.traffic_state.step()
        self._step += 1
        self._collect_metrics(warmup)
        return None, None, None, False

    def close(self):
        """
        Closes the env.
        """
        self.traci.close()

    def get_average_speed(self) -> float:
        """
        Return the average speed of the vehicles in the main intersection. Does NOT work during warmup.
        """
        return mean(self.vehicle_metrics[v]['speed'][-1] for v in self.traffic_state.current_vehicles)

    def get_metrics(self) -> Dict[str, any]:
        """
        Supposed to be called at the end of the episode, returns the metric of the episode and resets the internal state
        """
        metrics = self._filtered_metrics()

        num_vehicles = len(metrics)
        
        approach_speed_means = []
        for _, series in metrics.items():
            if series['approach_speed'] != []:
                approach_speed_means.append(mean(series['approach_speed']))
        
        int_speed_means = []
        for _, series in metrics.items():
            if series['int_speed'] != []:
                int_speed_means.append(mean(series['int_speed']))
                
        leaving_speed_means = []
        for _, series in metrics.items():
            if series['leaving_speed'] != []:
                leaving_speed_means.append(mean(series['leaving_speed']))
                
        if approach_speed_means != []:
            approach_speed_mean = mean(approach_speed_means)
        else:
            approach_speed_mean = 10e6

        if int_speed_means != []:
            int_speed_mean = mean(int_speed_means)
        else:
            int_speed_mean = 10e6
            
        if leaving_speed_means != []:
            leaving_speed_mean = mean(leaving_speed_means)
        else:  
            leaving_speed_mean = 10e6
            
        aggregated_metrics = {
            'num_vehicle_completed': num_vehicles,
            'num_vehicle': sum(series['int_crossed'][-1] for _, series in metrics.items()),
            'vehicle_speed': mean(mean(series['speed'])
                                  for _, series in metrics.items()),
            'approach_vehicle_speed': approach_speed_mean,
            'int_vehicle_speed': int_speed_mean,
            'leaving_vehicle_speed': leaving_speed_mean,
            'stopping_time': mean(sum(1 for s in series['speed'] if s < 1) * self.config.sim_step_duration
                                  for _, series in metrics.items()),
            **{f'regular_vehicle_emission_{condition}': mean(sum(series[f'emission_{condition}_{REGULAR}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
               },
            **{f'electric_vehicle_emission_{condition}': mean(sum(series[f'emission_{condition}_{ELECTRIC}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == ELECTRIC
               },
            **{f'regular_approach_vehicle_emission_{condition}': mean(sum(series[f'approach_emission_{condition}_{REGULAR}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
               },
            **{f'regular_approach_vehicle_emission_idling_{condition}': mean(sum(series[f'approach_idling_emission_{condition}_{REGULAR}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
               },
            **{f'electric_approach_vehicle_emission_{condition}': mean(sum(series[f'approach_emission_{condition}_{ELECTRIC}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == ELECTRIC
               },
            **{f'electric_approach_vehicle_emission_idling_{condition}': mean(sum(series[f'approach_idling_emission_{condition}_{ELECTRIC}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == ELECTRIC
               },
            **{f'regular_leaving_vehicle_emission_{condition}': mean(sum(series[f'leaving_emission_{condition}_{REGULAR}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
               },
            **{f'regular_leaving_vehicle_emission_idling_{condition}': mean(sum(series[f'leaving_idling_emission_{condition}_{REGULAR}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
               },
            **{f'electric_leaving_vehicle_emission_{condition}': mean(sum(series[f'leaving_emission_{condition}_{ELECTRIC}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == ELECTRIC
               },
            **{f'electric_leaving_vehicle_emission_idling_{condition}': mean(sum(series[f'leaving_idling_emission_{condition}_{ELECTRIC}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == ELECTRIC
               },
            **{f'regular_intersection_vehicle_emission_{condition}': mean(sum(series[f'intersection_emission_{condition}_{REGULAR}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
               },
            **{f'regular_intersection_vehicle_emission_idling_{condition}': mean(sum(series[f'intersection_idling_emission_{condition}_{REGULAR}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
               },
            **{f'electric_intersection_vehicle_emission_{condition}': mean(sum(series[f'intersection_emission_{condition}_{ELECTRIC}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == ELECTRIC
               },
            **{f'electric_intersection_vehicle_emission_idling_{condition}': mean(sum(series[f'intersection_idling_emission_{condition}_{ELECTRIC}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == ELECTRIC
               },
            **{f'effective_uncontrolled_regular_vehicle_emission_{condition}': mean(sum(series[f'effective_uncontrolled_emission_{condition}_{REGULAR}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
               },
            **{f'effective_uncontrolled_electric_vehicle_emission_{condition}': mean(sum(series[f'effective_uncontrolled_emission_{condition}_{ELECTRIC}'])
                                     for _, series in metrics.items())
               for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == ELECTRIC
               },
            'vehicle_fuel': mean(sum(series['fuel'])
                                 for _, series in metrics.items()),
            'vehicle_accel_squared': mean(mean(acc ** 2 for acc in series['accel'])
                                          for _, series in metrics.items()),
            'lane_changes': mean(sum(series['lane_change']) for _, series in metrics.items()),
            'removed_vehicles': sum(sum(series['removed']) for _, series in metrics.items())
        } if num_vehicles > 0 else {'num_vehicle': num_vehicles}

        if self.config.task_context.penetration_rate != 1 and len(
                [v_id for v_id in metrics if v_id.startswith('rl')]) > 0:
            
            rl_vehicle_accel_mean = []
            rl_vehicle_decel_mean = []
            rl_vehicle_decel_max = []
            rl_vehicle_accel_max = []
            rl_vehicle_stopping_time = []
            
            for v_id, series in metrics.items():
                if is_rl(v_id):
                    try:
                        acc = mean(acc for acc in series['accel'] if acc >= 0)
                        rl_vehicle_accel_mean.append(acc)
                    except:
                        continue
                    
                    try:
                        decel = mean(acc for acc in series['accel'] if acc < 0)
                        rl_vehicle_decel_mean.append(decel)
                    except:
                        continue
                    
                    try:
                        decel_max = min(acc for acc in series['accel'] if acc < 0)
                        rl_vehicle_decel_max.append(decel_max)
                    except:
                        continue
                    
                    try:
                        accel_max = max(acc for acc in series['accel'] if acc >= 0)
                        rl_vehicle_accel_max.append(accel_max)
                    except:
                        continue
                    
                    try:
                        stopping_time = sum(1 for s in series['speed'] if s < 1) * self.config.sim_step_duration
                        rl_vehicle_stopping_time.append(stopping_time)
                    except:
                        continue
                    
            aggregated_metrics.update({
                'rl_vehicle_speed': mean(mean(series['speed'])
                                         for v_id, series in metrics.items()
                                         if is_rl(v_id)),
                **{f'rl_vehicle_emission_{condition}': mean(sum(series[f'emission_{condition}_{REGULAR}'])
                                         for v_id, series in metrics.items()
                                         if is_rl(v_id))
                   for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
                   },
               
                **{f'rl_approaching_vehicle_emission_{condition}': mean(sum(series[f'approach_emission_{condition}_{REGULAR}'])
                                         for v_id, series in metrics.items()
                                         if is_rl(v_id))
                   for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
                   },
               
                **{f'rl_leaving_vehicle_emission_{condition}': mean(sum(series[f'leaving_emission_{condition}_{REGULAR}'])
                                         for v_id, series in metrics.items()
                                         if is_rl(v_id))
                   for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
                   },
                
                **{f'rl_intersection_vehicle_emission_{condition}': mean(sum(series[f'intersection_emission_{condition}_{REGULAR}'])
                                         for v_id, series in metrics.items()
                                         if is_rl(v_id))
                   for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
                   },
                
                'rl_vehicle_fuel': mean(sum(series['fuel'])
                                        for v_id, series in metrics.items()
                                        if is_rl(v_id)),
                'rl_vehicle_accel_squared': mean(mean(acc ** 2 for acc in series['accel'])
                                                 for v_id, series in metrics.items()
                                                 if is_rl(v_id)),
            
            })
            
            if len(rl_vehicle_accel_mean) > 0:
                aggregated_metrics.update({'rl_vehicle_accel_mean': mean(rl_vehicle_accel_mean)})
            if len(rl_vehicle_decel_mean) > 0:
                aggregated_metrics.update({'rl_vehicle_decel_mean': mean(rl_vehicle_decel_mean)})
            if len(rl_vehicle_decel_max) > 0:
                aggregated_metrics.update({'rl_vehicle_decel_max': mean(rl_vehicle_decel_max)})
            if len(rl_vehicle_accel_max) > 0:
                aggregated_metrics.update({'rl_vehicle_accel_max': mean(rl_vehicle_accel_max)})

        if self.config.task_context.penetration_rate != 1 and len(
                [v_id for v_id in metrics if not v_id.startswith('rl')]) > 0:
            
            non_rl_vehicle_accel_mean = []
            non_rl_vehicle_decel_mean = []
            non_rl_vehicle_decel_max = []
            non_rl_vehicle_accel_max = []
            non_rl_vehicle_stopping_time = []
            
            for v_id, series in metrics.items():
                if not is_rl(v_id):
                    try:
                        acc = mean(acc for acc in series['accel'] if acc >= 0)
                        non_rl_vehicle_accel_mean.append(acc)
                    except:
                        continue
                    
                    try:
                        decel = mean(acc for acc in series['accel'] if acc < 0)
                        non_rl_vehicle_decel_mean.append(decel)
                    except:
                        continue
                    
                    try:
                        decel_max = min(acc for acc in series['accel'] if acc < 0)
                        non_rl_vehicle_decel_max.append(decel_max)
                    except:
                        continue
                    
                    try:
                        accel_max = max(acc for acc in series['accel'] if acc >= 0)
                        non_rl_vehicle_accel_max.append(accel_max)
                    except:
                        continue
                    
                    try:
                        stopping_time = sum(1 for s in series['speed'] if s < 1) * self.config.sim_step_duration
                        non_rl_vehicle_stopping_time.append(stopping_time)
                    except:
                        continue
                

            aggregated_metrics.update({
            
                'non_rl_vehicle_speed': mean(mean(series['speed'])
                                         for v_id, series in metrics.items()
                                         if not is_rl(v_id)),
                
                **{f'non_rl_vehicle_emission_{condition}': mean(sum(series[f'emission_{condition}_{REGULAR}'])
                                         for v_id, series in metrics.items()
                                         if not is_rl(v_id))
                   for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
                   },
                
                **{f'non_rl_approaching_vehicle_emission_{condition}': mean(sum(series[f'approach_emission_{condition}_{REGULAR}'])
                                         for v_id, series in metrics.items()
                                         if not is_rl(v_id))
                   for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
                   },
                
                **{f'non_rl_leaving_vehicle_emission_{condition}': mean(sum(series[f'leaving_emission_{condition}_{REGULAR}'])
                                         for v_id, series in metrics.items()
                                         if not is_rl(v_id))
                   for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
                   },
                
                **{f'non_rl_intersection_vehicle_emission_{condition}': mean(sum(series[f'intersection_emission_{condition}_{REGULAR}'])
                                         for v_id, series in metrics.items()
                                         if not is_rl(v_id))
                   for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
                   },
                
                'non_rl_vehicle_accel_squared': mean(mean(acc ** 2 for acc in series['accel'])
                                                 for v_id, series in metrics.items()
                                                 if not is_rl(v_id)),
            
            })
            if len(non_rl_vehicle_accel_mean) > 0:
                aggregated_metrics.update({'non_rl_vehicle_accel_mean': mean(non_rl_vehicle_accel_mean)})
            if len(non_rl_vehicle_decel_mean) > 0:
                aggregated_metrics.update({'non_rl_vehicle_decel_mean': mean(non_rl_vehicle_decel_mean)})
            if len(non_rl_vehicle_decel_max) > 0:
                aggregated_metrics.update({'non_rl_vehicle_decel_max': mean(non_rl_vehicle_decel_max)})
            if len(non_rl_vehicle_accel_max) > 0:
                aggregated_metrics.update({'non_rl_vehicle_accel_max': mean(non_rl_vehicle_accel_max)})
            
        if self.config.report_uncontrolled_region_metrics and num_vehicles > 0:
            aggregated_metrics.update({
                'uncontrolled_waiting': sum(self.uncontrolled_region_metrics['waiting']) / num_vehicles,
                **{f'regular_uncontrolled_emission_{condition}': mean(sum(series[f'emission_{condition}_{REGULAR}'])
                                     for _, series in metrics.items())
                for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == REGULAR
                },
                **{f'electric_uncontrolled_emission_{condition}': mean(sum(series[f'emission_{condition}_{REGULAR}'])
                                     for _, series in metrics.items())
                for i, condition in enumerate(self.config.moves_emissions_models) if self.config.moves_emissions_models_conditions[i] == ELECTRIC
                },
            })

        return aggregated_metrics

    def export_moves_csv(self, file: Path) -> None:
        """
        Export run data to the format used by the MOVES script.
        """
        metrics = self._filtered_metrics()

        def keep_point(index: int):
            step_duration = self.config.sim_step_duration
            timing = index * step_duration
            return int(timing) < int(timing + step_duration)

        pd.concat(
            [pd.DataFrame({
                'trajectory_id': i,
                'drive_cycle': str([s
                                    for j, s in enumerate(vehicle_metrics['speed'])
                                    if keep_point(j)]),
                'grade': mean(vehicle_metrics['slope']),
                'vehicle_type': 21,
                'age': 5,
            }, index=[i])
                for i, vehicle_metrics in enumerate(metrics.values())]) \
            .to_csv(file)

    def _filtered_metrics(self) -> dict:
        """
        Returns the metrics for vehicles outside the warmup and who completely crossed the intersection.
        """
        return {
            v_id: series for v_id, series in self.vehicle_metrics.items()
            if (v_id not in self.warmup_vehicles
                and self.traffic_state.vehicles[v_id] in self.traffic_state.completed_vehicle)
        }

    def _collect_metrics(self, warmup) -> None:
        """
        Supposed to be called at each step, updates the internal metrics
        """
        if warmup:
            for v_id in self.traffic_state.current_vehicles:
                self.warmup_vehicles.add(v_id)
        else:
            for v_id, vehicle in self.traffic_state.current_vehicles.items():
                self.vehicle_metrics[v_id]['speed'].append(vehicle.speed)
                if vehicle.lane_id.startswith("A2TL") or vehicle.lane_id.startswith("B2TL") \
                                or vehicle.lane_id.startswith("C2TL") or vehicle.lane_id.startswith("D2TL") \
                                    or vehicle.lane_id.startswith("E2TL") or vehicle.lane_id.startswith("F2TL"):
                    self.vehicle_metrics[v_id]['approach_speed'].append(vehicle.speed)
                elif vehicle.lane_id.startswith("TL"):
                    self.vehicle_metrics[v_id]['int_speed'].append(vehicle.speed)
                else:
                    self.vehicle_metrics[v_id]['leaving_speed'].append(vehicle.speed)
                    
                self.vehicle_metrics[v_id]['accel'].append(vehicle.accel)
                self.vehicle_metrics[v_id]['fuel'].append(vehicle.fuel_consumption)

                if vehicle.lane_id.startswith("A2TL") or vehicle.lane_id.startswith("B2TL") \
                                or vehicle.lane_id.startswith("C2TL") or vehicle.lane_id.startswith("D2TL") \
                                    or vehicle.lane_id.startswith("E2TL") or vehicle.lane_id.startswith("F2TL"):
                    self.vehicle_metrics[v_id]['int_crossed'] = [0]
                else:
                    self.vehicle_metrics[v_id]['int_crossed'] = [1]

                if vehicle.has_changed_lane:
                    self.vehicle_metrics[v_id]['lane_change'].append(1)
                if vehicle.closest_optim_lane_distance is not None:
                    self.vehicle_metrics[v_id]['removed'] = [0]
                for condition, emission in vehicle.co2_emission.items():
                    self.vehicle_metrics[v_id][f'emission_{condition}'].append(emission)
                    if vehicle.lane_id.startswith("A2TL") or vehicle.lane_id.startswith("B2TL") \
                                or vehicle.lane_id.startswith("C2TL") or vehicle.lane_id.startswith("D2TL") \
                                    or vehicle.lane_id.startswith("E2TL") or vehicle.lane_id.startswith("F2TL"):
                        self.vehicle_metrics[v_id][f'approach_emission_{condition}'].append(emission)
                        if vehicle.speed < 0.1:
                            self.vehicle_metrics[v_id][f'approach_idling_emission_{condition}'].append(emission)
                        elif vehicle.accel > 0:
                            self.vehicle_metrics[v_id][f'approach_accelaration_emission_{condition}'].append(emission)
                        elif vehicle.accel < 0:
                            self.vehicle_metrics[v_id][f'approach_decelaration_emission_{condition}'].append(emission)
                        else:
                            self.vehicle_metrics[v_id][f'approach_const_speed_emission_{condition}'].append(emission)
                    elif vehicle.lane_id.startswith("TL"):
                        self.vehicle_metrics[v_id][f'leaving_emission_{condition}'].append(emission)
                        if vehicle.speed < 0.1:
                            self.vehicle_metrics[v_id][f'leaving_idling_emission_{condition}'].append(emission)
                        elif vehicle.accel > 0:
                            self.vehicle_metrics[v_id][f'leaving_accelaration_emission_{condition}'].append(emission)
                        elif vehicle.accel < 0:
                            self.vehicle_metrics[v_id][f'leaving_decelaration_emission_{condition}'].append(emission)
                        else:
                            self.vehicle_metrics[v_id][f'leaving_const_speed_emission_{condition}'].append(emission)
                    else:
                        self.vehicle_metrics[v_id][f'intersection_emission_{condition}'].append(emission)
                        if vehicle.speed < 0.1:
                            self.vehicle_metrics[v_id][f'intersection_idling_emission_{condition}'].append(emission)
                        elif vehicle.accel > 0:
                            self.vehicle_metrics[v_id][f'intersection_accelaration_emission_{condition}'].append(emission)
                        elif vehicle.accel < 0:
                            self.vehicle_metrics[v_id][f'intersection_decelaration_emission_{condition}'].append(emission)
                        else:
                            self.vehicle_metrics[v_id][f'intersection_const_speed_emission_{condition}'].append(emission)
                    
                if (vehicle.lane_id.startswith("A2TL") or vehicle.lane_id.startswith("B2TL") \
                            or vehicle.lane_id.startswith("C2TL") or vehicle.lane_id.startswith("D2TL") \
                                or vehicle.lane_id.startswith("E2TL") or vehicle.lane_id.startswith("F2TL")):
                    if self.vehicle_metrics[v_id]['start_speed'] == []:
                        self.vehicle_metrics[v_id]['start_speed'].append(vehicle.speed)
                self.vehicle_metrics[v_id]['slope'].append(vehicle.slope)
            
            for v_id, vehicle in self.traffic_state.vehicles.items():
                if (vehicle not in self.traffic_state.completed_vehicle
                    and v_id not in self.traffic_state.current_vehicles
                    and vehicle not in self.warmup_vehicles):
                    for condition, emission in vehicle.co2_emission.items():
                        self.vehicle_metrics[v_id][f'effective_uncontrolled_emission_{condition}'].append(emission)

            if self.config.report_uncontrolled_region_metrics:
                blocked_vehicles_number = len(self.traci.simulation.getPendingVehicles())
                self.uncontrolled_region_metrics['waiting'].append(blocked_vehicles_number)

                self.uncontrolled_region_metrics['fuel'].append(
                    blocked_vehicles_number * 0)

                for v_id, vehicle in self.traffic_state.vehicles.items():
                    if (vehicle not in self.traffic_state.completed_vehicle
                            and v_id not in self.traffic_state.current_vehicles
                            and vehicle not in self.warmup_vehicles):
                        self.uncontrolled_region_metrics['waiting'][-1] += 1
                        self.uncontrolled_region_metrics['fuel'][-1] += vehicle.fuel_consumption
                        for condition, emission in vehicle.co2_emission.items():
                            self.uncontrolled_region_metrics[f'emission_{condition}'].append(emission)