using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Domain.Entities.Members;
namespace Domain.Entities.Forum.Posts;
///
/// 분석글 응원(Cheer) — D3 M4 (Twitch Bits 모델).
/// 후원자(From)가 글 작성자(To)에게 캐시/토큰을 팁으로 보내며 플랫폼이 수수료를 취한다.
/// 순액(NetAmount = Amount − FeeAmount)만 작성자 지갑 Reward 파티션으로 적립되고(출금 불가),
/// 수수료(FeeAmount)는 어디에도 적립되지 않고 소각(=유통에서 제거)된다.
///
/// 규제 가드라인(§⑧): 응원은 사후·자율·무대가이며 글 열람 조건이 아니다(글은 항상 무료).
/// 본인 글 응원 금지·일일 발신 캡·수신 적립분 출금 불가는 자전 수수료 소각 어뷰징을 차단한다.
///
public class PostCheer
{
[ForeignKey(nameof(PostID))]
public virtual Post Post { get; private set; } = null!;
[ForeignKey(nameof(FromMemberID))]
public virtual Member? FromMember { get; private set; }
[ForeignKey(nameof(ToMemberID))]
public virtual Member? ToMember { get; private set; }
[Key]
public int ID { get; private set; }
/// 응원 대상 게시글
public int PostID { get; private set; }
/// 응원 발신자 (후원자)
public int FromMemberID { get; private set; }
/// 응원 수신자 (글 작성자 스냅샷)
public int ToMemberID { get; private set; }
/// 총 응원 금액 (발신자 차감액)
public int Amount { get; private set; }
/// 플랫폼 수수료 (소각분, 어디에도 적립되지 않음)
public int FeeAmount { get; private set; }
/// 작성자 수령 순액 (Amount − FeeAmount, Reward 파티션 적립)
public int NetAmount { get; private set; }
/// 응원 한마디 (선택)
public string? Message { get; private set; }
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
private PostCheer() { }
private PostCheer(int postID, int fromMemberID, int toMemberID, int amount, int feeAmount, int netAmount, string? message)
{
PostID = postID;
FromMemberID = fromMemberID;
ToMemberID = toMemberID;
Amount = amount;
FeeAmount = feeAmount;
NetAmount = netAmount;
Message = message;
}
public static PostCheer Create(int postID, int fromMemberID, int toMemberID, int amount, int feeAmount, int netAmount, string? message)
{
if (postID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(postID));
}
if (fromMemberID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(fromMemberID));
}
if (toMemberID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(toMemberID));
}
if (fromMemberID == toMemberID)
{
throw new ArgumentException("본인 글에는 응원할 수 없습니다.", nameof(toMemberID));
}
if (amount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(amount));
}
if (feeAmount < 0 || netAmount <= 0 || feeAmount + netAmount != amount)
{
throw new ArgumentException("수수료/순액 계산이 올바르지 않습니다.", nameof(netAmount));
}
return new PostCheer(postID, fromMemberID, toMemberID, amount, feeAmount, netAmount, message);
}
}