using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Domain.Entities.Members;
using Domain.Entities.Payments.ValueObject;

namespace Domain.Entities.Payments;

public class PaymentOrder
{
    [ForeignKey(nameof(MemberID))]
    public virtual Member? Member { get; private set; }

    [Key]
    public int ID { get; private set; }

    public int MemberID { get; private set; }

    /// <summary>가맹점 주문번호 (unique, 중복 불가)</summary>
    public string OrderID { get; private set; } = default!;

    /// <summary>다날 CPID (계약 완료 후 발급)</summary>
    public string MerchantID { get; private set; } = default!;

    /// <summary>상품명</summary>
    public string OrderName { get; private set; } = "포인트 충전";

    /// <summary>다날 거래번호 (승인 후 저장)</summary>
    public string? TransactionID { get; private set; }

    /// <summary>결제 금액 (부가세 포함 총액)</summary>
    public int Amount { get; private set; }

    /// <summary>충전될 포인트 (= 결제금액, 1:1)</summary>
    public int PointAmount { get; private set; }

    /// <summary>부가세 (표시용)</summary>
    public int VatAmount { get; private set; }

    public PaymentMethod PaymentMethod { get; private set; }

    public PaymentStatus Status { get; private set; }

    public DateTime? PaidAt { get; private set; }

    public DateTime? CancelledAt { get; private set; }

    public string? FailReason { get; private set; }

    /// <summary>가상계좌: 계좌번호</summary>
    public string? VirtualAccountNumber { get; private set; }

    /// <summary>가상계좌: 은행명</summary>
    public string? VirtualAccountBank { get; private set; }

    /// <summary>가상계좌: 예금주</summary>
    public string? VirtualAccountHolder { get; private set; }

    /// <summary>가상계좌: 입금 기한</summary>
    public DateTime? VirtualAccountExpireAt { get; private set; }

    public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;

    private PaymentOrder() { }

    public static PaymentOrder Create(
        int memberID,
        string orderID,
        string merchantID,
        int amount,
        PaymentMethod method,
        string orderName = "포인트 충전"
    ) {
        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(memberID);
        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);

        if (string.IsNullOrWhiteSpace(orderID)) {
            throw new ArgumentException("orderID required", nameof(orderID));
        }

        if (string.IsNullOrWhiteSpace(merchantID)) {
            throw new ArgumentException("merchantID required", nameof(merchantID));
        }

        var vat = (int)Math.Round(amount / 11.0);

        return new PaymentOrder
        {
            MemberID = memberID,
            OrderID = orderID,
            MerchantID = merchantID,
            OrderName = orderName,
            Amount = amount,
            PointAmount = amount,
            VatAmount = vat,
            PaymentMethod = method,
            Status = PaymentStatus.Pending
        };
    }

    public void MarkPaid(string transactionID)
    {
        if (Status is not PaymentStatus.Pending and not PaymentStatus.WaitingDeposit)
        {
            throw new InvalidOperationException($"Cannot mark as paid from status {Status}");
        }

        TransactionID = transactionID;
        Status = PaymentStatus.Paid;
        PaidAt = DateTime.UtcNow;
    }

    public void MarkFailed(string reason)
    {
        Status = PaymentStatus.Failed;
        FailReason = reason;
    }

    public void MarkCancelled()
    {
        if (Status != PaymentStatus.Paid)
        {
            throw new InvalidOperationException($"Cannot cancel from status {Status}");
        }

        Status = PaymentStatus.Cancelled;
        CancelledAt = DateTime.UtcNow;
    }

    public void MarkWaitingDeposit(string accountNumber, string bank, string holder, DateTime expireAt)
    {
        Status = PaymentStatus.WaitingDeposit;
        VirtualAccountNumber = accountNumber;
        VirtualAccountBank = bank;
        VirtualAccountHolder = holder;
        VirtualAccountExpireAt = expireAt;
    }
}