using FluentAssertions;
using FnbEngine.Domain.AggregatesModel.KitchenAggregate;
using FnbEngine.Domain.Events;
using Xunit;

namespace FnbEngine.UnitTests.Domain;

/// <summary>
/// EN: Unit tests for KitchenTicket aggregate behavior.
/// VI: Unit tests cho hanh vi aggregate KitchenTicket.
/// </summary>
public class KitchenTicketTests
{
    private static readonly Guid ValidSessionId = Guid.NewGuid();
    private static readonly Guid ValidOrderItemId = Guid.NewGuid();
    private static readonly Guid ValidProductId = Guid.NewGuid();

    [Fact]
    public void Constructor_WithAllParams_ShouldCreatePendingTicket()
    {
        // Act
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Pho Bo", 1, "Kitchen", 1);

        // Assert
        ticket.Id.Should().NotBeEmpty();
        ticket.SessionId.Should().Be(ValidSessionId);
        ticket.OrderItemId.Should().Be(ValidOrderItemId);
        ticket.ProductId.Should().Be(ValidProductId);
        ticket.ItemName.Should().Be("Pho Bo");
        ticket.Station.Should().Be("Kitchen");
        ticket.Priority.Should().Be(1);
        ticket.Quantity.Should().Be(1);
        ticket.Status.Should().Be("Pending");
        ticket.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5));
        ticket.CompletedAt.Should().BeNull();
    }

    [Fact]
    public void Constructor_WithProductIdAndQuantity_ShouldSetCorrectValues()
    {
        // Act
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Bun Cha", 3, "Kitchen", 2);

        // Assert
        ticket.ProductId.Should().Be(ValidProductId);
        ticket.Quantity.Should().Be(3);
        ticket.ItemName.Should().Be("Bun Cha");
        ticket.Priority.Should().Be(2);
    }

    [Fact]
    public void Constructor_WithZeroQuantity_ShouldDefaultToOne()
    {
        // Act
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Item", 0);

        // Assert
        ticket.Quantity.Should().Be(1);
    }

    [Fact]
    public void Constructor_WithNegativeQuantity_ShouldDefaultToOne()
    {
        // Act
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Item", -5);

        // Assert
        ticket.Quantity.Should().Be(1);
    }

    [Fact]
    public void Constructor_WithNullStation_ShouldAllowNull()
    {
        // Act
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Drink");

        // Assert
        ticket.Station.Should().BeNull();
    }

    [Fact]
    public void MarkAsInProgress_ShouldChangeStatusToInProgress()
    {
        // Arrange
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Pho Bo");

        // Act
        ticket.MarkAsInProgress();

        // Assert
        ticket.Status.Should().Be("InProgress");
        ticket.CompletedAt.Should().BeNull();
    }

    [Fact]
    public void MarkAsReady_ShouldChangeStatusAndSetCompletedAt()
    {
        // Arrange
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Pho Bo");

        // Act
        ticket.MarkAsReady();

        // Assert
        ticket.Status.Should().Be("Ready");
        ticket.CompletedAt.Should().NotBeNull();
        ticket.CompletedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5));
    }

    [Fact]
    public void MarkAsServed_ShouldChangeStatusToServed()
    {
        // Arrange
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Pho Bo");

        // Act
        ticket.MarkAsServed();

        // Assert
        ticket.Status.Should().Be("Served");
    }

    [Fact]
    public void MarkAsServed_ShouldRaiseKitchenTicketServedDomainEvent()
    {
        // Arrange
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Pho Bo");

        // Act
        ticket.MarkAsServed();

        // Assert
        ticket.DomainEvents.Should().ContainSingle();
        ticket.DomainEvents.Should().ContainSingle(e => e is KitchenTicketServedDomainEvent);
        var domainEvent = ticket.DomainEvents.OfType<KitchenTicketServedDomainEvent>().Single();
        domainEvent.Ticket.Should().BeSameAs(ticket);
    }

    [Fact]
    public void MarkAsServed_CalledTwice_ShouldRaiseTwoDomainEvents()
    {
        // Arrange
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Pho Bo");

        // Act
        ticket.MarkAsServed();
        ticket.MarkAsServed();

        // Assert
        ticket.DomainEvents.Should().HaveCount(2);
    }

    [Fact]
    public void StatusTransition_PendingToInProgressToReadyToServed_ShouldSucceed()
    {
        // Arrange
        var ticket = new KitchenTicket(ValidSessionId, ValidOrderItemId, ValidProductId, "Pho Bo");

        // Act & Assert
        ticket.MarkAsInProgress();
        ticket.Status.Should().Be("InProgress");

        ticket.MarkAsReady();
        ticket.Status.Should().Be("Ready");

        ticket.MarkAsServed();
        ticket.Status.Should().Be("Served");
    }
}
