using System.Collections.Concurrent;
using LightlessSync.API.Dto.User;
using LightlessSync.LightlessConfiguration;
using LightlessSync.LightlessConfiguration.Models;
using LightlessSync.Services.Mediator;
using LightlessSync.Services.ServerConfiguration;
using Microsoft.Extensions.Logging;

namespace LightlessSync.PlayerData.Pairs;

/// <summary>
///     wires mediator events into the pair system
/// </summary>
public sealed partial class PairCoordinator : MediatorSubscriberBase
{
    private readonly ILogger<PairCoordinator> _logger;
    private readonly LightlessConfigService _configService;
    private readonly LightlessMediator _mediator;
    private readonly PairHandlerRegistry _handlerRegistry;
    private readonly PairManager _pairManager;
    private readonly PairLedger _pairLedger;
    private readonly ServerConfigurationManager _serverConfigurationManager;
    private readonly PairPerformanceMetricsCache _metricsCache;
    private readonly ConcurrentDictionary<string, OnlineUserCharaDataDto> _pendingCharacterData = new(StringComparer.Ordinal);

    public PairCoordinator(
        ILogger<PairCoordinator> logger,
        LightlessConfigService configService,
        LightlessMediator mediator,
        PairHandlerRegistry handlerRegistry,
        PairManager pairManager,
        PairLedger pairLedger,
        ServerConfigurationManager serverConfigurationManager,
        PairPerformanceMetricsCache metricsCache)
        : base(logger, mediator)
    {
        _logger = logger;
        _configService = configService;
        _mediator = mediator;
        _handlerRegistry = handlerRegistry;
        _pairManager = pairManager;
        _pairLedger = pairLedger;
        _serverConfigurationManager = serverConfigurationManager;
        _metricsCache = metricsCache;

        mediator.Subscribe<ActiveServerChangedMessage>(this, msg => HandleActiveServerChange(msg.ServerUrl));
        mediator.Subscribe<DisconnectedMessage>(this, _ => HandleDisconnected());
    }

    internal PairLedger Ledger => _pairLedger;

    private void PublishPairDataChanged(bool groupChanged = false)
    {
        _mediator.Publish(new RefreshUiMessage());
        _mediator.Publish(new PairDataChangedMessage());
        if (groupChanged)
        {
            _mediator.Publish(new GroupCollectionChangedMessage());
        }
    }

    private void NotifyUserOnline(PairConnection? connection, bool sendNotification)
    {
        if (connection is null)
        {
            return;
        }

        var config = _configService.Current;
        if (config.ShowOnlineNotifications && _logger.IsEnabled(LogLevel.Debug))
        {
            _logger.LogDebug("Pair {Uid} marked online", connection.User.UID);
        }

        if (!sendNotification || !config.ShowOnlineNotifications)
        {
            return;
        }

        if (config.ShowOnlineNotificationsOnlyForIndividualPairs &&
            (!connection.IsDirectlyPaired || connection.IsOneSided))
        {
            return;
        }

        var note = _serverConfigurationManager.GetNoteForUid(connection.User.UID);
        if (config.ShowOnlineNotificationsOnlyForNamedPairs &&
            string.IsNullOrEmpty(note))
        {
            return;
        }

        var message = !string.IsNullOrEmpty(note)
            ? $"{note} ({connection.User.AliasOrUID}) is now online"
            : $"{connection.User.AliasOrUID} is now online";

        _mediator.Publish(new NotificationMessage("User online", message, NotificationType.Info, TimeSpan.FromSeconds(5)));
    }

    private void ReapplyLastKnownData(string userId, string ident, bool forced = false)
    {
        var result = _handlerRegistry.ApplyLastReceivedData(new PairUniqueIdentifier(userId), ident, forced);
        if (!result.Success && _logger.IsEnabled(LogLevel.Debug))
        {
            _logger.LogDebug("Failed to reapply cached data for {Uid}: {Error}", userId, result.Error);
        }
    }

    private void HandleActiveServerChange(string serverUrl)
    {
        if (_logger.IsEnabled(LogLevel.Debug))
        {
            _logger.LogDebug("Active server changed to {Server}", serverUrl);
        }

        ResetPairState();
    }

    private void HandleDisconnected()
    {
        if (_logger.IsEnabled(LogLevel.Debug))
        {
            _logger.LogDebug("Lightless disconnected, clearing pair state");
        }

        ResetPairState();
    }

    private void ResetPairState()
    {
        _handlerRegistry.ResetAllHandlers();
        _pairManager.ClearAll();
        _pendingCharacterData.Clear();
        _metricsCache.ClearAll();
        _mediator.Publish(new ClearProfileUserDataMessage());
        _mediator.Publish(new ClearProfileGroupDataMessage());
        PublishPairDataChanged(groupChanged: true);
    }
}
