using Microsoft.Extensions.Caching.Memory;
using MongoDB.Driver;
using OnlyOneAccessTemplate.Models;

namespace OnlyOneAccessTemplate.Services
{
    public class ModuleService : IModuleService
    {
        private readonly IMongoDatabase _database;
        private readonly HttpClient _httpClient;
        private readonly IMemoryCache _cache;
        private readonly ILogger<ModuleService> _logger;
        private readonly IMongoCollection<ModuleConfig> _moduleCollection;

        public ModuleService(
            IMongoDatabase database,
            HttpClient httpClient,
            IMemoryCache cache,
            ILogger<ModuleService> logger)
        {
            _database = database;
            _httpClient = httpClient;
            _cache = cache;
            _logger = logger;
            _moduleCollection = _database.GetCollection<ModuleConfig>("modules");
        }

        public async Task<ModuleConfig?> GetModuleConfigAsync(string moduleId)
        {
            try
            {
                var filter = Builders<ModuleConfig>.Filter.And(
                    Builders<ModuleConfig>.Filter.Eq(x => x.ModuleId, moduleId),
                    Builders<ModuleConfig>.Filter.Eq(x => x.IsActive, true)
                );

                return await _moduleCollection.Find(filter).FirstOrDefaultAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao buscar configuração do módulo {ModuleId}", moduleId);
                return null;
            }
        }

        public async Task<string> GetModuleContentAsync(string moduleId)
        {
            var cacheKey = $"module_content_{moduleId}";

            if (_cache.TryGetValue(cacheKey, out string? cachedContent) && !string.IsNullOrEmpty(cachedContent))
            {
                return cachedContent;
            }

            var config = await GetModuleConfigAsync(moduleId);
            if (config == null)
            {
                return "<p>Módulo não encontrado.</p>";
            }

            var content = await FetchContentFromUrlAsync(config.Url, config.Headers);

            // Cache por X minutos conforme configuração
            _cache.Set(cacheKey, content, TimeSpan.FromMinutes(config.CacheMinutes));

            return content;
        }

        public async Task<string> FetchContentFromUrlAsync(string url, Dictionary<string, string>? headers = null)
        {
            try
            {
                using var request = new HttpRequestMessage(HttpMethod.Get, url);

                if (headers != null)
                {
                    foreach (var header in headers)
                    {
                        request.Headers.TryAddWithoutValidation(header.Key, header.Value);
                    }
                }

                using var response = await _httpClient.SendAsync(request);

                if (response.IsSuccessStatusCode)
                {
                    return await response.Content.ReadAsStringAsync();
                }

                _logger.LogWarning("Falha ao buscar conteúdo de {Url}. Status: {StatusCode}", url, response.StatusCode);
                return "<p>Conteúdo temporariamente indisponível.</p>";
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Erro ao buscar conteúdo de {Url}", url);
                return "<p>Erro ao carregar conteúdo.</p>";
            }
        }

        public async Task<List<ModuleConfig>> GetAllActiveModulesAsync()
        {
            var filter = Builders<ModuleConfig>.Filter.Eq(x => x.IsActive, true);
            return await _moduleCollection.Find(filter).ToListAsync();
        }

        public async Task SaveModuleConfigAsync(ModuleConfig config)
        {
            config.UpdatedAt = DateTime.UtcNow;

            if (string.IsNullOrEmpty(config.Id))
            {
                config.CreatedAt = DateTime.UtcNow;
                await _moduleCollection.InsertOneAsync(config);
            }
            else
            {
                var filter = Builders<ModuleConfig>.Filter.Eq(x => x.Id, config.Id);
                await _moduleCollection.ReplaceOneAsync(filter, config);
            }
        }
    }
}
