using System.Globalization;
using System.Text.Json;
using Microsoft.JSInterop;

namespace RutasDBUS.Components.Pages;

public partial class PlanificadorRutas
{
    private const string ClaveConfiguracionLocal = "rutasdbus.planificador.config.v1";
    private bool _configuracionLocalCargada = false;
    private bool _mostrarConfirmacionRestablecer = false;

    /// <summary>
    /// Instantánea persistida de la configuración del planificador.
    /// </summary>
    private sealed class ConfiguracionLocalPlanificador
    {
        public bool? MostrarParadas { get; set; }
        public bool UsarHorariosTeoricos { get; set; }
        public int SegundosPorParadaBusManual { get; set; }
        public bool ActivarRangoLineas { get; set; }
        public int LineaMin { get; set; }
        public int LineaMax { get; set; }
        public List<int>? LineasPermitidas { get; set; }
        public bool ActivarRangoItinerarios { get; set; }
        public int ItinerarioMin { get; set; }
        public int ItinerarioMax { get; set; }
        public bool ActivarRangoTransbordos { get; set; }
        public int MinTransbordosPermitidos { get; set; }
        public int MaxTransbordosPermitidos { get; set; }
        public bool UsarHoraSimulada { get; set; }
        public string? FechaSimulada { get; set; }
        public string? HoraSimulada { get; set; }
        public double? FactorPenalizacionDistanciaPie { get; set; }
        public double? ParametroPenalizacionDistanciaPieMetros { get; set; }
        public double? CoeficienteEsfuerzoAltura { get; set; }
        public double? PesoTransbordoRankingMinutos { get; set; }
        public double? FactorEquilibradoAndarBus { get; set; }
        public double? UmbralPendienteSubidaRuta { get; set; }
        public double? UmbralPendienteBajadaRuta { get; set; }
        public double VelocidadPieKmH { get; set; }
        public double VelocidadBusKmH { get; set; }
        public int ElevMaxPuntosPorTramo { get; set; }
        public int RadioCandidatasMetros { get; set; }
        public int MargenTransbordoSegundos { get; set; }
        public string? WalkProvider { get; set; }
        public int NivelBrilloMapa { get; set; }
        public bool UsarColorRealLineaEnItinerarios { get; set; }
        public bool UsarOsrmParaRanking { get; set; }
        public string? CriterioPrioritarioRuta { get; set; }
        public string? PreferenciaEtiquetaRuta { get; set; }
    }

    /// <summary>
    /// Carga la configuración persistida del navegador y la aplica al estado actual.
    /// </summary>
    private async Task CargarConfiguracionLocalAsync()
    {
        try
        {
            var json = await JS.InvokeAsync<string?>("rt.loadLocalValue", ClaveConfiguracionLocal);
            if (string.IsNullOrWhiteSpace(json))
                return;

            var config = JsonSerializer.Deserialize<ConfiguracionLocalPlanificador>(json);
            if (config is null)
                return;

            AplicarConfiguracionLocal(config);
        }
        catch (JSException)
        {
        }
        catch (JsonException)
        {
        }
        finally
        {
            _configuracionLocalCargada = true;
        }
    }

    /// <summary>
    /// Guarda la configuración actual del planificador en el navegador.
    /// </summary>
    private async Task PersistirConfiguracionAsync()
    {
        if (!_configuracionLocalCargada)
            return;

        try
        {
            if (!_usarHorariosTeoricos)
                _segundosPorParadaBusManual = Math.Max(0, _segundosPorParadaBus);

            var json = JsonSerializer.Serialize(CrearConfiguracionLocal());
            await JS.InvokeVoidAsync("rt.saveLocalValue", ClaveConfiguracionLocal, json);
        }
        catch (JSException)
        {
        }
    }

    /// <summary>
    /// Sincroniza el valor manual de segundos por parada y persiste la configuración.
    /// </summary>
    private async Task OnSegundosPorParadaBusChanged()
    {
        _segundosPorParadaBus = Math.Max(0, _segundosPorParadaBus);
        _segundosPorParadaBusManual = _segundosPorParadaBus;
        await PersistirConfiguracionAsync();
    }

