using IWS5.Core.DTO;
using IWS5.DatabaseAccess.Interfaces.Importer;
using IWS5.Importer.Abstractions.Services;
using Microsoft.Extensions.Caching.Memory;
using Serilog;
using Serilog.Context;

namespace IWS5.Services.ImporterService;

public class ProcessService : IProcessService
{
    private readonly IProcessRepository _processRepository;
    private readonly IMemoryCache _cache;
    private readonly ILogger _logService;
    private readonly TimeSpan _cacheExpiration = TimeSpan.FromHours(3);

    public ProcessService(IProcessRepository processRepository, ILogger logService, IMemoryCache cache)
    {
        _processRepository = processRepository;
        _logService = logService;
        _cache = cache;
    }

    /// <summary>
    /// Retrieves a dictionary of valid processes with unique names.
    /// Uses caching to avoid redundant queries.
    /// </summary>
    public async Task<Dictionary<string, List<ProcessDto>>> GetValidProcessesAsync()
    {
        return await _cache.GetOrCreateAsync("ValidProcesses", async entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = _cacheExpiration;
            var processes = await FetchProcessesFromDatabase();
            return processes
            .Where(p => !string.IsNullOrWhiteSpace(p.ProcessName)) // Null-safe
            .GroupBy(p => p.ProcessName!, StringComparer.OrdinalIgnoreCase)
            .ToDictionary(
                g => g.Key!,
                g => g.ToList(),
                StringComparer.OrdinalIgnoreCase);
        }) ?? new Dictionary<string, List<ProcessDto>>();
    }

    public async Task<ProcessDto?> GetProcessByNameAndVersionAsync(string processName, int? version = null)
    {
        try
        {
            var process = await _processRepository.GetProcessByNameAndVersionAsync(processName, version);

            if (process == null)
            {
                using (LogContext.PushProperty("LogContext", nameof(ProcessService)))
                    _logService.Warning("No process found with name {ProcessName} and version {Version}", processName, version);
            }

            return process;
        }
        catch (Exception ex)
        {
            using (LogContext.PushProperty("LogContext", nameof(GetProcessByNameAndVersionAsync)))
                _logService.Error(ex, "Error retrieving process {ProcessName} version {Version}", processName, version);
            throw;
        }
    }


    /// <summary>
    /// Fetches all processes directly from the database.
    /// </summary>
    private async Task<List<ProcessDto>> FetchProcessesFromDatabase()
    {
        try
        {
            var processes = await _processRepository.GetProcessesAsync();
            using (LogContext.PushProperty("LogContext", nameof(ProcessService)))
                _logService.Information($"Retrieved {processes.Count} processes from database.");
            return processes;
        }
        catch (Exception ex)
        {
            LogCreationError(ex);
            throw;
        }
    }

    private void LogCreationError(Exception ex)
    {
        using (LogContext.PushProperty("LogContext", nameof(ProcessService)))
            _logService.Error(ex, "Error retrieving processes from repository.");
    }
}
