using FluentAssertions;
using FnbEngine.API.Application.Commands;
using FnbEngine.Domain.AggregatesModel.KitchenAggregate;
using FnbEngine.Domain.SeedWork;
using Moq;
using Xunit;

namespace FnbEngine.UnitTests.Application.Commands;

/// <summary>
/// EN: Unit tests for CreateKitchenTicketCommandHandler.
/// VI: Unit tests cho CreateKitchenTicketCommandHandler.
/// </summary>
public class CreateKitchenTicketCommandHandlerTests
{
    private readonly Mock<IKitchenTicketRepository> _repoMock;
    private readonly CreateKitchenTicketCommandHandler _handler;

    public CreateKitchenTicketCommandHandlerTests()
    {
        _repoMock = new Mock<IKitchenTicketRepository>();
        _repoMock.Setup(r => r.UnitOfWork.SaveEntitiesAsync(It.IsAny<CancellationToken>()))
            .ReturnsAsync(true);
        _repoMock.Setup(r => r.AddAsync(It.IsAny<KitchenTicket>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync((KitchenTicket t, CancellationToken _) => t);
        _handler = new CreateKitchenTicketCommandHandler(_repoMock.Object);
    }

    [Fact]
    public async Task Handle_WithValidCommand_ShouldCreateTicketAndReturnId()
    {
        // Arrange
        var command = new CreateKitchenTicketCommand(
            SessionId: Guid.NewGuid(),
            OrderItemId: Guid.NewGuid(),
            ItemName: "Pho Bo",
            Station: "Kitchen",
            Priority: 1);

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

        // Assert
        result.Should().NotBeEmpty();
        _repoMock.Verify(r => r.AddAsync(It.IsAny<KitchenTicket>(), It.IsAny<CancellationToken>()), Times.Once);
        _repoMock.Verify(r => r.UnitOfWork.SaveEntitiesAsync(It.IsAny<CancellationToken>()), Times.Once);
    }

    [Fact]
    public async Task Handle_WithProductIdAndQuantity_ShouldPassToEntity()
    {
        // Arrange
        var productId = Guid.NewGuid();
        var command = new CreateKitchenTicketCommand(
            SessionId: Guid.NewGuid(),
            OrderItemId: Guid.NewGuid(),
            ItemName: "Bun Cha",
            ProductId: productId,
            Quantity: 3);

        KitchenTicket? capturedTicket = null;
        _repoMock.Setup(r => r.AddAsync(It.IsAny<KitchenTicket>(), It.IsAny<CancellationToken>()))
            .Callback<KitchenTicket, CancellationToken>((t, _) => capturedTicket = t)
            .ReturnsAsync((KitchenTicket t, CancellationToken _) => t);

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

        // Assert
        capturedTicket.Should().NotBeNull();
        capturedTicket!.ProductId.Should().Be(productId);
        capturedTicket.Quantity.Should().Be(3);
    }

    [Fact]
    public async Task Handle_WithoutProductId_ShouldUseOrderItemIdAsProductId()
    {
        // Arrange
        var orderItemId = Guid.NewGuid();
        var command = new CreateKitchenTicketCommand(
            SessionId: Guid.NewGuid(),
            OrderItemId: orderItemId,
            ItemName: "Ca Phe");

        KitchenTicket? capturedTicket = null;
        _repoMock.Setup(r => r.AddAsync(It.IsAny<KitchenTicket>(), It.IsAny<CancellationToken>()))
            .Callback<KitchenTicket, CancellationToken>((t, _) => capturedTicket = t)
            .ReturnsAsync((KitchenTicket t, CancellationToken _) => t);

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

        // Assert
        capturedTicket.Should().NotBeNull();
        capturedTicket!.ProductId.Should().Be(orderItemId);
    }

    [Fact]
    public void Constructor_WithNullRepository_ShouldThrowArgumentNullException()
    {
        // Act
        var action = () => new CreateKitchenTicketCommandHandler(null!);

        // Assert
        action.Should().Throw<ArgumentNullException>();
    }
}