    /// <summary>
    /// Construye la instantánea persistible a partir del estado actual.
    /// </summary>
    private ConfiguracionLocalPlanificador CrearConfiguracionLocal()
        => new()
        {
            MostrarParadas = _mostrarParadas,
            UsarHorariosTeoricos = _usarHorariosTeoricos,
            SegundosPorParadaBusManual = Math.Max(0, _segundosPorParadaBusManual),
            ActivarRangoLineas = _activarRangoLineas,
            LineaMin = _lineaMin,
            LineaMax = _lineaMax,
            LineasPermitidas = ObtenerLineasPermitidasSeleccionadas().ToList(),
            ActivarRangoItinerarios = _activarRangoItinerarios,
            ItinerarioMin = _itinerarioMin,
            ItinerarioMax = _itinerarioMax,
            ActivarRangoTransbordos = _activarRangoTransbordos,
            MinTransbordosPermitidos = _minTransbordosPermitidos,
            MaxTransbordosPermitidos = _maxTransbordosPermitidos,
            UsarHoraSimulada = _usarHoraSimulada,
            FechaSimulada = _fechaSimulada.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
            HoraSimulada = _horaSimulada.ToString("HH:mm:ss", CultureInfo.InvariantCulture),
            FactorPenalizacionDistanciaPie = _factorPenalizacionDistanciaPie,
            ParametroPenalizacionDistanciaPieMetros = _parametroPenalizacionDistanciaPieMetros,
            CoeficienteEsfuerzoAltura = _coeficienteEsfuerzoAltura,
            PesoTransbordoRankingMinutos = _pesoTransbordoRankingMinutos,
            FactorEquilibradoAndarBus = _factorEquilibradoAndarBus,
            UmbralPendienteSubidaRuta = _umbralPendienteSubidaRuta,
            UmbralPendienteBajadaRuta = _umbralPendienteBajadaRuta,
            VelocidadPieKmH = _velocidadPieKmH,
            VelocidadBusKmH = _velocidadBusKmH,
            ElevMaxPuntosPorTramo = _elevMaxPuntosPorTramo,
            RadioCandidatasMetros = _radioCandidatasMetros,
            MargenTransbordoSegundos = _margenTransbordoSegundos,
            WalkProvider = _walkProvider.ToString(),
            NivelBrilloMapa = _nivelBrilloMapa,
            UsarColorRealLineaEnItinerarios = _usarColorRealLineaEnItinerarios,
            UsarOsrmParaRanking = _usarOsrmParaRanking,
            CriterioPrioritarioRuta = _criterioPrioritarioRuta,
            PreferenciaEtiquetaRuta = _preferenciaEtiquetaRuta
        };

