using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Domain.Entities.Members;
///
/// 회원 예측 트랙레코드 집계 (D2 M4 예측 채점+트랙레코드, d2 §③).
/// PredictionSettlementService(M4)가 채점 후 회원별로 재집계한다 — 매 실행마다 해당 회원의
/// 전체 non-Pending 예측으로 처음부터 다시 계산하므로 멱등하다.
/// MemberID = PK(FK Member). Predictions=채점 완료 총계(Hits+Misses+Voids), HitRate=Hits/(Hits+Misses)*100(Void 제외).
/// 리더보드는 채점 표본(Hits+Misses) 10+ 회원만 HitRate 내림차순 필터 인덱스로 조회한다.
///
public class MemberTrackRecord
{
[ForeignKey(nameof(MemberID))]
public virtual Member Member { get; private set; } = null!;
/// 회원 ID (PK, FK Member)
[Key]
public int MemberID { get; private set; }
/// 채점 완료 예측 총계 (Hits + Misses + Voids)
public int Predictions { get; private set; }
/// 적중 수
public int Hits { get; private set; }
/// 실패 수
public int Misses { get; private set; }
/// 무효 수 (거래정지·상폐·시세결측 — 적중률 표본 제외)
public int Voids { get; private set; }
/// 적중률 % = Hits/(Hits+Misses)*100 (Void 제외). 표본 0 이면 0.
public decimal HitRate { get; private set; }
/// 현재 연속 적중 수 (가장 최근 채점부터의 Hit 연속, Void 무시)
public int CurrentStreak { get; private set; }
/// 역대 최고 연속 적중 수
public int BestStreak { get; private set; }
/// 배지 등급 (0=None/1=Bronze/2=Silver/3=Gold/4=GoldAnt)
public TrackRecordTier Tier { get; private set; }
public DateTime UpdatedAt { get; private set; } = DateTime.UtcNow;
private MemberTrackRecord() { }
/// 빈 트랙레코드 생성 (최초 채점 전). 이후 로 채운다.
public static MemberTrackRecord Create(int memberID)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(memberID);
return new MemberTrackRecord { MemberID = memberID };
}
///
/// 집계값 일괄 갱신 (M4 채점 배치가 회원 예측 전체를 다시 계산해 호출 — 멱등 재집계).
/// hitRate 는 백분율(0~100), tier 는 (hits+misses, hitRate) 로 산정한 값.
///
public void Recompute(int predictions, int hits, int misses, int voids, decimal hitRate, int currentStreak, int bestStreak, TrackRecordTier tier)
{
Predictions = predictions;
Hits = hits;
Misses = misses;
Voids = voids;
HitRate = hitRate;
CurrentStreak = currentStreak;
BestStreak = bestStreak;
Tier = tier;
UpdatedAt = DateTime.UtcNow;
}
}