using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Domain.Entities.Developers.ValueObject;
namespace Domain.Entities.Developers;
///
/// NICE 통합인증 1회 진행 세션. StartKyc 시 생성 → callback 에서 매칭 → Completed/Failed/Expired.
///
/// 가이드: .claude/plan/nice-auth-integration.md §5
///
public class NiceAuthSession
{
/// 유효기간: 10분 (NICE transaction_id 의 효력과 동일).
public static readonly TimeSpan DefaultLifetime = TimeSpan.FromMinutes(10);
[ForeignKey(nameof(DeveloperID))]
public virtual Developer? Developer { get; private set; }
[Key]
public int ID { get; private set; }
public int DeveloperID { get; private set; }
///
/// 가맹점 측 세션 식별자. NICE return_url path segment 로 embed → callback 매칭.
/// URL-safe (Base64Url no-padding 22 char).
///
public string SessionToken { get; private set; } = default!;
/// NICE auth/url request 의 request_no.
public string RequestNo { get; private set; } = default!;
/// NICE auth/url 응답의 transaction_id.
public string TransactionID { get; private set; } = default!;
/// NICE 표준창이 return_url 로 넘긴 web_transaction_id (callback 시점에 set).
public string? WebTransactionID { get; private set; }
/// auth/url 호출 시점 token 의 ticket. result 복호화 KDF 입력.
public string Ticket { get; private set; } = default!;
/// auth/url 호출 시점 token 의 iterators. result 복호화 KDF 입력.
public int Iterators { get; private set; }
public NiceAuthSessionStatus Status { get; private set; } = NiceAuthSessionStatus.Pending;
public string? FailReason { get; private set; }
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
public DateTime ExpiresAt { get; private set; }
public DateTime? CompletedAt { get; private set; }
private NiceAuthSession() { }
public static NiceAuthSession Create(
int developerID,
string sessionToken,
string requestNo,
string transactionID,
string ticket,
int iterators
) {
if (string.IsNullOrWhiteSpace(sessionToken))
{
throw new ArgumentException("Session token is required.", nameof(sessionToken));
}
if (string.IsNullOrWhiteSpace(requestNo))
{
throw new ArgumentException("Request no is required.", nameof(requestNo));
}
if (string.IsNullOrWhiteSpace(transactionID))
{
throw new ArgumentException("Transaction ID is required.", nameof(transactionID));
}
if (string.IsNullOrWhiteSpace(ticket))
{
throw new ArgumentException("Ticket is required.", nameof(ticket));
}
if (iterators <= 0)
{
throw new ArgumentOutOfRangeException(nameof(iterators), iterators, "iterators must be positive.");
}
var now = DateTime.UtcNow;
return new NiceAuthSession
{
DeveloperID = developerID,
SessionToken = sessionToken,
RequestNo = requestNo,
TransactionID = transactionID,
Ticket = ticket,
Iterators = iterators,
Status = NiceAuthSessionStatus.Pending,
CreatedAt = now,
ExpiresAt = now + DefaultLifetime
};
}
/// callback 에서 web_transaction_id 받아 기록 — result 호출 직전 단계.
public void MarkWebTransactionReceived(string webTransactionID)
{
if (string.IsNullOrWhiteSpace(webTransactionID))
{
throw new ArgumentException("Web transaction ID is required.", nameof(webTransactionID));
}
WebTransactionID = webTransactionID;
}
public void MarkCompleted()
{
Status = NiceAuthSessionStatus.Completed;
CompletedAt = DateTime.UtcNow;
}
public void MarkFailed(string reason)
{
Status = NiceAuthSessionStatus.Failed;
FailReason = reason;
CompletedAt = DateTime.UtcNow;
}
public void MarkExpired()
{
Status = NiceAuthSessionStatus.Expired;
FailReason = "유효시간이 만료되었습니다.";
CompletedAt = DateTime.UtcNow;
}
public bool IsExpired(DateTime utcNow) => utcNow >= ExpiresAt;
public bool CanProcessCallback() => Status == NiceAuthSessionStatus.Pending;
}