package sd.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Representa um veículo que se move pela rede de interseções.
 * 
 * <p>
 * Esta classe é o "gémeo digital" de um carro, mota ou camião.
 * Mantém toda a informação necessária:
 * </p>
 * <ul>
 * <li>Identificação e tipo do veículo</li>
 * <li>Rota completa a percorrer</li>
 * <li>Métricas de tempo (espera, travessia, total)</li>
 * </ul>
 * 
 * <p>
 * O objeto é serializado e enviado pela rede à medida que o veículo
 * se move entre processos distribuídos.
 * </p>
 */
public class Vehicle implements Serializable {
    private static final long serialVersionUID = 1L;

    /** Identificador único do veículo (ex: "V1", "V2") */
    private final String id;

    /** Tipo de veículo (BIKE, LIGHT, HEAVY) */
    private final VehicleType type;

    /** Tempo de simulação (em segundos) em que o veículo foi gerado */
    private final double entryTime;

    /**
     * Lista ordenada completa de destinos (IDs de interseções e saída "S").
     * Exemplo: ["Cr1", "Cr3", "S"]
     */
    private final List<String> route;

    /**
     * Índice que acompanha o progresso do veículo ao longo da {@link #route}.
     * {@code route.get(currentRouteIndex)} é o destino *atual* do veículo.
     */
    private int currentRouteIndex;

    /**
     * Tempo total acumulado (segundos) que o veículo passou à espera em semáforos
     * vermelhos
     */
    private double totalWaitingTime;

    /**
     * Tempo total acumulado (segundos) que o veículo passou a atravessar
     * interseções
     */
    private double totalCrossingTime;

    /**
     * Cria um novo veículo pronto para se fazer à estrada.
     * 
     * @param id        Identificador único (ex: "V1").
     * @param type      O tipo de veículo (determina velocidade/tamanho).
     * @param entryTime Quando este veículo entrou na simulação (segundos).
     * @param route     A lista ordenada de paragens (Interseções -> Saída).
     */
    public Vehicle(String id, VehicleType type, double entryTime, List<String> route) {
        this.id = id;
        this.type = type;
        this.entryTime = entryTime;
        this.route = new ArrayList<>(route);
        this.currentRouteIndex = 0;
        this.totalWaitingTime = 0.0;
        this.totalCrossingTime = 0.0;
    }

    /**
     * Move o GPS interno do veículo para o próximo destino.
     * 
     * Chame isto quando um veículo chega a uma interseção para atualizar para onde
     * deve ir a seguir.
     * 
     * @return true se houver mais paragens, false se a viagem terminou.
     */
    public boolean advanceRoute() {
        currentRouteIndex++;
        return currentRouteIndex < route.size();
    }

    /**
     * Obtém o destino atual (próxima interseção ou saída) para onde o veículo se
     * dirige.
     *
     * @return ID do destino atual (ex: "Cr1"), ou {@code null} se a rota terminou
     */
    public String getCurrentDestination() {
        return (currentRouteIndex < route.size()) ? route.get(currentRouteIndex) : null;
    }

    /**
     * Verifica se o veículo completou toda a sua rota.
     *
     * @return {@code true} se chegou ao fim da rota, {@code false} caso contrário
     */
    public boolean hasReachedEnd() {
        return currentRouteIndex >= route.size();
    }

    /** @return identificador único do veículo */
    public String getId() {
        return id;
    }

    /** @return tipo do veículo */
    public VehicleType getType() {
        return type;
    }

    /** @return tempo de simulação em que o veículo entrou no sistema */
    public double getEntryTime() {
        return entryTime;
    }

    /** @return cópia da rota completa do veículo */
    public List<String> getRoute() {
        return new ArrayList<>(route);
    }

    /** @return índice atual apontando para o destino do veículo na sua rota */
    public int getCurrentRouteIndex() {
        return currentRouteIndex;
    }

    /** @return tempo total acumulado de espera em segundos */
    public double getTotalWaitingTime() {
        return totalWaitingTime;
    }

    /**
     * Adiciona uma duração ao tempo total de espera do veículo.
     * Chamado quando um veículo começa a atravessar uma interseção.
     *
     * @param time duração (em segundos) a adicionar
     */
    public void addWaitingTime(double time) {
        totalWaitingTime += time;
    }

    /** @return tempo total acumulado de travessia em segundos */
    public double getTotalCrossingTime() {
        return totalCrossingTime;
    }

    /**
     * Adiciona uma duração ao tempo total de travessia do veículo.
     * Chamado quando um veículo termina de atravessar uma interseção.
     *
     * @param time duração (em segundos) a adicionar
     */
    public void addCrossingTime(double time) {
        totalCrossingTime += time;
    }

    /**
     * Calcula o tempo total que o veículo passou no sistema até agora.
     *
     * @param currentTime tempo atual da simulação
     * @return tempo total decorrido (em segundos) desde que o veículo foi gerado
     */
    public double getTotalTravelTime(double currentTime) {
        return currentTime - entryTime;
    }

    /** @return representação textual do estado atual do veículo */
    @Override
    public String toString() {
        return String.format(
                "Vehicle{id='%s', type=%s, next='%s', route=%s}",
                id, type, getCurrentDestination(), route);
    }
}