namespace BudgetApp.Tests.Domain;

using BudgetApp.Domain.Models;
using Xunit;

public class LedgerEntryTests
{
    [Fact]
    public void Constructor_WithValidExpense_CreatesEntry()
    {
        // Arrange
        var budgetAllocations = new Dictionary<Guid, Money>
        {
            { Guid.NewGuid(), new Money(100m, "USD") }
        };

        // Act
        var entry = new LedgerEntry(
            DateTime.Now,
            "Grocery Shopping",
            new Money(100m, "USD"),
            EntryType.Expense,
            budgetAllocations);

        // Assert
        Assert.Equal("Grocery Shopping", entry.Description);
        Assert.Equal(100m, entry.Amount.Amount);
        Assert.Equal(EntryType.Expense, entry.Type);
        Assert.Single(entry.BudgetAllocations);
    }

    [Fact]
    public void Constructor_WithValidIncome_CreatesEntry()
    {
        // Arrange
        var budgetAllocations = new Dictionary<Guid, Money>
        {
            { Guid.NewGuid(), new Money(1000m, "USD") }
        };

        // Act
        var entry = new LedgerEntry(
            DateTime.Now,
            "Salary",
            new Money(1000m, "USD"),
            EntryType.Income,
            budgetAllocations);

        // Assert
        Assert.Equal("Salary", entry.Description);
        Assert.Equal(1000m, entry.Amount.Amount);
        Assert.Equal(EntryType.Income, entry.Type);
    }

    [Fact]
    public void Validate_WithExpenseAllocationsNotMatchingAmount_ThrowsException()
    {
        // Arrange
        var budgetAllocations = new Dictionary<Guid, Money>
        {
            { Guid.NewGuid(), new Money(50m, "USD") }
        };

        // Act & Assert
        Assert.Throws<InvalidOperationException>(() => new LedgerEntry(
            DateTime.Now,
            "Grocery Shopping",
            new Money(100m, "USD"),
            EntryType.Expense,
            budgetAllocations));
    }

    [Fact]
    public void Validate_WithIncomeAllocationsNotMatchingAmount_ThrowsException()
    {
        // Arrange
        var budgetAllocations = new Dictionary<Guid, Money>
        {
            { Guid.NewGuid(), new Money(500m, "USD") }
        };

        // Act & Assert
        Assert.Throws<InvalidOperationException>(() => new LedgerEntry(
            DateTime.Now,
            "Salary",
            new Money(1000m, "USD"),
            EntryType.Income,
            budgetAllocations));
    }

    [Fact]
    public void Validate_WithMultipleBudgetAllocationsSummingToAmount_IsValid()
    {
        // Arrange
        var budgetAllocations = new Dictionary<Guid, Money>
        {
            { Guid.NewGuid(), new Money(90m, "USD") },
            { Guid.NewGuid(), new Money(10m, "USD") }
        };

        // Act
        var entry = new LedgerEntry(
            DateTime.Now,
            "Grocery Shopping",
            new Money(100m, "USD"),
            EntryType.Expense,
            budgetAllocations);

        // Assert
        Assert.Equal(2, entry.BudgetAllocations.Count);
        Assert.Equal(100m, entry.Amount.Amount);
    }

    [Fact]
    public void Validate_WithEmptyBudgetAllocations_ThrowsException()
    {
        // Arrange
        var budgetAllocations = new Dictionary<Guid, Money>();

        // Act & Assert
        Assert.Throws<InvalidOperationException>(() => new LedgerEntry(
            DateTime.Now,
            "Grocery Shopping",
            new Money(100m, "USD"),
            EntryType.Expense,
            budgetAllocations));
    }

    [Fact]
    public void Validate_WithNullDescription_ThrowsException()
    {
        // Arrange
        var budgetAllocations = new Dictionary<Guid, Money>
        {
            { Guid.NewGuid(), new Money(100m, "USD") }
        };

        // Act & Assert
        Assert.Throws<ArgumentException>(() => new LedgerEntry(
            DateTime.Now,
            null!,
            new Money(100m, "USD"),
            EntryType.Expense,
            budgetAllocations));
    }
}


