using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace Elecciones.Infrastructure.Services;

public class SharedTokenService
{
    private readonly IElectoralApiService _apiService;
    private readonly ILogger<SharedTokenService> _logger;
    private string? _authToken;
    private DateTimeOffset _tokenExpiration = DateTimeOffset.MinValue;
    
    // Un SemaphoreSlim para asegurar que solo una tarea a la vez intente renovar el token.
    private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);

    public SharedTokenService(IElectoralApiService apiService, ILogger<SharedTokenService> logger)
    {
        _apiService = apiService;
        _logger = logger;
    }

    public async Task<string?> GetValidAuthTokenAsync(CancellationToken stoppingToken)
    {
        // Si el token es válido, lo devolvemos inmediatamente sin bloquear.
        if (!string.IsNullOrEmpty(_authToken) && DateTimeOffset.UtcNow < _tokenExpiration.AddMinutes(-1))
        {
            return _authToken;
        }

        // Si el token necesita renovación, esperamos nuestro turno para intentar renovarlo.
        await _tokenSemaphore.WaitAsync(stoppingToken);
        try
        {
            // Volvemos a comprobar por si otra tarea ya lo renovó mientras esperábamos.
            if (!string.IsNullOrEmpty(_authToken) && DateTimeOffset.UtcNow < _tokenExpiration.AddMinutes(-1))
            {
                return _authToken;
            }

            _logger.LogInformation("Token no válido o a punto de expirar. Solicitando uno nuevo...");
            var tokenResponse = await _apiService.GetAuthTokenAsync();

            if (tokenResponse?.Data?.AccessToken != null)
            {
                _authToken = tokenResponse.Data.AccessToken;
                _tokenExpiration = DateTimeOffset.UtcNow.AddSeconds(tokenResponse.Data.ExpiresIn);
                _logger.LogInformation("Nuevo token obtenido. Válido hasta: {expiration}", _tokenExpiration);
            }
            else
            {
                _logger.LogError("CRÍTICO: No se pudo obtener un nuevo token de autenticación.");
                _authToken = null;
            }
        }
        finally
        {
            _tokenSemaphore.Release();
        }
        
        return _authToken;
    }
}