namespace MissionService.UnitTests.Domain;

using FluentAssertions;
using MissionService.Domain.AggregatesModel.CheckInAggregate;
using Xunit;

/// <summary>
/// EN: Tests for UserCheckIn aggregate root invariants and behavior.
/// VI: Kiểm thử các quy tắc bất biến và hành vi của UserCheckIn aggregate root.
/// </summary>
public class UserCheckInAggregateTests
{
    private readonly StreakBonusConfig _defaultConfig = StreakBonusConfig.Default();
    
    #region Constructor Tests
    
    [Fact]
    public void Constructor_ValidUserId_CreatesCheckInWithZeroStreak()
    {
        // Arrange
        var userId = Guid.NewGuid();
        
        // Act
        var checkIn = new UserCheckIn(userId);
        
        // Assert
        checkIn.UserId.Should().Be(userId);
        checkIn.CurrentStreak.Should().Be(0);
        checkIn.LongestStreak.Should().Be(0);
        checkIn.TotalCheckIns.Should().Be(0);
        checkIn.LastCheckInDate.Should().BeNull();
        checkIn.CheckInDays.Should().BeEmpty();
    }
    
    [Fact]
    public void Constructor_EmptyUserId_ThrowsArgumentException()
    {
        // Arrange & Act
        var act = () => new UserCheckIn(Guid.Empty);
        
        // Assert
        act.Should().Throw<ArgumentException>()
            .WithMessage("*User ID is required*");
    }
    
    #endregion
    
    #region CanCheckInToday Tests
    
    [Fact]
    public void CanCheckInToday_NewUser_ReturnsTrue()
    {
        // Arrange
        var checkIn = new UserCheckIn(Guid.NewGuid());
        
        // Act
        var result = checkIn.CanCheckInToday();
        
        // Assert
        result.Should().BeTrue();
    }
    
    [Fact]
    public void CanCheckInToday_AfterCheckIn_ReturnsFalse()
    {
        // Arrange
        var checkIn = new UserCheckIn(Guid.NewGuid());
        checkIn.CheckIn(_defaultConfig);
        
        // Act
        var result = checkIn.CanCheckInToday();
        
        // Assert
        result.Should().BeFalse();
    }
    
    #endregion
    
    #region CheckIn Tests
    
    [Fact]
    public void CheckIn_FirstTime_SetsStreakToOne()
    {
        // Arrange
        var checkIn = new UserCheckIn(Guid.NewGuid());
        
        // Act
        var result = checkIn.CheckIn(_defaultConfig);
        
        // Assert
        checkIn.CurrentStreak.Should().Be(1);
        checkIn.LongestStreak.Should().Be(1);
        checkIn.TotalCheckIns.Should().Be(1);
        result.CurrentStreak.Should().Be(1);
    }
    
    [Fact]
    public void CheckIn_FirstTime_AwardsDailyPoints()
    {
        // Arrange
        var checkIn = new UserCheckIn(Guid.NewGuid());
        
        // Act
        var result = checkIn.CheckIn(_defaultConfig);
        
        // Assert
        result.DailyPoints.Should().BeGreaterThan(0);
        result.TotalPoints.Should().BeGreaterThanOrEqualTo(result.DailyPoints);
    }
    
    [Fact]
    public void CheckIn_AlreadyCheckedInToday_ThrowsInvalidOperationException()
    {
        // Arrange
        var checkIn = new UserCheckIn(Guid.NewGuid());
        checkIn.CheckIn(_defaultConfig);
        
        // Act
        var act = () => checkIn.CheckIn(_defaultConfig);
        
        // Assert
        act.Should().Throw<InvalidOperationException>()
            .WithMessage("*Already checked in today*");
    }
    
    [Fact]
    public void CheckIn_AddsCheckInDayRecord()
    {
        // Arrange
        var checkIn = new UserCheckIn(Guid.NewGuid());
        
        // Act
        var result = checkIn.CheckIn(_defaultConfig);
        
        // Assert
        checkIn.CheckInDays.Should().HaveCount(1);
        var dayRecord = checkIn.CheckInDays.First();
        dayRecord.PointsEarned.Should().Be(result.TotalPoints);
        dayRecord.StreakOnDay.Should().Be(1);
    }
    
    #endregion
    
    #region ResetStreak Tests
    
    [Fact]
    public void ResetStreak_WithActiveStreak_ResetsCurrentStreakToZero()
    {
        // Arrange
        var checkIn = new UserCheckIn(Guid.NewGuid());
        checkIn.CheckIn(_defaultConfig);
        checkIn.CurrentStreak.Should().Be(1);
        
        // Act
        checkIn.ResetStreak();
        
        // Assert
        checkIn.CurrentStreak.Should().Be(0);
        // EN: LongestStreak should remain unchanged
        // VI: LongestStreak nên giữ nguyên
        checkIn.LongestStreak.Should().Be(1);
    }
    
    #endregion
    
    #region GetMonthlyCheckIns Tests
    
    [Fact]
    public void GetMonthlyCheckIns_NoCheckIns_ReturnsEmptyList()
    {
        // Arrange
        var checkIn = new UserCheckIn(Guid.NewGuid());
        var now = DateTime.UtcNow;
        
        // Act
        var result = checkIn.GetMonthlyCheckIns(now.Year, now.Month);
        
        // Assert
        result.Should().BeEmpty();
    }
    
    [Fact]
    public void GetMonthlyCheckIns_WithCheckIns_ReturnsCurrentMonthCheckIns()
    {
        // Arrange
        var checkIn = new UserCheckIn(Guid.NewGuid());
        checkIn.CheckIn(_defaultConfig);
        var now = DateTime.UtcNow;
        
        // Act
        var result = checkIn.GetMonthlyCheckIns(now.Year, now.Month);
        
        // Assert
        result.Should().HaveCount(1);
    }
    
    #endregion
}
