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>();


    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);
        }
    }

    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);
    }


    [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);
        }
    }

    [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.
    [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);
        }
    }
}