using adas_core.Application.Services.Interfaces;
using adas_core.Application.Subscriptions;
using adas_core.Domain.Models.SignalR;
using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
using Serilog;
using ILogger = Serilog.ILogger;

namespace adas_core.WebSocket.Hubs;

//[Authorize]
public class UciHub(
    IClientMessageService clientMessageService,
    ISubscribersService subscribersService,
    ISubscriberGroupedService subscriberGroupedService)
    : Hub
{
    private static readonly ILogger Logger = Log.ForContext<UciHub>();


    /// <summary>
    /// Handles a new SignalR hub connection by registering the connection as a subscriber, removing any prior subscriber
    /// that shares the same connection id to avoid duplicates on reconnection, and then invoking the base connection handler.
    /// Any exception encountered during processing is logged and surfaced as a failed task.
    /// </summary>
    public override async Task OnConnectedAsync()
    {
        try
        {
            Logger.Debug("OnConnectedAsync new connection id {ContextConnectionId}", Context.ConnectionId);

            var subscriberSameId = subscribersService.GetById(Context.ConnectionId);
            if (subscriberSameId != null)
            {
                Logger.Debug(
                    "OnConnectedAsync connection id {ContextConnectionId} exists in subscribers list, removing it before inserting new one",
                    Context.ConnectionId);
                subscribersService.RemoveConnectionById(subscriberSameId.Id);
            }

            subscribersService.AddSubscriber(new WsSubscriber(Context.ConnectionId));

            await base.OnConnectedAsync();
        }
        catch (Exception ex)
        {
            Logger.Error("error on new connection {exMessage} trace: {exStackTrace}", ex.Message, ex.StackTrace);
            await Task.FromException(ex);
        }
    }

    /// <summary>
    /// Handles the disconnection of a SignalR client by logging the disconnection cause and cleaning up the
    /// connection from grouped subscribers and the main subscribers service. Errors are logged as errors
    /// when an exception is provided, while normal disconnections are logged at debug level.
    /// </summary>
    /// <param name="exception">The exception that caused the disconnection, or <c>null</c> when the disconnection was not caused by an error.</param>
    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        if (exception != null)
            Logger.Error(exception,
                "Unexpected onDisconnectedAsync received for connection id {ContextConnectionId} with exception  {exceptionMessage}",
                Context.ConnectionId, exception.Message);
        else
            Logger.Debug("onDisconnectedAsync received for connection id {ContextConnectionId}", Context.ConnectionId);

        Logger.Debug("Removing from subscribers grouped with ConnectionId {ContextConnectionId}", Context.ConnectionId);
        var grouped = subscriberGroupedService.GetGrouped();
        foreach (var wsl in grouped)
        {
            if (wsl.WsSubscriber.Any())
            {
                var removedEs = wsl.WsSubscriber.RemoveAll(ws => ws.Equals(Context.ConnectionId));
                if (removedEs > 0)
                    Logger.Verbose(
                        "Removed {removedEs} grouped subscribers with connectionId: {ContextConnectionId}", removedEs,
                        Context.ConnectionId);
            }

            var removed = subscriberGroupedService.CheckEmptySubscriberGroup(wsl);
            if (removed.Any()) Logger.Verbose($"CheckEmptySubscriberGroup: {string.Join(", ", removed)}");
        }


        var total = subscribersService.RemoveConnectionById(Context.ConnectionId);
        Logger.Debug("Removing {total} from subscribers with ConnectionId {ContextConnectionId}", total,
            Context.ConnectionId);
        Logger.Debug("Removed id: {ContextConnectionId}", Context.ConnectionId);

        await base.OnDisconnectedAsync(exception);
    }


    /// <summary>
    /// Handles a SignalR subscribe request by deserializing the incoming message, updating the subscriber
    /// information associated with the current connection, and forwarding the message for processing.
    /// Logs a warning and exits if the message cannot be deserialized or if the connection is unknown;
    /// any exception thrown during processing is caught and logged without rethrowing.
    /// </summary>
    /// <param name="subMessage">The raw JSON payload received from the client representing the subscription request.</param>
    [HubMethodName("subscribe")]
    public async Task OnSubscribe(string subMessage)
    {
        try
        {
            var msg = JsonConvert.DeserializeObject<Message>(subMessage);
            if (msg == null)
            {
                Logger.Warning("Error deserializing subscription message {subMessage}", subMessage);
                return;
            }

            var subs = subscribersService.GetById(Context.ConnectionId);

            if (subs == null)
            {
                Logger.Warning("Subscription received from unknown connection id {ContextConnectionId}",
                    Context.ConnectionId);
                return;
            }

            subs.Section = msg.Data?.Section;
            subs.Version = msg.Version ?? "Not a squirrel app";
            subs.GroupedFields = msg.Data?.GroupedFields;

            await clientMessageService.ProcessMessage(msg, Context.ConnectionId);
        }
        catch (Exception ex)
        {
            Logger.Error("Error on subscribe {exMessage}", ex);
        }
    }

    /// <summary>
    /// Unsubscribes the current SignalR client by removing its connection from the subscribers service. 
    /// Logs and swallows any error encountered during connection removal.
    /// </summary>
    [HubMethodName("unsubscribe")]
    public void UnSubscribe()
    {
        try
        {
            subscribersService.RemoveConnectionById(Context.ConnectionId);
        }
        catch (Exception ex)
        {
            Logger.Error("error on unsubscribe {exMessage}", ex.Message);
        }
    }

    //TODO revisar esto, si va a acabar llamando al mismo sitio y procesando el mensaje igual que OnSuscribe, realmente sobra.
    /// <summary>
    /// Handles incoming chat messages received via the SignalR hub. 
    /// Deserializes the payload into a <see cref="Message"/> instance and delegates processing to the client message service, logging and ignoring malformed payloads or unexpected errors.
    /// </summary>
    /// <param name="message">The raw JSON payload of the chat message sent by the client.</param>
    [HubMethodName("chatMessage")]
    public async Task OnChatMessage(string message)
    {
        try
        {
            var msg = JsonConvert.DeserializeObject<Message>(message);
            if (msg == null)
            {
                Logger.Error("Error deserializing Chat message: {message}", message);
                return;
            }

            await clientMessageService.ProcessMessage(msg, Context.ConnectionId);
        }
        catch (Exception ex)
        {
            Logger.Error("error on subscribe {exMessage} trace: {exStackTrace}", ex.Message, ex.StackTrace);
        }
    }
}