using adas_core.Application.Exceptions;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Enums;
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.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace adas_core.Application.Services;

public class ConfigPumpsService(
    IConfigPumpsRepository configPumpsRepository,
    IOptions<ApiSettings> apiSettings,
    ILogger<ConfigPumpsService> logger,
    IHttpContextAccessor httpContextAccessor,
    ILocalAuditService auditService)
    : IConfigPumpsService
{
    private static ConfigPumps? _config;
    private static DateTime _nextRefresh = DateTime.MinValue;

    private readonly bool _configPumpsRequired = apiSettings.Value.ConfigPumpsRequired;
    private readonly string _key = apiSettings.Value.ConfigPumpsKey ?? "PV1";
    private readonly int? _refreshTimeout = apiSettings.Value.ConfigObservation?.Refresh;

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

        var conf = !string.IsNullOrEmpty(obs.AlarmType.ToString())
            ? await Get(obs.AlarmType.ToString() ?? string.Empty)
            : null;

        if (conf == null) return obs;

        return await MapConf(obs, conf);
    }

    public async Task<List<ConfigPumps>?> GetAllPumpConfigs()
    {
        return await configPumpsRepository.GetAllConfigs();
    }

    public async Task<ConfigPumps?> GetPumpConfigById(string id)
    {
        return await configPumpsRepository.FindById(id);
    }

    public async Task<List<ConfigPumpItem>?> GetConfigItems(string id)
    {
        var result = await configPumpsRepository.FindById(id);
        return result?.Items;
    }

    public async Task<ConfigPumps?> UpdatePumpConfig(ConfigPumps pumpConfig)
    {
        var oldPumpConfig = configPumpsRepository.FindById(pumpConfig.Id);
        var newPumpConfig = await configPumpsRepository.UpdateConfig(pumpConfig) ??
                            throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
        await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldPumpConfig, newPumpConfig);
        return newPumpConfig;
    }

    public async Task<ConfigPumps?> InsertPumpConfig(ConfigPumps pumpConfig)
    {
        try
        {
            await configPumpsRepository.InsertOneAsync(pumpConfig);
            var newPumpConfig = await configPumpsRepository.FindById(pumpConfig.Id) ??
                                throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed);
            await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, newPumpConfig);
            return newPumpConfig;
        }
        catch (Exception ex)
        {
            logger.LogError("ERROR inserting config_pumps: {key}. Exception: {exMessage} ", pumpConfig.Id, ex.Message);
            return null;
        }
    }

    public async Task<bool> DeletePumpConfig(ConfigPumps config)
    {
        try
        {
            var result = await configPumpsRepository.DeleteConfig(config);

            if (result)
            {
                logger.LogError("ERROR deleting config_pumps: {key}.  ", config.Id);
                return false;
            }

            await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, config, null);
            return true;
        }
        catch (Exception ex)
        {
            logger.LogError("ERROR deleting config_pumps: {key}. Exception: {exMessage} ", config.Id, ex.Message);
            return false;
        }
    }

    public async Task<ObservatitonRetentionResult?> RetentionActions(PumpObservation obs)
    {
        //TODO sacarlo de la configuración específica de Bombas
        var conf = await Get(obs);
        return conf is { RetentionPolicy: not null } ? 
            new ObservatitonRetentionResult(conf.RetentionPolicy.Value, conf.RetentionPolicyValue) : 
            new ObservatitonRetentionResult(RetentionPolicy.NoDelete, null);
    }

    private static Task<PumpObservation> MapConf(PumpObservation obs, ConfigPumpItem conf)
    {
        if (conf.UiConfiguration != null && conf.UiConfiguration.Count != 0) 
            obs.UiConfiguration = conf.UiConfiguration;

        return Task.FromResult(obs);
    }

    private async Task<ConfigPumpItem?> Get(string alarmType)
    {
        if (!Enum.TryParse(alarmType, out PumpEnum.AlarmType alarmTypeParsed))
            return null;
        var result = await GetConfig();
        return result?.Items?.FirstOrDefault(i => i.AlarmType == alarmTypeParsed);
    }

    private async Task<ConfigPumpItem?> Get(PumpObservation pobs)
    {
        var config =  await GetConfig();
        return config?.Items?.FirstOrDefault(i => i.Type == pobs.MessageType);
    }
    public async Task<List<ConfigPumpItem>?> Get()
    {
        var result = await GetConfig();
        return result?.Items;
    }


    private async Task<ConfigPumps?> GetConfig()
    {
        try
        {
            if (_config != null && DateTime.Now <= _nextRefresh) 
                return _config;
            _config = await configPumpsRepository.FindById(_key);
            _nextRefresh = _refreshTimeout.HasValue
                ? DateTime.Now.AddSeconds(_refreshTimeout.Value)
                : DateTime.MinValue;

            return _config;
        }
        catch (Exception ex)
        {
            logger.LogError("ERROR READ config_pumps: {key}. Exception: {exMessage} ", _key, ex.Message);
            return null;
        }
    }
}