using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Domain.Entities.Paper.ValueObject;
using Microsoft.EntityFrameworkCore;
using SharedKernel.Results;
namespace Application.Features.Api.Paper.CancelOrder;
///
/// 모의투자 주문 취소 — 본인 계좌의 Pending 주문 && now < CancelableUntil 만 취소 (d4 §③/§⑤).
/// 매수는 예약금 환원, 매도는 예약 수량 환원.
///
internal sealed class Handler(IAppDbContext db) : ICommandHandler
{
public async Task Handle(Command request, CancellationToken ct)
{
var order = await db.PaperOrder
.Include(c => c.Account)
.FirstOrDefaultAsync(c => c.ID == request.OrderID, ct);
if (order is null || order.Account.MemberID != request.MemberID)
{
return Result.Failure(Error.NotFound("Paper.OrderNotFound", "주문을 찾을 수 없습니다."));
}
if (order.Status != PaperOrderStatus.Pending)
{
return Result.Failure(Error.Conflict("Paper.NotCancelable", "체결 대기 상태의 주문만 취소할 수 있습니다."));
}
if (DateTime.UtcNow >= order.CancelableUntil)
{
return Result.Failure(Error.Conflict("Paper.CancelWindowClosed", "취소 가능 시한이 지났습니다."));
}
if (!order.Cancel())
{
return Result.Failure(Error.Conflict("Paper.NotCancelable", "주문을 취소할 수 없습니다."));
}
if (order.Side == PaperOrderSide.Buy)
{
if (order.ReservedAmount > 0)
{
order.Account.ReleaseBuyReserve(order.ReservedAmount);
}
}
else
{
var position = await db.PaperPosition.FirstOrDefaultAsync(c => c.AccountID == order.AccountID && c.StockCode == order.StockCode, ct);
position?.ReleaseSellReserve(order.Quantity);
}
await db.SaveChangesAsync(ct);
return Result.Success();
}
}