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

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

public sealed class MessageSentDomainEventHandler : INotificationHandler<MessageSentDomainEvent>
{
    private readonly IChatRepository _chatRepository;
    private readonly IUserRepository _userRepository;
    private readonly ISettingsService _settingsService;
    private readonly FederationPacketService _packetService;
    private readonly IFederationGateway _gateway;

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

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

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

        var memberIds = chat.Members.Select(m => m.UserId).ToList();
        var members = await _userRepository.GetByIdsAsync(memberIds, cancellationToken);

        var externalDomains = members
            .Where(u => u.IsExternal && !string.IsNullOrEmpty(u.Domain))
            .Select(u => u.Domain!)
            .Distinct()
            .ToList();

        if (!externalDomains.Any()) return;

        var sender = await _userRepository.GetByIdAsync(notification.SenderId, cancellationToken);
        var senderUsername = sender?.Username ?? "unknown";

        var metadata = new FederationMetadata(
            notification.ChatId,
            notification.SenderId,
            senderUsername,
            "text",
            DateTime.UtcNow
        );

        foreach (var domain in externalDomains)
        {
            var packetResult = await _packetService.PreparePacketAsync(
                notification.Content ?? string.Empty,
                metadata,
                domain,
                cancellationToken);

            if (packetResult.IsSuccess)
            {
                await _gateway.SendPacketAsync(packetResult.Value, domain, cancellationToken);
            }
        }
    }
}

