using System.Globalization;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Utils.Interfaces;
using Microsoft.Extensions.Options;

namespace adas_core.Domain.Utils;

/// <summary>
/// Provides a sealed implementation of mapping utilities, serving as the concrete type for the <see cref="IMappingUtils"/> contract.
/// </summary>
public sealed class MappingUtils : IMappingUtils
{
    private readonly List<MappingInterventions>? _cccData;
    private bool _isTransformedValue;

    public MappingUtils(IOptions<ApiSettings> apiSettings)
    {
        var cccMappingData = apiSettings.Value.MappingInterventions;

        _cccData = cccMappingData;
        _isTransformedValue = false;
    }

/*
    public (string Tipo, string Nombre)? SearchByCode(double code)
    {
        ConvertStringToDoubleInMapping();
        var data = _cccData;

        foreach (var dato in data)
        {
            foreach (var rango in dato.Codes)
            {
                if (rango.FinalValue == null || rango.FinalValue.Equals("")) // Es un valor unico
                {
                    if ((double)rango.InitialValue == code)
                        return (dato.Type, dato.Name);
                }
                else // Es un rango
                {
                    if (code >= (double)rango.InitialValue && code <= (double)rango.FinalValue)
                        return (dato.Type, dato.Name);
                }
            }
        }

        return null; // No se encontro el code
    }*/
    public (string type, string name, string group)? SearchByCode(object code, string category)
    {
        // esta funcion conviete a double los string que permitan conversion si no se puede los deja como string
        ConvertStringToDoubleInMapping();

        var data = _cccData;

        if (data == null)
            return null;

        // Si el parámetro es un double, buscar directamente en los valores numéricos
        if (code is double searchValue)
        {
            foreach (var dato in data)
            foreach (var rango in dato.Codes)
                if (rango.FinalValue is null or "") // Es un valor unico
                {
                    if (rango.InitialValue is double initialDouble && Math.Abs(initialDouble - searchValue) == 0 &&
                        category == dato.Category.ToString())
                        return (dato.Type, dato.Name, dato.Group)!;
                }
                else // Es un rango
                {
                    if (rango is { InitialValue: double initialDouble, FinalValue: double finalDouble } &&
                        searchValue >= initialDouble && searchValue <= finalDouble
                        && category == dato.Category.ToString())
                        return (dato.Type, dato.Name, dato.Group)!;
                }
        }
        // Si el parámetro es un string
        else if (code is string searchString)
        {
            // Intentar convertir el string a double para buscar entre valores numericos
            if (double.TryParse(searchString, NumberStyles.Float, CultureInfo.InvariantCulture,
                    out var searchValueAsDouble))
            {
                foreach (var dato in data)
                foreach (var rango in dato.Codes)
                    if (rango.FinalValue is null or "") // Es un valor unico
                    {
                        if (rango.InitialValue is double initialDouble &&
                            Math.Abs(initialDouble - searchValueAsDouble) == 0 && category == dato.Category.ToString())
                            return (dato.Type, dato.Name, dato.Group)!;
                    }
                    else // Es un rango
                    {
                        if (rango is { InitialValue: double initialDouble, FinalValue: double finalDouble } &&
                            searchValueAsDouble >= initialDouble && searchValueAsDouble <= finalDouble
                            && category == dato.Category.ToString())
                            return (dato.Type, dato.Name, dato.Group)!;
                    }
            }
            else
            {
                // Si no es convertible a double, buscar directamente entre los valores de tipo string
                foreach (var dato in data)
                foreach (var rango in dato.Codes)
                {
                    if (rango.InitialValue is string initialString && initialString == searchString &&
                        category == dato.Category.ToString())
                        return (dato.Type, dato.Name, dato.Group)!;

                    if (rango.FinalValue is string finalString && finalString == searchString &&
                        category == dato.Category.ToString())
                        return (dato.Type, dato.Name, dato.Group)!;
                }
            }
        }

        // No se encontro el codigo
        return null;
    }


    /// <summary>
    /// Retrieves a complexity value from the CCC data mapping by matching the provided <paramref name="name"/>.
    /// If a single value entry exists for the name, its initial value is returned as an integer; if multiple ranges are defined, the value contributed by the range containing the optional <paramref name="peso"/> is returned. Returns 0 when no matching name is found or when the data is not available.
    /// </summary>
    /// <param name="name">The name of the entry to look up in the CCC data.</param>
    /// <param name="peso">Optional weight used to select the matching range when multiple values are defined; when null, range-based lookup is skipped.</param>
    /// <returns>The matched initial value (for single-entry entries) or the contributed value (for range-based entries); returns 0 if the name is not found or no data is available.</returns>
    public int GetComplexityValue(string name, double? peso = null)
        {
            ConvertStringToDoubleInMapping();
            var data = _cccData;
    
            if (data == null) return 0; // No encontro ningun nombre que conincida con el dado
    
            foreach (var dato in data)
                if (name.Equals(dato.Name))
                {
                    if (dato.Value is
                        {
                            Count: 1
                        }) //value es una lista, si contiene un solo elemento retornaremos el initial value de ese elemento
                        return Convert.ToInt32(dato.Value[0].InitialValue);
    
                    if (dato.Value is { Count: > 1 } && peso != null)
                        foreach (var rango in dato.Value)
                            if (peso >= (double?)rango.InitialValue && peso <= (double?)rango.FinalValue)
                                return rango.ValueContributed;
                }
    
            return 0; // No encontro ningun nombre que conincida con el dado
        }

    /// <summary>
    /// Converts string representations of numeric values in the <c>_cccData</c> mapping to <see cref="double"/> using the invariant culture, ensuring the period (".") is recognized as the decimal separator. The method only runs when the data has not already been transformed (<c>_isTransformedValue</c> is false) and when <c>_cccData</c> is not null, iterating through each mapping's <c>Codes</c> and <c>Value</c> entries to parse and replace their <c>InitialValue</c> and <c>FinalValue</c> when they are strings. After processing, it marks the transformation as completed by setting <c>_isTransformedValue</c> to true.
    /// </summary>
    private void ConvertStringToDoubleInMapping()
        {
            // esto es  para garantizar que el punto (".") sea reconocido como separador decimal
            var culture = CultureInfo.InvariantCulture;
            if (_isTransformedValue)
            {
            }
            else if (_cccData != null)
            {
                foreach (var mapping in _cccData)
                {
                    // Recorrer la lista Codes
                    foreach (var code in mapping.Codes)
                    {
                        if (code.InitialValue is string initialValueString &&
                            double.TryParse(initialValueString, culture, out var initialValueDouble))
                            code.InitialValue = initialValueDouble; // Convertir y guardar como double
    
                        if (code.FinalValue is string finalValueString &&
                            double.TryParse(finalValueString, culture, out var finalValueDouble))
                            code.FinalValue = finalValueDouble; // Convertir y guardar como double
                    }
    
                    // Recorrer la lista Value (si no es nula)
                    if (mapping.Value != null)
                        foreach (var value in mapping.Value)
                        {
                            if (value.InitialValue is string initialValueString &&
                                double.TryParse(initialValueString, culture, out var initialValueDouble))
                                value.InitialValue = initialValueDouble; // Convertir y guardar como double
    
                            if (value.FinalValue is string finalValueString &&
                                double.TryParse(finalValueString, culture, out var finalValueDouble))
                                value.FinalValue = finalValueDouble; // Convertir y guardar como double
                        }
                } //Fin ford
    
                _isTransformedValue = true;
            }
        }
}