using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Domain.Entities.Store.ValueObject;
namespace Domain.Entities.Store;
///
/// 쿠폰 메타. 실제 코드 풀은 CouponCode 1:N 으로 관리.
/// ProductID 필수 (어떤 Digital 상품에 발급할 쿠폰인지), GameID 필수 (사용자 정책: 게임 필수 연결).
///
public class Coupon
{
[ForeignKey(nameof(ProductID))]
public virtual Product? Product { get; private set; }
[ForeignKey(nameof(GameID))]
public virtual Game? Game { get; private set; }
[Key]
public int ID { get; private set; }
public int ProductID { get; private set; }
public int GameID { get; private set; }
public string Name { get; private set; } = default!;
public DateTime? ExpiresAt { get; private set; }
public CouponUsagePolicy UsagePolicy { get; private set; } = CouponUsagePolicy.SingleUse;
/// 누적 발급 가능 코드 수 (CouponCode 풀의 총 row 수). 통계용.
public int GeneratedCount { get; private set; }
/// 실제 사용된 코드 수 (Status=Used). 통계용.
public int UsedCount { get; private set; }
/// 잔여 코드 부족 알림 임계치 (Available 코드가 이 값 미만이면 Admin 알림).
public int LowStockThreshold { get; private set; } = 10;
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; private set; }
private Coupon() { }
public static Coupon Create(
int productID,
int gameID,
string name,
CouponUsagePolicy usagePolicy = CouponUsagePolicy.SingleUse,
DateTime? expiresAt = null,
int lowStockThreshold = 10
) {
if (productID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(productID), "Product is required.");
}
if (gameID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(gameID), "Game is required.");
}
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Name is required.", nameof(name));
}
if (name.Length > 200)
{
throw new ArgumentOutOfRangeException(nameof(name));
}
if (lowStockThreshold < 0)
{
throw new ArgumentOutOfRangeException(nameof(lowStockThreshold));
}
return new Coupon
{
ProductID = productID,
GameID = gameID,
Name = name,
UsagePolicy = usagePolicy,
ExpiresAt = expiresAt,
LowStockThreshold = lowStockThreshold
};
}
public void Update(string name, DateTime? expiresAt, int lowStockThreshold)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Name is required.", nameof(name));
}
if (lowStockThreshold < 0)
{
throw new ArgumentOutOfRangeException(nameof(lowStockThreshold));
}
Name = name;
ExpiresAt = expiresAt;
LowStockThreshold = lowStockThreshold;
UpdatedAt = DateTime.UtcNow;
}
/// BulkUpload 후 발급 가능 코드 수 누적.
public void IncreaseGeneratedCount(int delta)
{
if (delta <= 0)
{
throw new ArgumentOutOfRangeException(nameof(delta));
}
GeneratedCount += delta;
UpdatedAt = DateTime.UtcNow;
}
/// 코드 1개가 Used 상태로 전이될 때 호출 (실제 사용 보고용 — 결제 발급 시점이 아니라 사용자가 사용 누른 시점).
public void IncreaseUsedCount(int delta = 1)
{
if (delta <= 0)
{
throw new ArgumentOutOfRangeException(nameof(delta));
}
UsedCount += delta;
UpdatedAt = DateTime.UtcNow;
}
}