    /// <summary>
    /// Aplica al componente una configuración previamente persistida.
    /// </summary>
    private void AplicarConfiguracionLocal(ConfiguracionLocalPlanificador config)
    {
        _mostrarParadas = config.MostrarParadas ?? true;
        _usarHorariosTeoricos = config.UsarHorariosTeoricos;
        _segundosPorParadaBusManual = Math.Clamp(config.SegundosPorParadaBusManual, 0, 3600);
        _segundosPorParadaBus = _segundosPorParadaBusManual;

        _lineaMin = Math.Clamp(config.LineaMin, 1, 200);
        _lineaMax = Math.Clamp(config.LineaMax, 1, 200);
        _lineasPermitidas.Clear();
        foreach (var linea in NormalizarLineasPermitidas(config.LineasPermitidas, config.LineaMin, config.LineaMax))
            _lineasPermitidas.Add(linea);
        _activarRangoLineas = config.ActivarRangoLineas;

        _activarRangoItinerarios = config.ActivarRangoItinerarios;
        _itinerarioMin = Math.Clamp(config.ItinerarioMin, 1, 99);
        _itinerarioMax = Math.Clamp(config.ItinerarioMax, 1, 99);

        _activarRangoTransbordos = config.ActivarRangoTransbordos;
        _minTransbordosPermitidos = Math.Clamp(config.MinTransbordosPermitidos, 0, 10);
        _maxTransbordosPermitidos = Math.Clamp(config.MaxTransbordosPermitidos, 0, 10);

        _usarHoraSimulada = config.UsarHoraSimulada;
        if (DateOnly.TryParse(config.FechaSimulada, out var fechaSimulada))
            _fechaSimulada = fechaSimulada;
        if (TimeOnly.TryParse(config.HoraSimulada, out var horaSimulada))
            _horaSimulada = horaSimulada;

        if (config.FactorPenalizacionDistanciaPie is { } factor && double.IsFinite(factor))
            _factorPenalizacionDistanciaPie = Math.Abs(factor - 1.3) < 0.0001 ? 1.6 : factor;
        if (config.ParametroPenalizacionDistanciaPieMetros is { } parametro && double.IsFinite(parametro))
            _parametroPenalizacionDistanciaPieMetros = parametro;
        if (config.CoeficienteEsfuerzoAltura is { } coeficienteAltura && double.IsFinite(coeficienteAltura))
            _coeficienteEsfuerzoAltura = Math.Abs(coeficienteAltura - 1.6) < 0.0001
                ? CoeficienteEsfuerzoAlturaPorDefecto
                : coeficienteAltura;
        if (config.PesoTransbordoRankingMinutos is { } pesoTransbordo && double.IsFinite(pesoTransbordo))
            _pesoTransbordoRankingMinutos = Math.Abs(pesoTransbordo - 8.0) < 0.0001 ? 4.0 : Math.Max(0, pesoTransbordo);
        if (config.FactorEquilibradoAndarBus is { } factorEquilibrado && double.IsFinite(factorEquilibrado))
            _factorEquilibradoAndarBus = Math.Max(0, factorEquilibrado);
        if (config.UmbralPendienteSubidaRuta is { } umbralSubida && double.IsFinite(umbralSubida))
            _umbralPendienteSubidaRuta = NormalizarUmbralPendienteRuta(umbralSubida);
        if (config.UmbralPendienteBajadaRuta is { } umbralBajada && double.IsFinite(umbralBajada))
            _umbralPendienteBajadaRuta = NormalizarUmbralPendienteRuta(umbralBajada);
        _velocidadPieKmH = Math.Clamp(config.VelocidadPieKmH, 0.5, 25.0);
        _velocidadBusKmH = Math.Clamp(
            Math.Abs(config.VelocidadBusKmH - 15.0) < 0.0001 ||
            Math.Abs(config.VelocidadBusKmH - 16.6) < 0.0001
                ? 16.0
                : config.VelocidadBusKmH,
            1.0,
            120.0);
        _elevMaxPuntosPorTramo = Math.Clamp(config.ElevMaxPuntosPorTramo, 2, 999);
        _radioCandidatasMetros = Math.Clamp(config.RadioCandidatasMetros, 200, 3500);
        _margenTransbordoSegundos = Math.Clamp(config.MargenTransbordoSegundos, 0, 1800);
        _nivelBrilloMapa = Math.Clamp(config.NivelBrilloMapa, 0, 4);
        _usarColorRealLineaEnItinerarios = config.UsarColorRealLineaEnItinerarios;
        _usarOsrmParaRanking = config.UsarOsrmParaRanking;
        _criterioPrioritarioRuta = NormalizarCriterioRuta(config.CriterioPrioritarioRuta, config.PreferenciaEtiquetaRuta);
        SincronizarPreferenciaDesdeCriterioRuta();

        if (Enum.TryParse<ProveedorCaminata>(config.WalkProvider, ignoreCase: true, out var walkProvider))
            _walkProvider = walkProvider;

        SincronizarTextosPenalizacionPie();
        SincronizarSegundosPorParadaConHorarios();
    }

    private static IReadOnlyList<int> NormalizarLineasPermitidas(
        IReadOnlyList<int>? lineas,
        int lineaMinLegacy,
        int lineaMaxLegacy)
    {
        if (lineas is { Count: > 0 })
        {
            return lineas
                .Where(linea => linea > 0 && linea <= 999)
                .Distinct()
                .OrderBy(linea => linea)
                .ToList();
        }

        if (lineaMinLegacy <= 0 && lineaMaxLegacy <= 0)
            return Array.Empty<int>();

        var min = Math.Clamp(Math.Min(lineaMinLegacy, lineaMaxLegacy), 1, 999);
        var max = Math.Clamp(Math.Max(lineaMinLegacy, lineaMaxLegacy), 1, 999);
        var esRangoPorDefecto = min <= 1 && max >= 200;
        if (esRangoPorDefecto)
            return Array.Empty<int>();

        return Enumerable.Range(min, max - min + 1).ToList();
    }

    /// <summary>
    /// Abre el cuadro de confirmación para restablecer valores.
    /// </summary>
    private void PedirConfirmacionRestablecer()
    {
        _mostrarConfirmacionRestablecer = true;
    }

