using Elecciones.Core.DTOs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.RateLimiting;
using System.Threading.Tasks;

namespace Elecciones.Infrastructure.Services;

public class ElectoralApiService : IElectoralApiService
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IConfiguration _configuration;
    private readonly ILogger<ElectoralApiService> _logger;
    private readonly RateLimiter _rateLimiter;

    public ElectoralApiService(
        IHttpClientFactory httpClientFactory,
        IConfiguration configuration,
        ILogger<ElectoralApiService> logger,
        RateLimiter rateLimiter)
    {
        _httpClientFactory = httpClientFactory;
        _configuration = configuration;
        _logger = logger;
        _rateLimiter = rateLimiter;
    }

    public async Task<TokenResponse?> GetAuthTokenAsync()
    {
        using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);
        if (lease.IsAcquired)
        {
            var client = _httpClientFactory.CreateClient("ElectoralApiClient");
            var username = _configuration["ElectoralApi:Username"];
            var password = _configuration["ElectoralApi:Password"];
            if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) return null;

            var request = new HttpRequestMessage(HttpMethod.Get, "/api/createtoken");
            request.Headers.Add("username", username);
            request.Headers.Add("password", password);

            var response = await client.SendAsync(request);
            if (!response.IsSuccessStatusCode) return null;

            return await response.Content.ReadFromJsonAsync<TokenResponse>();
        }
        return null;
    }

    public async Task<CatalogoDto?> GetCatalogoAmbitosAsync(string authToken, int categoriaId)
    {
        // "Pedir una ficha". Este método ahora devuelve un "lease" (permiso).
        // Si no hay fichas, esperará aquí automáticamente hasta que se rellene el cubo.
        /*
                using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);

                // Si se nos concede el permiso para proceder...
                if (lease.IsAcquired)
                {*/
        var client = _httpClientFactory.CreateClient("ElectoralApiClient");
        var request = new HttpRequestMessage(HttpMethod.Get, $"/api/catalogo/getCatalogo?categoriaId={categoriaId}");
        request.Headers.Add("Authorization", $"Bearer {authToken}");
        var response = await client.SendAsync(request);
        return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<CatalogoDto>() : null;
        /* }
         // Si no se pudo obtener un permiso (ej. la cola está llena), devolvemos null.
         return null;*/
    }

    public async Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId)
    {
        // "Pedir una ficha". Este método ahora devuelve un "lease" (permiso).
        // Si no hay fichas, esperará aquí automáticamente hasta que se rellene el cubo.
        /*
                using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);

                // Si se nos concede el permiso para proceder...
                if (lease.IsAcquired)
                {*/
        var client = _httpClientFactory.CreateClient("ElectoralApiClient");
        var requestUri = $"/api/catalogo/getAgrupaciones?distritoId={distritoId}&categoriaId={categoriaId}";
        var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        request.Headers.Add("Authorization", $"Bearer {authToken}");
        var response = await client.SendAsync(request);
        return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<List<AgrupacionDto>>() : null;
        /* }
         // Si no se pudo obtener un permiso (ej. la cola está llena), devolvemos null.
         return null;*/
    }

    public async Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string? seccionId, string? municipioId, int categoriaId)
    {
        using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);
        if (!lease.IsAcquired) return null;

        var client = _httpClientFactory.CreateClient("ElectoralApiClient");
        var requestUri = $"/api/resultados/getResultados?distritoId={distritoId}&seccionId={seccionId}&categoriaId={categoriaId}";

        if (!string.IsNullOrEmpty(municipioId))
        {
            requestUri += $"&municipioId={municipioId}";
        }

        var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        request.Headers.Add("Authorization", $"Bearer {authToken}");

        try
        {
            var response = await client.SendAsync(request);

            // --- APLICAMOS LA MISMA LÓGICA DEFENSIVA ---
            if (response.IsSuccessStatusCode)
            {
                try
                {
                    // Leemos el contenido como un string primero para poder loguearlo si falla.
                    var contentString = await response.Content.ReadAsStringAsync();
                    if (string.IsNullOrEmpty(contentString))
                    {
                        _logger.LogWarning("La API devolvió 200 OK pero con cuerpo vacío para getResultados. URI: {uri}", requestUri);
                        return null;
                    }

                    return JsonSerializer.Deserialize<ResultadosDto>(contentString);
                }
                catch (JsonException ex)
                {
                    _logger.LogWarning(ex, "La API devolvió una respuesta no-JSON para getResultados. URI: {uri}", requestUri);
                    return null;
                }
            }

            _logger.LogWarning("La API devolvió un código de error {statusCode} para getResultados. URI: {uri}", response.StatusCode, requestUri);
            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "La petición HTTP a getResultados falló. URI: {uri}", requestUri);
            return null;
        }
    }

    public async Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string? seccionProvincialId, int categoriaId)
    {
        using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);
        if (!lease.IsAcquired) return null;

        var client = _httpClientFactory.CreateClient("ElectoralApiClient");
        var requestUri = $"/api/resultados/getBancas?distritoId={distritoId}&categoriaId={categoriaId}";
        if (!string.IsNullOrEmpty(seccionProvincialId))
        {
            requestUri += $"&seccionProvincialId={seccionProvincialId}";
        }

        var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        request.Headers.Add("Authorization", $"Bearer {authToken}");

        try
        {
            var response = await client.SendAsync(request);

            if (response.IsSuccessStatusCode)
            {
                // Verificamos si la respuesta realmente tiene contenido.
                // ContentLength puede ser null, así que lo verificamos primero.
                if (response.Content.Headers.ContentLength == 0)
                {
                    _logger.LogInformation("La API devolvió 200 OK pero con cuerpo vacío para getBancas. URI: {uri}", requestUri);
                    return null; // Tratamos un cuerpo vacío como "sin datos".
                }

                try
                {
                    // Solo intentamos deserializar si sabemos que hay contenido.
                    return await response.Content.ReadFromJsonAsync<RepartoBancasDto>();
                }
                catch (JsonException ex)
                {
                    // Si aún así falla (ej. el contenido es "[]" en lugar de "{}"), lo capturamos como una advertencia.
                    _logger.LogWarning(ex, "La API devolvió una respuesta no-JSON para getBancas. URI: {uri}", requestUri);
                    return null;
                }
            }

            _logger.LogWarning("La API devolvió un código de error {statusCode} para getBancas. URI: {uri}", response.StatusCode, requestUri);
            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "La petición HTTP a getBancas falló. URI: {uri}", requestUri);
            return null;
        }
    }

    public async Task<List<string>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId, int? categoriaId = null)
    {
        // "Pedir una ficha". Este método ahora devuelve un "lease" (permiso).
        // Si no hay fichas, esperará aquí automáticamente hasta que se rellene el cubo.
        /*
                using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);

                // Si se nos concede el permiso para proceder...
                if (lease.IsAcquired)
                {*/
        var client = _httpClientFactory.CreateClient("ElectoralApiClient");
        var requestUri = $"/api/resultados/getTelegramasTotalizados?distritoId={distritoId}&seccionId={seccionId}";

        if (categoriaId.HasValue)
        {
            requestUri += $"&categoriaId={categoriaId.Value}";
        }

        var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        request.Headers.Add("Authorization", $"Bearer {authToken}");
        var response = await client.SendAsync(request);

        // Ahora deserializamos al tipo correcto: List<string>
        return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<List<string>>() : null;
        /* }
         // Si no se pudo obtener un permiso (ej. la cola está llena), devolvemos null.
         return null;*/
    }

    public async Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId)
    {
        using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);
        if (!lease.IsAcquired) return null;

        var client = _httpClientFactory.CreateClient("ElectoralApiClient");
        var requestUri = $"/api/resultados/getFile?mesaId={mesaId}";
        var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        request.Headers.Add("Authorization", $"Bearer {authToken}");

        try
        {
            var response = await client.SendAsync(request);

            // --- APLICAMOS LA MISMA CORRECCIÓN AQUÍ POR CONSISTENCIA ---
            if (response.IsSuccessStatusCode)
            {
                try
                {
                    return await response.Content.ReadFromJsonAsync<TelegramaFileDto>();
                }
                catch (JsonException ex)
                {
                    _logger.LogWarning(ex, "La API devolvió una respuesta no-JSON para getFile para la mesa {mesaId}", mesaId);
                    return null;
                }
            }

            _logger.LogWarning("La API devolvió un código de error {statusCode} para la mesa {mesaId}", response.StatusCode, mesaId);
            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "La petición HTTP a getFile falló para la mesa {mesaId}", mesaId);
            return null;
        }
    }

    public async Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId)
    {
        // "Pedir una ficha". Este método ahora devuelve un "lease" (permiso).
        // Si no hay fichas, esperará aquí automáticamente hasta que se rellene el cubo.
        /*
                using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);

                // Si se nos concede el permiso para proceder...
                if (lease.IsAcquired)
                {*/
        var client = _httpClientFactory.CreateClient("ElectoralApiClient");
        // La URL ahora usa el parámetro 'categoriaId' que se recibe
        var requestUri = $"/api/estados/estadoRecuento?distritoId={distritoId}&categoriaId={categoriaId}";
        var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        request.Headers.Add("Authorization", $"Bearer {authToken}");
        var response = await client.SendAsync(request);
        return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<EstadoRecuentoGeneralDto>() : null;
        /* }
         // Si no se pudo obtener un permiso (ej. la cola está llena), devolvemos null.
         return null;*/
    }

    public async Task<List<CategoriaDto>?> GetCategoriasAsync(string authToken)
    {
        // "Pedir una ficha". Este método ahora devuelve un "lease" (permiso).
        // Si no hay fichas, esperará aquí automáticamente hasta que se rellene el cubo.
        /*
                using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);

                // Si se nos concede el permiso para proceder...
                if (lease.IsAcquired)
                {*/
        var client = _httpClientFactory.CreateClient("ElectoralApiClient");
        var request = new HttpRequestMessage(HttpMethod.Get, "/api/catalogo/getCategorias");
        request.Headers.Add("Authorization", $"Bearer {authToken}");
        var response = await client.SendAsync(request);
        return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<List<CategoriaDto>>() : null;
        /* }
         // Si no se pudo obtener un permiso (ej. la cola está llena), devolvemos null.
         return null;*/
    }
}