namespace MissionService.Domain.AggregatesModel.CheckInAggregate;

using MissionService.Domain.SeedWork;

/// <summary>
/// EN: User check-in aggregate root - tracks daily check-in streaks
/// VI: UserCheckIn aggregate root - theo dõi streak điểm danh hàng ngày
/// </summary>
public class UserCheckIn : Entity, IAggregateRoot
{
    /// <summary>EN: User ID / VI: ID người dùng</summary>
    public Guid UserId { get; private set; }
    
    /// <summary>EN: Current consecutive check-in days / VI: Số ngày điểm danh liên tiếp hiện tại</summary>
    public int CurrentStreak { get; private set; }
    
    /// <summary>EN: Longest streak ever achieved / VI: Streak dài nhất từng đạt</summary>
    public int LongestStreak { get; private set; }
    
    /// <summary>EN: Total check-ins all time / VI: Tổng số lần điểm danh</summary>
    public int TotalCheckIns { get; private set; }
    
    /// <summary>EN: Last check-in date / VI: Ngày điểm danh cuối</summary>
    public DateOnly? LastCheckInDate { get; private set; }
    
    /// <summary>EN: Check-in history / VI: Lịch sử điểm danh</summary>
    private readonly List<CheckInDay> _checkInDays = new();
    public IReadOnlyCollection<CheckInDay> CheckInDays => _checkInDays.AsReadOnly();
    
    // EF Core constructor
    protected UserCheckIn() { }
    
    /// <summary>
    /// EN: Create new check-in profile for user / VI: Tạo hồ sơ điểm danh mới cho user
    /// </summary>
    public UserCheckIn(Guid userId)
    {
        if (userId == Guid.Empty)
            throw new ArgumentException("User ID is required", nameof(userId));
            
        UserId = userId;
        CurrentStreak = 0;
        LongestStreak = 0;
        TotalCheckIns = 0;
    }
    
    /// <summary>
    /// EN: Check if user can check in today / VI: Kiểm tra user có thể điểm danh hôm nay không
    /// </summary>
    public bool CanCheckInToday()
    {
        var today = DateOnly.FromDateTime(DateTime.UtcNow);
        return LastCheckInDate != today;
    }
    
    /// <summary>
    /// EN: Perform daily check-in / VI: Thực hiện điểm danh hàng ngày
    /// </summary>
    /// <returns>EN: Points earned including bonuses / VI: Điểm nhận được bao gồm bonus</returns>
    public CheckInResult CheckIn(StreakBonusConfig config)
    {
        var today = DateOnly.FromDateTime(DateTime.UtcNow);
        
        if (!CanCheckInToday())
            throw new InvalidOperationException("Already checked in today");
        
        // EN: Check if streak continues or resets
        // VI: Kiểm tra streak tiếp tục hay reset
        var yesterday = today.AddDays(-1);
        if (LastCheckInDate != yesterday)
        {
            // EN: Streak broken, reset to 1 / VI: Streak bị hỏng, reset về 1
            CurrentStreak = 1;
        }
        else
        {
            // EN: Continue streak / VI: Tiếp tục streak
            CurrentStreak++;
        }
        
        // EN: Update longest streak / VI: Cập nhật streak dài nhất
        if (CurrentStreak > LongestStreak)
        {
            LongestStreak = CurrentStreak;
        }
        
        TotalCheckIns++;
        LastCheckInDate = today;
        
        // EN: Calculate points / VI: Tính điểm
        var (dailyPoints, streakBonus, milestoneBonus, isMilestone) = config.CalculateReward(CurrentStreak);
        var totalPoints = dailyPoints + streakBonus + milestoneBonus;
        
        // EN: Add check-in day record / VI: Thêm bản ghi ngày điểm danh
        var checkInDay = new CheckInDay(today, totalPoints, isMilestone, CurrentStreak);
        _checkInDays.Add(checkInDay);
        
        return new CheckInResult(
            CurrentStreak,
            dailyPoints,
            streakBonus,
            milestoneBonus,
            totalPoints,
            isMilestone);
    }
    
    /// <summary>
    /// EN: Get check-ins for a specific month / VI: Lấy điểm danh cho một tháng cụ thể
    /// </summary>
    public IReadOnlyList<CheckInDay> GetMonthlyCheckIns(int year, int month)
    {
        return _checkInDays
            .Where(d => d.Date.Year == year && d.Date.Month == month)
            .OrderBy(d => d.Date)
            .ToList();
    }
    
    /// <summary>
    /// EN: Reset streak (admin action) / VI: Reset streak (hành động admin)
    /// </summary>
    public void ResetStreak()
    {
        CurrentStreak = 0;
    }
}

/// <summary>
/// EN: Single check-in day record - Entity
/// VI: Bản ghi ngày điểm danh - Entity
/// </summary>
public class CheckInDay : Entity
{
    /// <summary>EN: Check-in date / VI: Ngày điểm danh</summary>
    public DateOnly Date { get; private set; }
    
    /// <summary>EN: Total points earned / VI: Tổng điểm nhận được</summary>
    public decimal PointsEarned { get; private set; }
    
    /// <summary>EN: Was this a milestone day / VI: Đây có phải ngày mốc không</summary>
    public bool IsMilestone { get; private set; }
    
    /// <summary>EN: Streak count on this day / VI: Số streak vào ngày này</summary>
    public int StreakOnDay { get; private set; }
    
    // EF Core constructor
    protected CheckInDay() { }
    
    public CheckInDay(DateOnly date, decimal pointsEarned, bool isMilestone, int streakOnDay)
    {
        Date = date;
        PointsEarned = pointsEarned;
        IsMilestone = isMilestone;
        StreakOnDay = streakOnDay;
    }
}

/// <summary>
/// EN: Result of a check-in operation / VI: Kết quả của thao tác điểm danh
/// </summary>
public record CheckInResult(
    int CurrentStreak,
    decimal DailyPoints,
    decimal StreakBonus,
    decimal MilestoneBonus,
    decimal TotalPoints,
    bool IsMilestone);