    /// <summary>
    /// Cierra el cuadro de confirmación de restablecer.
    /// </summary>
    private void CancelarConfirmacionRestablecer()
    {
        _mostrarConfirmacionRestablecer = false;
    }

    /// <summary>
    /// Restablece la configuracion avanzada a los valores por defecto de la aplicacion.
    /// </summary>
    private async Task RestablecerConfiguracionPorDefectoAsync()
    {
        _mostrarConfirmacionRestablecer = false;
        bool confirmado = true;
        try
        {
            confirmado = await JS.InvokeAsync<bool>("confirm", "¿Seguro que quieres restablecer la configuración avanzada a sus valores por defecto?");
        }
        catch (JSException)
        {
            confirmado = false;
        }

        if (!confirmado)
            return;

        _mostrarParadas = true;
        _usarHorariosTeoricos = true;
        _segundosPorParadaBusManual = 30;
        _segundosPorParadaBus = 30;

        _activarRangoItinerarios = true;
        _itinerarioMin = 1;
        _itinerarioMax = 99;

        _activarRangoTransbordos = true;
        _minTransbordosPermitidos = 0;
        _maxTransbordosPermitidos = 3;

        _factorPenalizacionDistanciaPie = 1.6;
        _parametroPenalizacionDistanciaPieMetros = 100.0;
        _coeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto;
        _pesoTransbordoRankingMinutos = 4.0;
        _factorEquilibradoAndarBus = 12.0;
        _umbralPendienteSubidaRuta = 3.0;
        _umbralPendienteBajadaRuta = 3.0;
        _velocidadPieKmH = 3.6;
        _velocidadBusKmH = 16.0;
        _minutosTransbordo = 5;
        _margenTransbordoSegundos = 120;
        _elevMaxPuntosPorTramo = 999;
        _radioCandidatasMetros = 1200;
        _walkProvider = ProveedorCaminata.Valhalla;
        _nivelBrilloMapa = 0;
        _usarColorRealLineaEnItinerarios = false;
        _usarOsrmParaRanking = false;

        if (_usarHoraSimulada)
            StopClock();
        else
            StartClock();
        SincronizarTextosPenalizacionPie();
        SincronizarSegundosPorParadaConHorarios();
        await RefrescarHoraUIAsync();
        await PersistirConfiguracionAsync();
        StateHasChanged();
    }

    /// <summary>
    /// Restablece la configuración desde el modal personalizado centrado.
    /// </summary>
    private async Task ConfirmarRestablecerConfiguracionAsync()
    {
        _mostrarConfirmacionRestablecer = false;

        _mostrarParadas = true;
        _usarHorariosTeoricos = true;
        _segundosPorParadaBusManual = 30;
        _segundosPorParadaBus = 30;

        _activarRangoItinerarios = true;
        _itinerarioMin = 1;
        _itinerarioMax = 99;

        _activarRangoTransbordos = true;
        _minTransbordosPermitidos = 0;
        _maxTransbordosPermitidos = 3;

        _factorPenalizacionDistanciaPie = 1.6;
        _parametroPenalizacionDistanciaPieMetros = 100.0;
        _coeficienteEsfuerzoAltura = CoeficienteEsfuerzoAlturaPorDefecto;
        _pesoTransbordoRankingMinutos = 4.0;
        _factorEquilibradoAndarBus = 12.0;
        _umbralPendienteSubidaRuta = 3.0;
        _umbralPendienteBajadaRuta = 3.0;
        _velocidadPieKmH = 3.6;
        _velocidadBusKmH = 16.0;
        _minutosTransbordo = 5;
        _margenTransbordoSegundos = 120;
        _elevMaxPuntosPorTramo = 999;
        _radioCandidatasMetros = 1200;
        _walkProvider = ProveedorCaminata.Valhalla;
        _nivelBrilloMapa = 0;
        _usarColorRealLineaEnItinerarios = false;
        _usarOsrmParaRanking = false;

        if (_usarHoraSimulada)
            StopClock();
        else
            StartClock();
        SincronizarTextosPenalizacionPie();
        SincronizarSegundosPorParadaConHorarios();
        await RefrescarHoraUIAsync();
        await PersistirConfiguracionAsync();
        StateHasChanged();
    }
}
