using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Domain.Entities.Paper.ValueObject;
namespace Domain.Entities.Paper;
///
/// 모의투자 주문 — 접수 시 서버가 FillRule/TargetDate/CancelableUntil 을 확정한다 (d4 §③ 체결 규칙).
/// 실제 기표는 TargetDate 시세가 도착하는 배치(M2)에서 수행 (Pending → Filled/Rejected).
///
public class PaperOrder
{
[ForeignKey(nameof(AccountID))]
public virtual PaperAccount Account { get; private set; } = null!;
[Key]
public int ID { get; private set; }
public int AccountID { get; private set; }
/// 단축코드 (char 6)
public string StockCode { get; private set; } = default!;
public PaperOrderSide Side { get; private set; }
public PaperFillRule FillRule { get; private set; }
public int Quantity { get; private set; }
/// 매수 예약금 (매도는 0)
public decimal ReservedAmount { get; private set; }
/// 체결 기준일
public DateOnly TargetDate { get; private set; }
/// 취소 가능 시한 — 이후 취소 불가 (look-ahead 어뷰징 차단)
public DateTime CancelableUntil { get; private set; }
public PaperOrderStatus Status { get; private set; }
/// 거부 사유 (Rejected 시)
public string? RejectReason { get; private set; }
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
private PaperOrder() { }
public static PaperOrder Create(
int accountID,
string stockCode,
PaperOrderSide side,
PaperFillRule fillRule,
int quantity,
decimal reservedAmount,
DateOnly targetDate,
DateTime cancelableUntil
) {
if (accountID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(accountID));
}
if (stockCode is not { Length: 6 })
{
throw new ArgumentException("stockCode must be 6 chars", nameof(stockCode));
}
if (quantity <= 0)
{
throw new ArgumentOutOfRangeException(nameof(quantity));
}
if (reservedAmount < 0)
{
throw new ArgumentOutOfRangeException(nameof(reservedAmount));
}
return new PaperOrder
{
AccountID = accountID,
StockCode = stockCode,
Side = side,
FillRule = fillRule,
Quantity = quantity,
ReservedAmount = reservedAmount,
TargetDate = targetDate,
CancelableUntil = cancelableUntil,
Status = PaperOrderStatus.Pending
};
}
/// 취소 — Pending 만 취소 가능. 성공 시 true. 시한 검증은 호출자.
public bool Cancel()
{
if (Status != PaperOrderStatus.Pending)
{
return false;
}
Status = PaperOrderStatus.Cancelled;
return true;
}
/// 체결 확정 (배치 M2).
public void MarkFilled()
{
Status = PaperOrderStatus.Filled;
}
/// 거부 처리 (배치 M2).
public void MarkRejected(string reason)
{
Status = PaperOrderStatus.Rejected;
RejectReason = reason;
}
}