using System.Reflection;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Pumps;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace adas_core.Application.Services;

public class ConfigUnitsService(
    IConfigUnitsRepository configUnitsRepository,
    IOptions<ApiSettings> apiSettings,
    ILogger<ConfigUnitsService> logger)
    : IConfigUnitsService
{
    private static ConfigUnits? _config;
    private static DateTime _nextRefresh = DateTime.MinValue;

    private readonly bool _configUnitsRequired = apiSettings.Value.ConfigUnitsRequired;

    private readonly string _key = apiSettings.Value.ConfigUnitsKey ?? "PV1";
    private readonly int? _refreshTimeout = apiSettings.Value.ConfigObservation?.Refresh;

    public async Task<T> Map<T>(T obs) where T : BasePatientObservation
    {
        if (!_configUnitsRequired) return obs;
        
        var conf = !string.IsNullOrEmpty(obs.Units) ? await Get(obs.Units) : null;

        return conf == null ? obs : MapConf(obs, conf);
    }

    public async Task<PumpObservation> Map(PumpObservation obs)
    {
        if (!_configUnitsRequired) return obs;

        //Para cada propiedad de la observación que sea del tipo PumpValue llama a GetByCodeSysAndCode(PumpValue)
        await MapPumpValues(obs);

        // var conf = !string.IsNullOrEmpty(obs.Units) ? await Get(obs.Units) : null;
        // return conf == null ? obs : MapConf(obs, conf);
        
        return obs;
    }

    /// <summary>
    ///     Recorre recursivamente las propiedades del objeto para encontrar y convertir PumpValues.
    /// </summary>
    private async Task MapPumpValues(object? targetObject)
    {
        if (targetObject == null) return;

        var properties = targetObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (var property in properties)
        {
            var propertyValue = property.GetValue(targetObject);

            if (propertyValue == null) continue;


            if (property.PropertyType == typeof(CommonPumpTypes.PumpValue))
            {
                var pumpValue = (CommonPumpTypes.PumpValue)propertyValue;

                if (string.IsNullOrEmpty(pumpValue.Units)) continue;

                var conf = await Get(pumpValue.Units);
                if (conf != null) pumpValue.Units = conf.Value;
            }
            // En este caso Syringe es una clase que tienen un pumpValue
            else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
            {
                // Llamada recursiva para inspeccionar las propiedades anidadas
                await MapPumpValues(propertyValue);
            }
        }
    }


    private T MapConf<T>(T obs, ConfigUnitItem conf) where T : BasePatientObservation
    {
        if (obs is not PatientObservation pobs) return obs;

        logger.LogDebug("Mapping config Unit obs: {obs} to units: {conf}", obs, conf.Value);
        pobs.Units = conf.Value;

        return obs;
    }

    public async Task<ConfigUnitItem?> Get(string code)
    {
        var result = await GetConfig();

        return result?.Items?.FirstOrDefault(i => i.Code == code);
    }

    private async Task<ConfigUnits?> GetConfig()
    {
        try
        {
            if (_config != null && DateTime.Now <= _nextRefresh) return _config;

            _config = await configUnitsRepository.FindById(_key);
            _nextRefresh = _refreshTimeout.HasValue
                ? DateTime.Now.AddSeconds(_refreshTimeout.Value)
                : DateTime.MinValue;
            return _config;
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "ERROR READ config_units: {_key}: {ex}", _key, ex.Message);
            return null;
        }
    }
}