using MiningService.Domain.SeedWork;

namespace MiningService.Domain.AggregatesModel.MinerAggregate;

/// <summary>
/// EN: Value object representing mining rate calculation components.
/// VI: Value object đại diện cho các thành phần tính toán tỷ lệ đào.
/// </summary>
public record MiningRate
{
    /// <summary>EN: Base rate in MP/hour / VI: Tỷ lệ cơ bản MP/giờ</summary>
    public decimal BaseRate { get; init; }
    
    /// <summary>EN: Role bonus percentage / VI: Phần trăm thưởng vai trò</summary>
    public decimal RoleBonus { get; init; }
    
    /// <summary>EN: Circle bonus percentage / VI: Phần trăm thưởng vòng tròn</summary>
    public decimal CircleBonus { get; init; }
    
    /// <summary>EN: Referral bonus percentage / VI: Phần trăm thưởng giới thiệu</summary>
    public decimal ReferralBonus { get; init; }
    
    /// <summary>EN: Streak bonus percentage / VI: Phần trăm thưởng streak</summary>
    public decimal StreakBonus { get; init; }
    
    /// <summary>
    /// EN: Calculated total rate in MP/hour.
    /// VI: Tỷ lệ tổng tính toán được MP/giờ.
    /// Formula: BaseRate × (1 + Role) × (1 + Circle) × (1 + Referral) × (1 + Streak)
    /// </summary>
    public decimal TotalRate => BaseRate 
        * (1 + RoleBonus) 
        * (1 + CircleBonus) 
        * (1 + ReferralBonus)
        * (1 + StreakBonus);
    
    /// <summary>EN: Daily earnings / VI: Thu nhập hàng ngày</summary>
    public decimal DailyRate => TotalRate * 24;
    
    /// <summary>
    /// EN: Create a new mining rate with default base rate.
    /// VI: Tạo mining rate mới với tỷ lệ cơ bản mặc định.
    /// </summary>
    public static MiningRate CreateDefault(decimal baseRate = 0.25m) => new()
    {
        BaseRate = baseRate,
        RoleBonus = 0,
        CircleBonus = 0,
        ReferralBonus = 0,
        StreakBonus = 0
    };
}

/// <summary>
/// EN: Value object representing mining streak tracking.
/// VI: Value object theo dõi streak đào liên tục.
/// </summary>
public record MiningStreak
{
    /// <summary>EN: Current consecutive mining days / VI: Số ngày đào liên tục hiện tại</summary>
    public int CurrentStreak { get; init; }
    
    /// <summary>EN: Personal best streak / VI: Kỷ lục streak cá nhân</summary>
    public int LongestStreak { get; init; }
    
    /// <summary>EN: Last successful mining date / VI: Ngày đào thành công cuối cùng</summary>
    public DateTime LastMiningDate { get; init; }
    
    /// <summary>EN: Available freeze tokens / VI: Số token đóng băng khả dụng</summary>
    public int FreezeTokens { get; init; }
    
    /// <summary>EN: Is in grace period (missed 1 day) / VI: Đang trong thời gian ân hạn</summary>
    public bool IsGracePeriod { get; init; }
    
    /// <summary>
    /// EN: Get bonus multiplier based on streak tier.
    /// VI: Lấy hệ số thưởng dựa trên cấp streak.
    /// </summary>
    public decimal BonusMultiplier => CurrentStreak switch
    {
        < 3 => 0m,          // Day 1-2: 0%
        < 7 => 0.10m,       // Day 3-6: +10%
        < 14 => 0.25m,      // Day 7-13: +25%
        < 30 => 0.50m,      // Day 14-29: +50%
        < 60 => 1.00m,      // Day 30-59: +100%
        < 90 => 1.25m,      // Day 60-89: +125%
        _ => 1.50m          // Day 90+: +150%
    };
    
    /// <summary>
    /// EN: Create initial streak for new miner.
    /// VI: Tạo streak ban đầu cho thợ đào mới.
    /// </summary>
    public static MiningStreak CreateNew() => new()
    {
        CurrentStreak = 0,
        LongestStreak = 0,
        LastMiningDate = DateTime.MinValue,
        FreezeTokens = 0,
        IsGracePeriod = false
    };
    
