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

namespace FnbEngine.UnitTests.Application.Commands;

/// <summary>
/// EN: Unit tests for CreateReservationCommandHandler.
/// VI: Unit tests cho CreateReservationCommandHandler.
/// </summary>
public class CreateReservationCommandHandlerTests
{
    private readonly Mock<IReservationRepository> _repoMock;
    private readonly CreateReservationCommandHandler _handler;

    public CreateReservationCommandHandlerTests()
    {
        _repoMock = new Mock<IReservationRepository>();
        _repoMock.Setup(r => r.UnitOfWork.SaveEntitiesAsync(It.IsAny<CancellationToken>()))
            .ReturnsAsync(true);
        _repoMock.Setup(r => r.AddAsync(It.IsAny<Reservation>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync((Reservation r, CancellationToken _) => r);
        _handler = new CreateReservationCommandHandler(_repoMock.Object);
    }

    [Fact]
    public async Task Handle_WithValidCommand_ShouldCreateReservationAndReturnId()
    {
        // Arrange
        var command = new CreateReservationCommand(
            ShopId: Guid.NewGuid(),
            GuestName: "Nguyen Van A",
            PartySize: 4,
            ReservationTime: DateTime.UtcNow.AddHours(2),
            Phone: "0901234567");

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

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

    [Fact]
    public async Task Handle_WithAllOptionalParams_ShouldCreateReservationWithAllFields()
    {
        // Arrange
        var tableId = Guid.NewGuid();
        var command = new CreateReservationCommand(
            ShopId: Guid.NewGuid(),
            GuestName: "Tran Thi B",
            PartySize: 6,
            ReservationTime: DateTime.UtcNow.AddHours(3),
            Phone: "0909876543",
            TableId: tableId,
            Note: "Birthday party");

        Reservation? capturedReservation = null;
        _repoMock.Setup(r => r.AddAsync(It.IsAny<Reservation>(), It.IsAny<CancellationToken>()))
            .Callback<Reservation, CancellationToken>((res, _) => capturedReservation = res)
            .ReturnsAsync((Reservation r, CancellationToken _) => r);

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

        // Assert
        capturedReservation.Should().NotBeNull();
        capturedReservation!.TableId.Should().Be(tableId);
        capturedReservation.Note.Should().Be("Birthday party");
        capturedReservation.Status.Should().Be("pending");
    }

    [Fact]
    public void Constructor_WithNullRepository_ShouldThrowArgumentNullException()
    {
        var action = () => new CreateReservationCommandHandler(null!);
        action.Should().Throw<ArgumentNullException>();
    }
}
