using RutasDBUS.Modelos.RedBus;

namespace RutasDBUS.Servicios.Planificacion;

/// <summary>
/// Indice compartido de paradas, patrones y transferencias del motor de transito.
/// </summary>
public sealed class IndiceTransitoDbus
{
    private readonly Dictionary<(int cx, int cy), List<Parada>> _gridParadas;
    private readonly int _escalaGrid;

    /// <summary>
    /// Inicializa una nueva instancia del indice.
    /// </summary>
    public IndiceTransitoDbus(
        IReadOnlyDictionary<string, Parada> paradasPorCodigo,
        IReadOnlyList<PatronTransitoDbus> patrones,
        IReadOnlyDictionary<string, List<ReferenciaPatronParadaDbus>> patronesPorParada,
        IReadOnlyDictionary<string, List<TransferenciaPeatonalDbus>> transferenciasPorParada,
        Dictionary<(int cx, int cy), List<Parada>> gridParadas,
        int escalaGrid)
    {
        ParadasPorCodigo = paradasPorCodigo;
        Patrones = patrones;
        PatronesPorParada = patronesPorParada;
        TransferenciasPorParada = transferenciasPorParada;
        _gridParadas = gridParadas;
        _escalaGrid = escalaGrid;
    }

    /// <summary>
    /// Paradas cargadas por codigo.
    /// </summary>
    public IReadOnlyDictionary<string, Parada> ParadasPorCodigo { get; }

    /// <summary>
    /// Patrones transitables construidos desde expediciones reales.
    /// </summary>
    public IReadOnlyList<PatronTransitoDbus> Patrones { get; }

    /// <summary>
    /// Patrones que pasan por cada parada.
    /// </summary>
    public IReadOnlyDictionary<string, List<ReferenciaPatronParadaDbus>> PatronesPorParada { get; }

    /// <summary>
    /// Transferencias a pie entre paradas cercanas.
    /// </summary>
    public IReadOnlyDictionary<string, List<TransferenciaPeatonalDbus>> TransferenciasPorParada { get; }

    /// <summary>
    /// Devuelve las paradas cercanas a unas coordenadas.
    /// </summary>
    public List<Parada> QueryParadasEnRadio(double latitud, double longitud, int radioMetros)
    {
        var deg = Math.Max(0.0005, radioMetros / 111_000.0);
        var paso = Math.Max(1, (int)Math.Ceiling(deg * _escalaGrid));
        var celda = Celda(latitud, longitud, _escalaGrid);
        var resultado = new List<Parada>(128);

        for (var dx = -paso; dx <= paso; dx++)
        {
            for (var dy = -paso; dy <= paso; dy++)
            {
                if (_gridParadas.TryGetValue((celda.cx + dx, celda.cy + dy), out var paradas))
                    resultado.AddRange(paradas);
            }
        }

        return resultado;
    }

    private static (int cx, int cy) Celda(double latitud, double longitud, int escala)
        => ((int)Math.Floor(latitud * escala), (int)Math.Floor(longitud * escala));
}

/// <summary>
/// Patron de viaje con su secuencia de paradas y sus expediciones.
/// </summary>
public sealed class PatronTransitoDbus
{
    /// <summary>
    /// Identificador estable del patron.
    /// </summary>
    public string Id { get; init; } = "";

    /// <summary>
    /// Identificador del patron de expedicion del ZIP.
    /// </summary>
    public string IdPatronExp { get; init; } = "";

    /// <summary>
    /// Identificador bruto de la ruta del ZIP.
    /// </summary>
    public string IdRuta { get; init; } = "";

    /// <summary>
    /// Codigo visible de la linea.
    /// </summary>
    public string CodigoLinea { get; init; } = "";

    /// <summary>
    /// Codigo numerico de la linea si existe.
    /// </summary>
    public int? NumeroLinea { get; init; }

    /// <summary>
    /// Codigo de itinerario.
    /// </summary>
    public int CodigoItinerario { get; init; }

    /// <summary>
    /// Sentido normalizado.
    /// </summary>
    public string Sentido { get; init; } = "";

    /// <summary>
    /// Destino descriptivo.
    /// </summary>
    public string Destino { get; init; } = "";

    /// <summary>
    /// Variante de la red visual asociada al patron.
    /// </summary>
    public InfoVariante Variante { get; init; } = default!;

    /// <summary>
    /// Secuencia ordenada de codigos de parada.
    /// </summary>
    public IReadOnlyList<string> CodigosParada { get; init; } = Array.Empty<string>();

    /// <summary>
    /// Distancia acumulada por parada para calculos aproximados.
    /// </summary>
    public IReadOnlyList<double> DistanciaAcumuladaPorParadaMetros { get; init; } = Array.Empty<double>();

    /// <summary>
    /// Expediciones que circulan por este patron.
    /// </summary>
    public List<ExpedicionTransitoDbus> Expediciones { get; } = new();
}

/// <summary>
/// Expedicion concreta de un patron.
/// </summary>
public sealed class ExpedicionTransitoDbus
{
    /// <summary>
    /// Identificador de la expedicion.
    /// </summary>
    public string Id { get; init; } = "";

    /// <summary>
    /// Calendario asociado.
    /// </summary>
    public string IdCalendario { get; init; } = "";

    /// <summary>
    /// Tiempos de llegada absolutos respecto al inicio del dia de servicio.
    /// </summary>
    public IReadOnlyList<int> LlegadasSegundos { get; init; } = Array.Empty<int>();

    /// <summary>
    /// Tiempos de salida absolutos respecto al inicio del dia de servicio.
    /// </summary>
    public IReadOnlyList<int> SalidasSegundos { get; init; } = Array.Empty<int>();
}

/// <summary>
/// Referencia de una parada dentro de un patron.
/// </summary>
public sealed class ReferenciaPatronParadaDbus
{
    /// <summary>
    /// Patron al que pertenece la parada.
    /// </summary>
    public PatronTransitoDbus Patron { get; init; } = default!;

    /// <summary>
    /// Indice de la parada dentro del patron.
    /// </summary>
    public int IndiceParada { get; init; }
}

/// <summary>
/// Conexion peatonal entre dos paradas.
/// </summary>
public sealed class TransferenciaPeatonalDbus
{
    /// <summary>
    /// Codigo de la parada destino.
    /// </summary>
    public string CodigoParadaDestino { get; init; } = "";

    /// <summary>
    /// Distancia aproximada de la transferencia.
    /// </summary>
    public double DistanciaMetros { get; init; }
}
