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

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

/// <summary>
/// Универсальный обработчик действий с сообщениями (Edit, Delete, Reactions) для Федерации.
/// </summary>
public sealed class FederatedMessageActionsHandler : 
    INotificationHandler<MessageEditedDomainEvent>,
    INotificationHandler<MessageDeletedDomainEvent>,
    INotificationHandler<MessageReactionAddedDomainEvent>,
    INotificationHandler<MessageReactionRemovedDomainEvent>
{
    private readonly IChatRepository _chatRepository;
    private readonly IUserRepository _userRepository;
    private readonly ISettingsService _settingsService;
    private readonly FederationPacketService _packetService;
    private readonly IFederationGateway _gateway;

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

    public async Task Handle(MessageEditedDomainEvent notification, CancellationToken cancellationToken) 
        => await ProcessAction(notification.ChatId, notification.MessageId, "message_edited", notification.NewContent, cancellationToken);

    public async Task Handle(MessageDeletedDomainEvent notification, CancellationToken cancellationToken)
        => await ProcessAction(notification.ChatId, notification.MessageId, "message_deleted", string.Empty, cancellationToken);

    public async Task Handle(MessageReactionAddedDomainEvent notification, CancellationToken cancellationToken)
        => await ProcessAction(notification.ChatId, notification.MessageId, "reaction_added", notification.Emoji, cancellationToken, notification.UserId);

    public async Task Handle(MessageReactionRemovedDomainEvent notification, CancellationToken cancellationToken)
        => await ProcessAction(notification.ChatId, notification.MessageId, "reaction_removed", notification.Emoji, cancellationToken, notification.UserId);

    private async Task ProcessAction(Guid chatId, Guid messageId, string actionType, string payload, CancellationToken cancellationToken, Guid actorId = default)
    {
        var settings = await _settingsService.GetSettingsAsync(cancellationToken);
        if (!settings.Federation.Enabled) return;

        var chat = await _chatRepository.GetByIdAsync(chatId, cancellationToken);
        if (chat == null) return;

        var externalDomains = await GetExternalDomains(chat, cancellationToken);
        if (!externalDomains.Any()) return;

        var metadata = new FederationMetadata(
            chatId,
            messageId, // MessageId в SenderId для операций
            "system",
            actionType,
            DateTime.UtcNow,
            actorId
        );

        foreach (var domain in externalDomains)
        {
            var packetResult = await _packetService.PreparePacketAsync(payload, metadata, domain, cancellationToken);
            if (packetResult.IsSuccess)
            {
                await _gateway.SendPacketAsync(packetResult.Value, domain, cancellationToken);
            }
        }
    }

    private async Task<List<string>> GetExternalDomains(Chat chat, CancellationToken cancellationToken)
    {
        var memberIds = chat.Members.Select(m => m.UserId).ToList();
        var members = await _userRepository.GetByIdsAsync(memberIds, cancellationToken);
        
        return members
            .Where(u => u.IsExternal && !string.IsNullOrEmpty(u.Domain))
            .Select(u => u.Domain!)
            .Distinct()
            .ToList();
    }
}

