using Microsoft.AspNetCore.SignalR.Client;
using Blazored.LocalStorage;

namespace FrontOffice.Main.Utilities;

/// <summary>
/// Service for managing SignalR connection and handling token-related notifications from BFF.
/// This service connects to the BFF SignalR Hub and notifies the application when token refresh is needed.
/// </summary>
public class TokenNotificationService : IAsyncDisposable
{
    private readonly ILocalStorageService _localStorage;
    private readonly IConfiguration _configuration;
    private readonly ILogger<TokenNotificationService> _logger;
    private HubConnection? _hubConnection;
    
    /// <summary>
    /// Event fired when a token revoked notification is received from server
    /// </summary>
    public event Func<TokenRevokedEventArgs, Task>? OnTokenRevoked;
    
    /// <summary>
    /// Event fired when a force refresh token notification is received from server
    /// </summary>
    public event Func<ForceRefreshEventArgs, Task>? OnForceRefreshToken;
    
    /// <summary>
    /// Event fired when a broadcast message is received from server
    /// </summary>
    public event Func<BroadcastMessageEventArgs, Task>? OnBroadcastMessage;
    
    /// <summary>
    /// Event fired when connection state changes
    /// </summary>
    public event Action<HubConnectionState>? OnConnectionStateChanged;

    public TokenNotificationService(
        ILocalStorageService localStorage,
        IConfiguration configuration,
        ILogger<TokenNotificationService> logger)
    {
        _localStorage = localStorage;
        _configuration = configuration;
        _logger = logger;
    }

    /// <summary>
    /// Gets the current connection state
    /// </summary>
    public HubConnectionState ConnectionState => _hubConnection?.State ?? HubConnectionState.Disconnected;

    /// <summary>
    /// Connect to the SignalR Hub with the user's authentication token
    /// </summary>
    public async Task ConnectAsync()
    {
        if (_hubConnection?.State == HubConnectionState.Connected)
        {
            _logger.LogWarning("Already connected to SignalR Hub");
            return;
        }

        try
        {
            var token = await _localStorage.GetItemAsync<string>("authToken");
            if (string.IsNullOrEmpty(token))
            {
                _logger.LogWarning("No auth token found, cannot connect to SignalR Hub");
                return;
            }

            var gwUrl = (_configuration["GW_URL"] ?? _configuration["GwUrl"])?.TrimEnd('/') ?? "https://localhost:5002";
            var hubPath = _configuration["SignalR:HubPath"] ?? "/hubs/token-relay";
            var hubUrl = $"{gwUrl}{hubPath}";
            
            _logger.LogInformation("Connecting to SignalR Hub at {HubUrl}", hubUrl);

            _hubConnection = new HubConnectionBuilder()
                .WithUrl(hubUrl, options =>
                {
                    options.AccessTokenProvider = () => Task.FromResult<string?>(token);
                })
                .WithAutomaticReconnect(new[] 
                { 
                    TimeSpan.FromSeconds(0), 
                    TimeSpan.FromSeconds(2), 
                    TimeSpan.FromSeconds(5), 
                    TimeSpan.FromSeconds(10),
                    TimeSpan.FromSeconds(30)
                })
                .Build();

            RegisterEventHandlers();

            await _hubConnection.StartAsync();
            
            _logger.LogInformation("Successfully connected to SignalR Hub");
            OnConnectionStateChanged?.Invoke(HubConnectionState.Connected);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to connect to SignalR Hub");
            OnConnectionStateChanged?.Invoke(HubConnectionState.Disconnected);
        }
    }

    /// <summary>
    /// Disconnect from the SignalR Hub
    /// </summary>
    public async Task DisconnectAsync()
    {
        if (_hubConnection != null)
        {
            await _hubConnection.StopAsync();
            _logger.LogInformation("Disconnected from SignalR Hub");
            OnConnectionStateChanged?.Invoke(HubConnectionState.Disconnected);
        }
    }

    private void RegisterEventHandlers()
    {
        if (_hubConnection == null) return;

        // Handle TokenRevoked event
        _hubConnection.On<TokenRevokedEventArgs>("TokenRevoked", async args =>
        {
            _logger.LogInformation("Received TokenRevoked notification. UserId: {UserId}, Reason: {Reason}", 
                args.UserId, args.Reason);
            
            if (OnTokenRevoked != null)
            {
                await OnTokenRevoked.Invoke(args);
            }
        });

        // Handle ForceRefreshToken event
        _hubConnection.On<ForceRefreshEventArgs>("ForceRefreshToken", async args =>
        {
            _logger.LogInformation("Received ForceRefreshToken notification. UserId: {UserId}", args.UserId);
            
            if (OnForceRefreshToken != null)
            {
                await OnForceRefreshToken.Invoke(args);
            }
        });

        // Handle BroadcastMessage event
        _hubConnection.On<BroadcastMessageEventArgs>("BroadcastMessage", async args =>
        {
            _logger.LogInformation("Received BroadcastMessage: {Message}", args.Message);
            
            if (OnBroadcastMessage != null)
            {
                await OnBroadcastMessage.Invoke(args);
            }
        });

        // Handle connection state changes
        _hubConnection.Reconnecting += error =>
        {
            _logger.LogWarning(error, "SignalR connection lost. Attempting to reconnect...");
            OnConnectionStateChanged?.Invoke(HubConnectionState.Reconnecting);
            return Task.CompletedTask;
        };

        _hubConnection.Reconnected += connectionId =>
        {
            _logger.LogInformation("SignalR reconnected. ConnectionId: {ConnectionId}", connectionId);
            OnConnectionStateChanged?.Invoke(HubConnectionState.Connected);
            return Task.CompletedTask;
        };

        _hubConnection.Closed += async error =>
        {
            _logger.LogWarning(error, "SignalR connection closed");
            OnConnectionStateChanged?.Invoke(HubConnectionState.Disconnected);
            await Task.CompletedTask;
        };
    }

    public async ValueTask DisposeAsync()
    {
        if (_hubConnection != null)
        {
            await _hubConnection.DisposeAsync();
        }
    }
}

/// <summary>
/// Event arguments for token revoked notification
/// </summary>
public class TokenRevokedEventArgs
{
    public long UserId { get; set; }
    public string Reason { get; set; } = string.Empty;
    public DateTime Timestamp { get; set; }
}

/// <summary>
/// Event arguments for force refresh token notification
/// </summary>
public class ForceRefreshEventArgs
{
    public long UserId { get; set; }
    public DateTime Timestamp { get; set; }
}

/// <summary>
/// Event arguments for broadcast message notification
/// </summary>
public class BroadcastMessageEventArgs
{
    public string Message { get; set; } = string.Empty;
    public DateTime Timestamp { get; set; }
}
