using Knot.Contracts.Messaging.Application.Abstractions;
using Knot.Contracts.Messaging.Domain;
using Knot.Contracts.Conversations.Application.Abstractions;
using Knot.Contracts.Conversations.Domain;
using Knot.Shared.Kernel;
using MediatR;

namespace Knot.Modules.Conversations.Application.Messages.Pin;

public sealed record PinMessageCommand(Guid MessageId, Guid ChatId, Guid UserId) : ICommand<Guid>;

public sealed class PinMessageCommandHandler : ICommandHandler<PinMessageCommand, Guid>
{
    private readonly IMessageRepository _messageRepository;
    private readonly IChatRepository _chatRepository;
    private readonly IChatsUnitOfWork _unitOfWork;
    private readonly IMediator _mediator;

    public PinMessageCommandHandler(
        IMessageRepository messageRepository,
        IChatRepository chatRepository,
        IChatsUnitOfWork unitOfWork,
        IMediator mediator)
    {
        _messageRepository = messageRepository;
        _chatRepository = chatRepository;
        _unitOfWork = unitOfWork;
        _mediator = mediator;
    }

    public async Task<Result<Guid>> Handle(PinMessageCommand request, CancellationToken cancellationToken)
    {
        var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
        if (chat is null) return Result.Failure<Guid>(ChatErrors.ChatsNotFound);

        // Security check
        if (!chat.Members.Any(m => m.UserId == request.UserId))
            return Result.Failure<Guid>(ChatErrors.ChatsForbidden);

        var message = await _messageRepository.GetByIdAsync(request.MessageId, cancellationToken);
        if (message is null) return Result.Failure<Guid>(ChatErrors.NotFound);

        if (message.ChatId != request.ChatId)
            return Result.Failure<Guid>(ChatErrors.NotFound);

        message.AddState(MessageState.IsPinned);
        
        await _messageRepository.UpdateAsync(message, cancellationToken);
        await _unitOfWork.SaveChangesAsync(cancellationToken);

        // Notify chat about pinned message change
        await _mediator.Publish(new MessagePinnedDomainEvent(message.Id, message.ChatId, message.SenderId, message.Content), cancellationToken);

        return Result.Success(message.Id);
    }
}

public record MessagePinnedDomainEvent(Guid MessageId, Guid ChatId, Guid SenderId, string? Content) : INotification;
