using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using WhatsAppService.API.Application.Commands;
using WhatsAppService.Domain.AggregatesModel.WhatsAppAccountAggregate;
using WhatsAppService.Domain.SeedWork;
using WhatsAppService.Infrastructure.Security;
using Xunit;

namespace WhatsAppService.UnitTests.Application;

/// <summary>
/// EN: Unit tests for ConnectWhatsAppAccountCommandHandler.
/// VI: Unit tests cho ConnectWhatsAppAccountCommandHandler.
/// </summary>
public class ConnectWhatsAppAccountCommandHandlerTests
{
    private readonly Mock<IWhatsAppAccountRepository> _repositoryMock = new();
    private readonly Mock<IAccessTokenEncryptionService> _encryptionServiceMock = new();
    private readonly Mock<ILogger<ConnectWhatsAppAccountCommandHandler>> _loggerMock = new();
    private readonly Mock<IUnitOfWork> _unitOfWorkMock = new();

    private readonly ConnectWhatsAppAccountCommandHandler _handler;

    public ConnectWhatsAppAccountCommandHandlerTests()
    {
        _repositoryMock.SetupGet(repository => repository.UnitOfWork).Returns(_unitOfWorkMock.Object);
        _unitOfWorkMock
            .Setup(unitOfWork => unitOfWork.SaveEntitiesAsync(It.IsAny<CancellationToken>()))
            .ReturnsAsync(true);

        _handler = new ConnectWhatsAppAccountCommandHandler(
            _repositoryMock.Object,
            _encryptionServiceMock.Object,
            _loggerMock.Object);
    }

    [Fact]
    public async Task Handle_WhenShopAndPhoneAvailable_ShouldEncryptTokenAndPersistAccount()
    {
        // Arrange
        var command = new ConnectWhatsAppAccountCommand(
            Guid.NewGuid(),
            "phone-001",
            "plain-token",
            "https://hook.example");

        _repositoryMock.Setup(repository => repository.GetByShopIdAsync(command.ShopId, It.IsAny<CancellationToken>()))
            .ReturnsAsync((WhatsAppAccount?)null);
        _repositoryMock.Setup(repository => repository.GetByPhoneNumberIdAsync(command.PhoneNumberId, It.IsAny<CancellationToken>()))
            .ReturnsAsync((WhatsAppAccount?)null);
        _encryptionServiceMock.Setup(service => service.Encrypt(command.AccessToken))
            .Returns("encrypted-token");
        _repositoryMock.Setup(repository => repository.AddAsync(It.IsAny<WhatsAppAccount>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync((WhatsAppAccount account, CancellationToken _) => account);

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

        // Assert
        result.Success.Should().BeTrue();
        result.AccountId.Should().NotBeNull();
        _encryptionServiceMock.Verify(service => service.Encrypt(command.AccessToken), Times.Once);
        _repositoryMock.Verify(repository => repository.AddAsync(
            It.Is<WhatsAppAccount>(account => account.AccessTokenEncrypted == "encrypted-token"),
            It.IsAny<CancellationToken>()), Times.Once);
    }

    [Fact]
    public async Task Handle_WhenShopAlreadyHasAccount_ShouldReturnFailure()
    {
        // Arrange
        var existingAccount = new WhatsAppAccount(Guid.NewGuid(), "phone-existing", "encrypted");
        var command = new ConnectWhatsAppAccountCommand(
            existingAccount.ShopId,
            "phone-new",
            "plain-token",
            null);

        _repositoryMock.Setup(repository => repository.GetByShopIdAsync(command.ShopId, It.IsAny<CancellationToken>()))
            .ReturnsAsync(existingAccount);

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

        // Assert
        result.Success.Should().BeFalse();
        result.Error.Should().Be("Shop already has a connected WhatsApp account");
        _repositoryMock.Verify(repository => repository.AddAsync(It.IsAny<WhatsAppAccount>(), It.IsAny<CancellationToken>()), Times.Never);
    }
}
