using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Knot.Contracts.Auth.Domain;
using Knot.Contracts.Conversations.Domain;
using Knot.Modules.Federation.Application.Abstractions;
using Knot.Modules.Federation.Application.Federation.Services;
using Knot.Contracts.Settings.Application.Abstractions;
using Knot.Contracts.Settings.Application.DTOs;

namespace Knot.Modules.Federation.Application.Federation.Events;

/// <summary>
/// Обработчик изменения статуса пользователя.
/// Отправляет статус всем внешним контактам, а также подписчикам чатов.
/// </summary>
public sealed class UserStatusChangedDomainEventHandler : INotificationHandler<UserStatusChangedDomainEvent>
{
    private readonly IChatRepository _chatRepository;
    private readonly IUserRepository _userRepository;
    private readonly ISettingsService _settingsService;
    private readonly FederationPacketService _packetService;
    private readonly IFederationGateway _gateway;

    public UserStatusChangedDomainEventHandler(
        IChatRepository chatRepository,
        IUserRepository userRepository,
        ISettingsService settingsService,
        FederationPacketService packetService,
        IFederationGateway gateway)
    {
        _chatRepository = chatRepository;
        _userRepository = userRepository;
        _settingsService = settingsService;
        _packetService = packetService;
        _gateway = gateway;
    }

    public async Task Handle(UserStatusChangedDomainEvent notification, CancellationToken cancellationToken)
    {
        var settings = await _settingsService.GetSettingsAsync(cancellationToken);
        if (!settings.Federation.Enabled) return;

        // 1. Получаем все чаты пользователя
        var userChats = await _chatRepository.GetUserChatsAsync(notification.UserId, cancellationToken);
        if (!userChats.Any()) return;

        // 2. Получаем уникальные ID участников всех чатов пользователя
        var allMemberIds = userChats.SelectMany(c => c.Members.Select(m => m.UserId)).Distinct().ToList();
        var members = await _userRepository.GetByIdsAsync(allMemberIds, cancellationToken);
        
        var externalDomains = members
            .Where(u => u.IsExternal && !string.IsNullOrEmpty(u.Domain))
            .Select(u => u.Domain!)
            .Distinct()
            .ToList();

        if (!externalDomains.Any()) return;

        // 3. Формируем данные статуса
        var statusData = new {
            notification.IsOnline,
            notification.LastSeen
        };
        var payload = System.Text.Json.JsonSerializer.Serialize(statusData);

        var metadata = new FederationMetadata(
            Guid.Empty,
            notification.UserId,
            "system",
            "presence_update",
            DateTime.UtcNow
        );

        // 4. Отправляем по доменам
        foreach (var domain in externalDomains)
        {
            var packetResult = await _packetService.PreparePacketAsync(payload, metadata, domain, cancellationToken);
            if (packetResult.IsSuccess)
            {
                await _gateway.SendPacketAsync(packetResult.Value, domain, cancellationToken);
            }
        }
    }
}
