using Microsoft.AspNetCore.SignalR;
using RobotNet.MapShares.Dtos;
using RobotNet.RobotManager.HubClients;
using RobotNet.RobotManager.Hubs;

namespace RobotNet.RobotManager.Services;

public class RobotPublisher(RobotManager RobotManager, IHubContext<RobotHub> RobotHub, MapHubClient MapHub, LoggerController<RobotPublisher> Logger) : IHostedService
{
    public readonly Dictionary<Guid, string> MapActive = [];
    public readonly Dictionary<string, string> RobotDetailActive = [];

    private int PublishCounter = 0;
    private const int intervalTime = 200;

    private WatchTimerAsync<RobotPublisher>? Timer;

    private readonly Dictionary<Guid, ElementDto[]> ElementsState = [];

    private async Task TimerHandler()
    {
        try
        {
            foreach (var mapActive in MapActive)
            {
                var robotinfos = RobotManager.GetRobotInfo(mapActive.Key);
                if (robotinfos is not null && robotinfos.Any())
                {
                    await RobotHub.Clients.Client(mapActive.Value).SendAsync("UpdateChanged", robotinfos);
                }
            }

            PublishCounter++;
            if (PublishCounter >= 5)
            {
                await RobotHub.Clients.All.SendAsync("IsOnlineChanged", RobotManager.GetRobotOnlineState());
                foreach (var robotActive in RobotDetailActive)
                {
                    var robotinfos = RobotManager.GetRobotVDA5050State(robotActive.Key);
                    if (robotinfos is not null)
                    {
                        await RobotHub.Clients.Client(robotActive.Value).SendAsync("VDA5050InfoChanged", robotinfos);
                    }
                }
                foreach (var mapActive in MapActive)
                {
                    var elementsState = await MapHub.GetElementsState(mapActive.Key);
                    if (!elementsState.IsSuccess) Logger.Warning($"Robot Publisher Error: Lấy dữ liệu element xảy ra lỗi: {elementsState.Message}");
                    else if (elementsState.Data?.Length > 0)
                    {
                        ElementsState.TryGetValue(mapActive.Key, out ElementDto[]? elementsOlder);
                        List<ElementDto> updateElementsState = [];
                        foreach (var element in elementsState.Data)
                        {
                            if (elementsOlder is null || !elementsOlder.Any(e => e.Id == element.Id) ||
                                elementsOlder.Any(e => e.Id == element.Id && (e.IsOpen != element.IsOpen || e.OffsetX != element.OffsetX || e.OffsetY != element.OffsetY)))
                            {
                                updateElementsState.Add(element);
                            }
                        }
                        if (updateElementsState is not null && updateElementsState.Count != 0)
                        {
                            ElementsState[mapActive.Key] = elementsState.Data;
                            await RobotHub.Clients.Client(mapActive.Value).SendAsync("ElementsStateChanged", updateElementsState);
                        }
                    }
                }
                PublishCounter = 0;
            }

        }
        catch (Exception ex)
        {
            Logger.Error($"Robot Publisher Error: {ex}");
        }
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        await Task.Yield();

        while (!cancellationToken.IsCancellationRequested)
        {
            try
            {
                Timer = new(intervalTime, TimerHandler, Logger);
                Timer.Start();
                await MapHub.StartAsync();
                break;
            }

            catch (Exception ex)
            {
                Logger.Warning($"Robot Publisher Start: Khởi tạo kết nối với MapManager có lỗi xảy ra: {ex.Message}");
                await Task.Delay(2000, cancellationToken);
            }
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        Timer?.Dispose();
        return Task.CompletedTask;
    }
}
