using AdsServingService.Domain.Exceptions;
using AdsServingService.Domain.SeedWork;

namespace AdsServingService.Domain.AggregatesModel.FrequencyAggregate;

/// <summary>
/// EN: Frequency cap - limits how often a user sees the same ad.
/// VI: Frequency cap - giới hạn tần suất người dùng nhìn thấy cùng một quảng cáo.
/// </summary>
public class FrequencyCap : Entity, IAggregateRoot
{
    private Guid _adId;
    private int _maxImpressionsPerUser;
    private FrequencyWindow _window;

    public Guid AdId => _adId;
    public int MaxImpressionsPerUser => _maxImpressionsPerUser;
    public FrequencyWindow Window => _window;

    protected FrequencyCap() { }

    public FrequencyCap(Guid adId, int maxImpressionsPerUser, FrequencyWindow window)
    {
        if (maxImpressionsPerUser <= 0)
            throw new AdsServingDomainException("Max impressions must be greater than zero");

        Id = Guid.NewGuid();
        _adId = adId;
        _maxImpressionsPerUser = maxImpressionsPerUser;
        _window = window;
    }

    /// <summary>
    /// EN: Check if user has reached frequency cap.
    /// VI: Kiểm tra xem người dùng đã đạt tới frequency cap chưa.
    /// </summary>
    public bool IsUserCapped(int currentImpressions)
    {
        return currentImpressions >= _maxImpressionsPerUser;
    }

    /// <summary>
    /// EN: Create a daily frequency cap (most common).
    /// VI: Tạo frequency cap hàng ngày (phổ biến nhất).
    /// </summary>
    public static FrequencyCap Daily(Guid adId, int maxImpressions) =>
        new(adId, maxImpressions, FrequencyWindow.Day);
}

/// <summary>
/// EN: Frequency window enumeration.
/// VI: Enum cửa sổ tần suất.
/// </summary>
public enum FrequencyWindow
{
    Hour = 1,
    Day = 2,
    Week = 3,
    Month = 4,
    Lifetime = 5
}

/// <summary>
/// EN: User ad history - tracks impressions per user (stored in Redis).
/// VI: Lịch sử quảng cáo người dùng - theo dõi impressions cho mỗi user (lưu trong Redis).
/// </summary>
/// <remarks>
/// This is a lightweight entity meant to be cached in Redis.
/// Key format: freq:{userId}:{date}
/// Value: Hash of adId -> impressionCount
/// </remarks>
public class UserAdHistory : ValueObject
{
    public Guid UserId { get; private set; }
    public Dictionary<Guid, int> AdImpressionCounts { get; private set; } = new();
    public DateTime Date { get; private set; }

    protected UserAdHistory() { }

    public UserAdHistory(Guid userId, DateTime date)
    {
        UserId = userId;
        Date = date.Date; // Normalize to start of day
    }

    /// <summary>
    /// EN: Increment impression count for an ad.
    /// VI: Tăng số lần impression cho một quảng cáo.
    /// </summary>
    public void RecordImpression(Guid adId)
    {
        if (AdImpressionCounts.ContainsKey(adId))
            AdImpressionCounts[adId]++;
        else
            AdImpressionCounts[adId] = 1;
    }

    /// <summary>
    /// EN: Get impression count for an ad.
    /// VI: Lấy số lần impression cho một quảng cáo.
    /// </summary>
    public int GetImpressionCount(Guid adId)
    {
        return AdImpressionCounts.TryGetValue(adId, out var count) ? count : 0;
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return UserId;
        yield return Date;
    }
}
