namespace WalletService.UnitTests.Domain;

using FluentAssertions;
using WalletService.Domain.AggregatesModel.WalletAggregate;
using WalletService.Domain.Exceptions;
using Xunit;

public class HoldItemTests
{
    [Fact]
    public void Create_ValidArguments_ShouldCreateHoldItem()
    {
        // Arrange
        var walletId = Guid.NewGuid();
        var amount = 100m;
        var currency = CurrencyType.VND;
        var refType = "CAMPAIGN";
        var refId = Guid.NewGuid();
        var desc = "Test Hold";

        // Act
        var hold = new HoldItem(walletId, amount, currency, refType, refId, desc);

        // Assert
        hold.WalletId.Should().Be(walletId);
        hold.OriginalAmount.Should().Be(amount);
        hold.RemainingAmount.Should().Be(amount);
        hold.ExecutedAmount.Should().Be(0);
        hold.ReleasedAmount.Should().Be(0);
        hold.Status.Should().Be(HoldStatus.Active);
        hold.ReferenceType.Should().Be(refType);
        hold.ReferenceId.Should().Be(refId);
        hold.Description.Should().Be(desc);
    }

    [Fact]
    public void Execute_ValidAmount_ShouldReduceRemainingAndIncreaseExecuted()
    {
        // Arrange
        var hold = CreateHold(100m);
        var execAmount = 40m;

        // Act
        hold.Execute(execAmount, "EXEC01");

        // Assert
        hold.RemainingAmount.Should().Be(60m);
        hold.ExecutedAmount.Should().Be(40m);
        hold.Status.Should().Be(HoldStatus.Active); // Still active as remaining > 0
    }

    [Fact]
    public void Execute_FullAmount_ShouldMarkAsExecuted()
    {
        // Arrange
        var hold = CreateHold(100m);

        // Act
        hold.Execute(100m, "EXEC_FULL");

        // Assert
        hold.RemainingAmount.Should().Be(0);
        hold.ExecutedAmount.Should().Be(100m);
        hold.Status.Should().Be(HoldStatus.Executed);
    }

    [Fact]
    public void Execute_ExcessAmount_ShouldThrowException()
    {
        // Arrange
        var hold = CreateHold(100m);

        // Act & Assert
        var act = () => hold.Execute(101m);
        act.Should().Throw<WalletDomainException>()
            .WithMessage("*Cannot execute*");
    }

    [Fact]
    public void Release_ValidAmount_ShouldReduceRemainingAndIncreaseReleased()
    {
        // Arrange
        var hold = CreateHold(100m);
        var releaseAmount = 30m;

        // Act
        hold.Release(releaseAmount);

        // Assert
        hold.RemainingAmount.Should().Be(70m);
        hold.ReleasedAmount.Should().Be(30m);
        hold.Status.Should().Be(HoldStatus.PartiallyReleased);
    }

    [Fact]
    public void Release_RestAmount_ShouldMarkAsReleased()
    {
        // Arrange
        var hold = CreateHold(100m);

        // Act
        hold.Release(100m);

        // Assert
        hold.RemainingAmount.Should().Be(0);
        hold.ReleasedAmount.Should().Be(100m);
        hold.Status.Should().Be(HoldStatus.Released);
    }

    [Fact]
    public void Cancel_ShouldReleaseAllRemainingAndMarkCancelled()
    {
        // Arrange
        var hold = CreateHold(100m);
        hold.Execute(20m); // Remaining 80

        // Act
        hold.Cancel();

        // Assert
        hold.RemainingAmount.Should().Be(0);
        hold.ExecutedAmount.Should().Be(20m);
        hold.ReleasedAmount.Should().Be(80m); // The rest is released
        hold.Status.Should().Be(HoldStatus.Cancelled);
    }

    [Fact]
    public void Execute_OnCancelledHold_ShouldThrowException()
    {
        // Arrange
        var hold = CreateHold(100m);
        hold.Cancel();

        // Act & Assert
        var act = () => hold.Execute(10m);
        act.Should().Throw<WalletDomainException>()
            .WithMessage("*Cannot modify hold in Cancelled status*");
    }

    private static HoldItem CreateHold(decimal amount)
    {
        return new HoldItem(
            Guid.NewGuid(),
            amount,
            CurrencyType.VND,
            "TEST",
            Guid.NewGuid(),
            "Description");
    }
}