    /// <summary>
    /// EN: Increment streak after successful claim.
    /// VI: Tăng streak sau khi nhận thưởng thành công.
    /// </summary>
    public MiningStreak IncrementStreak()
    {
        var newStreak = CurrentStreak + 1;
        var earnedToken = newStreak % 7 == 0; // Earn 1 token per 7 days
        
        return this with
        {
            CurrentStreak = newStreak,
            LongestStreak = Math.Max(LongestStreak, newStreak),
            LastMiningDate = DateTime.UtcNow,
            FreezeTokens = earnedToken ? FreezeTokens + 1 : FreezeTokens,
            IsGracePeriod = false
        };
    }
    
    /// <summary>
    /// EN: Reset streak (missed beyond grace period).
    /// VI: Reset streak (bỏ lỡ quá thời gian ân hạn).
    /// </summary>
    public MiningStreak Reset() => this with
    {
        CurrentStreak = 0,
        IsGracePeriod = false
    };
}

/// <summary>
/// EN: Value object representing a mining session.
/// VI: Value object đại diện cho phiên đào.
/// </summary>
public record MiningSession
{
    /// <summary>EN: Session unique identifier / VI: Định danh phiên duy nhất</summary>
    public Guid SessionId { get; init; }
    
    /// <summary>EN: Session start time / VI: Thời gian bắt đầu phiên</summary>
    public DateTime StartTime { get; init; }
    
    /// <summary>EN: Session end time / VI: Thời gian kết thúc phiên</summary>
    public DateTime EndTime { get; init; }
    
    /// <summary>EN: Mining rate at session start / VI: Tỷ lệ đào khi bắt đầu phiên</summary>
    public decimal HourlyRate { get; init; }
    
    /// <summary>EN: Current session status / VI: Trạng thái phiên hiện tại</summary>
    public MiningSessionStatus Status { get; init; }
    
    /// <summary>EN: Points accumulated in this session / VI: Điểm tích lũy trong phiên</summary>
    public decimal AccumulatedPoints { get; init; }
    
    /// <summary>
    /// EN: Create a new 24-hour mining session.
    /// VI: Tạo phiên đào 24 giờ mới.
    /// </summary>
    public static MiningSession Create(decimal hourlyRate, int sessionHours = 24)
    {
        var now = DateTime.UtcNow;
        return new MiningSession
        {
            SessionId = Guid.NewGuid(),
            StartTime = now,
            EndTime = now.AddHours(sessionHours),
            HourlyRate = hourlyRate,
            Status = MiningSessionStatus.Active,
            AccumulatedPoints = 0
        };
    }
    
    /// <summary>
    /// EN: Check if session is ready to claim.
    /// VI: Kiểm tra phiên đã sẵn sàng để nhận thưởng chưa.
    /// </summary>
    public bool IsReadyToClaim => Status == MiningSessionStatus.Active && DateTime.UtcNow >= EndTime;
    
    /// <summary>
    /// EN: Calculate earned points for completed session.
    /// VI: Tính điểm kiếm được cho phiên hoàn thành.
    /// </summary>
    public decimal CalculateEarnedPoints()
    {
        if (Status != MiningSessionStatus.Active)
            return AccumulatedPoints;
            
        var elapsedHours = (decimal)(DateTime.UtcNow - StartTime).TotalHours;
        var cappedHours = Math.Min(elapsedHours, 24); // Cap at 24 hours
        return Math.Round(cappedHours * HourlyRate, 4);
    }
    
    /// <summary>
    /// EN: Mark session as claimed with final points.
    /// VI: Đánh dấu phiên đã nhận với điểm cuối cùng.
    /// </summary>
    public MiningSession MarkAsClaimed(decimal earnedPoints) => this with
    {
        Status = MiningSessionStatus.Claimed,
        AccumulatedPoints = earnedPoints
    };
}
