using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using MediatR;
using NSubstitute;
using Xunit;
using Knot.Shared.Kernel;
using Knot.Modules.Conversations.Domain;
using Knot.Modules.Messaging.Domain;
using Knot.Modules.Conversations.Application.Abstractions;
using Knot.Modules.Conversations.Application.Messages.Send;
using Knot.Modules.Settings.Application.Settings.Abstractions;
using Knot.Modules.Settings.Application.Settings.DTOs;

namespace Knot.Modules.Conversations.UnitTests.Messages;

public class SendMessageCommandHandlerTests
{
    private readonly IChatRepository _chatRepository;
    private readonly IMessageRepository _messageRepository;
    private readonly IChatsUnitOfWork _unitOfWork;
    private readonly IMediator _mediator;
    private readonly IMessagesSettings _messagesSettings;
    private readonly SendMessageCommandHandler _handler;

    public SendMessageCommandHandlerTests()
    {
        _chatRepository = Substitute.For<IChatRepository>();
        _messageRepository = Substitute.For<IMessageRepository>();
        _unitOfWork = Substitute.For<IChatsUnitOfWork>();
        _mediator = Substitute.For<IMediator>();
        _messagesSettings = Substitute.For<IMessagesSettings>();
        
        var config = new Knot.Modules.Settings.Application.Settings.DTOs.MessagesConfig();
        _messagesSettings.Current.Returns(config);

        _handler = new SendMessageCommandHandler(_chatRepository, _messageRepository, _unitOfWork, _mediator, _messagesSettings);
    }

    [Fact]
    public async Task Handle_ShouldReturnFailure_WhenChatNotFound()
    {
        // Arrange
        var command = new SendMessageCommand(Guid.NewGuid(), Guid.NewGuid(), "Hello", "text");
        _chatRepository.GetByIdAsync(command.ChatId, Arg.Any<CancellationToken>())
            .Returns((Chat)null!);

        // Act
        var result = await _handler.Handle(command, CancellationToken.None);

        // Assert
        result.IsFailure.Should().BeTrue();
        result.Error.Code.Should().Be(ChatErrors.ChatsNotFound.Code);
    }

    [Fact]
    public async Task Handle_ShouldReturnFailure_WhenSenderIsNotMember()
    {
        // Arrange
        var chat = Chat.Create("Test", ChatType.Group);
        var command = new SendMessageCommand(chat.Id, Guid.NewGuid(), "Hello", "text");
        
        _chatRepository.GetByIdAsync(command.ChatId, Arg.Any<CancellationToken>())
            .Returns(chat);

        // Act
        var result = await _handler.Handle(command, CancellationToken.None);

        // Assert
        result.IsFailure.Should().BeTrue();
        result.Error.Code.Should().Be(ChatErrors.ChatsForbidden.Code);
    }

    [Fact]
    public async Task Handle_ShouldCreateMessage_WhenAuthorized()
    {
        // Arrange
        var senderId = Guid.NewGuid();
        var chat = Chat.Create("Test", ChatType.Group);
        chat.AddMember(senderId, ChatRole.Member);

        var command = new SendMessageCommand(chat.Id, senderId, "Hello", "text");
        
        _chatRepository.GetByIdAsync(command.ChatId, Arg.Any<CancellationToken>())
            .Returns(chat);

        // Act
        var result = await _handler.Handle(command, CancellationToken.None);

        // Assert
        result.IsSuccess.Should().BeTrue();
        result.Value.Should().NotBeEmpty();

        _messageRepository.Received(1).Add(Arg.Is<Message>(m => 
            m.ChatId == chat.Id &&
            m.SenderId == senderId &&
            m.Content == "Hello" &&
            m.Type == "text"));

        await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any<CancellationToken>());
    }
}


