namespace BudgetApp.Tests.Domain;

using BudgetApp.Domain.Models;
using Xunit;

public class GoalBudgetTests
{
    [Fact]
    public void Constructor_WithValidParameters_CreatesGoalBudget()
    {
        // Arrange & Act
        var targetDate = DateTime.Now.AddYears(3);
        var budget = new GoalBudget(
            "Car Purchase",
            new Money(0m, "USD"),
            new Money(30000m, "USD"),
            targetDate,
            new Money(1000m, "USD"),
            TimeSpan.FromDays(30));

        // Assert
        Assert.Equal("Car Purchase", budget.Name);
        Assert.Equal(30000m, budget.GoalAmount.Amount);
        Assert.Equal(targetDate, budget.TargetDate);
        Assert.Equal(1000m, budget.PeriodicContribution.Amount);
    }

    [Fact]
    public void Constructor_WithPastTargetDate_ThrowsException()
    {
        // Arrange, Act & Assert
        var pastDate = DateTime.Now.AddDays(-1);
        Assert.Throws<ArgumentException>(() => new GoalBudget(
            "Test",
            new Money(0m, "USD"),
            new Money(10000m, "USD"),
            pastDate,
            new Money(1000m, "USD"),
            TimeSpan.FromDays(30)));
    }

    [Fact]
    public void Constructor_WithZeroGoalAmount_ThrowsException()
    {
        // Arrange, Act & Assert
        var targetDate = DateTime.Now.AddYears(1);
        Assert.Throws<ArgumentException>(() => new GoalBudget(
            "Test",
            new Money(0m, "USD"),
            new Money(0m, "USD"),
            targetDate,
            new Money(1000m, "USD"),
            TimeSpan.FromDays(30)));
    }

    [Fact]
    public void IsGoalAchievable_WithSufficientContributions_ReturnsTrue()
    {
        // Arrange
        var targetDate = DateTime.Now.AddYears(3);
        var budget = new GoalBudget(
            "Car Purchase",
            new Money(0m, "USD"),
            new Money(36000m, "USD"),
            targetDate,
            new Money(1000m, "USD"),
            TimeSpan.FromDays(30));

        // Act
        var isAchievable = budget.IsGoalAchievable(DateTime.Now);

        // Assert
        Assert.True(isAchievable);
    }

    [Fact]
    public void IsGoalAchievable_WithInsufficientContributions_ReturnsFalse()
    {
        // Arrange
        var targetDate = DateTime.Now.AddMonths(1);
        var budget = new GoalBudget(
            "Car Purchase",
            new Money(0m, "USD"),
            new Money(100000m, "USD"),
            targetDate,
            new Money(1000m, "USD"),
            TimeSpan.FromDays(30));

        // Act
        var isAchievable = budget.IsGoalAchievable(DateTime.Now);

        // Assert
        Assert.False(isAchievable);
    }

    [Fact]
    public void CalculateRequiredContributionRate_WithSufficientTime_ReturnsCorrectRate()
    {
        // Arrange
        var targetDate = DateTime.Now.AddYears(3);
        var budget = new GoalBudget(
            "Car Purchase",
            new Money(0m, "USD"),
            new Money(36000m, "USD"),
            targetDate,
            new Money(1000m, "USD"),
            TimeSpan.FromDays(30));

        // Act
        var requiredRate = budget.CalculateRequiredContributionRate(DateTime.Now);

        // Assert
        Assert.True(requiredRate.Amount > 0);
        Assert.True(requiredRate.Amount <= 1000m); // Should be achievable
    }

    [Fact]
    public void CalculateRequiredContributionRate_WithCurrentBalanceAtGoal_ReturnsZero()
    {
        // Arrange
        var targetDate = DateTime.Now.AddYears(1);
        var budget = new GoalBudget(
            "Car Purchase",
            new Money(10000m, "USD"),
            new Money(10000m, "USD"),
            targetDate,
            new Money(1000m, "USD"),
            TimeSpan.FromDays(30));

        // Act
        var requiredRate = budget.CalculateRequiredContributionRate(DateTime.Now);

        // Assert
        Assert.Equal(0m, requiredRate.Amount);
    }

    [Fact]
    public void IsGoalAchievable_WithBalanceExceedingGoal_ReturnsTrue()
    {
        // Arrange
        var targetDate = DateTime.Now.AddYears(1);
        var budget = new GoalBudget(
            "Car Purchase",
            new Money(15000m, "USD"),
            new Money(10000m, "USD"),
            targetDate,
            new Money(1000m, "USD"),
            TimeSpan.FromDays(30));

        // Act
        var isAchievable = budget.IsGoalAchievable(DateTime.Now);

        // Assert
        Assert.True(isAchievable);
    }
}


