import Decimal from 'decimal.js';
import { UserId, ChainType, AssetType, Money } from '@/domain/value-objects';
import { WithdrawalStatus } from '@/domain/value-objects/withdrawal-status.enum';
import { DomainError } from '@/shared/exceptions/domain.exception';

/**
 * 提现订单聚合根
 *
 * 提现流程:
 * 1. 用户发起提现请求 -> PENDING
 * 2. 冻结用户余额 -> FROZEN
 * 3. blockchain-service 签名并广播 -> BROADCASTED
 * 4. 链上确认 -> CONFIRMED
 *
 * 失败/取消时解冻资金
 */
export class WithdrawalOrder {
  private readonly _id: bigint;
  private readonly _orderNo: string;
  private readonly _accountSequence: string;
  private readonly _userId: UserId;
  private readonly _amount: Money;
  private readonly _fee: Money;          // 手续费
  private readonly _chainType: ChainType;
  private readonly _toAddress: string;   // 提现目标地址
  private _txHash: string | null;
  // 内部转账标识
  private readonly _isInternalTransfer: boolean;  // 是否为内部转账（ID转ID）
  private readonly _toAccountSequence: string | null;  // 接收方ID（内部转账时有值）
  private readonly _toUserId: UserId | null;  // 接收方用户ID（内部转账时有值）
  private _status: WithdrawalStatus;
  private _errorMessage: string | null;
  private _frozenAt: Date | null;
  private _broadcastedAt: Date | null;
  private _confirmedAt: Date | null;
  private readonly _createdAt: Date;

  private constructor(
    id: bigint,
    orderNo: string,
    accountSequence: string,
    userId: UserId,
    amount: Money,
    fee: Money,
    chainType: ChainType,
    toAddress: string,
    txHash: string | null,
    isInternalTransfer: boolean,
    toAccountSequence: string | null,
    toUserId: UserId | null,
    status: WithdrawalStatus,
    errorMessage: string | null,
    frozenAt: Date | null,
    broadcastedAt: Date | null,
    confirmedAt: Date | null,
    createdAt: Date,
  ) {
    this._id = id;
    this._orderNo = orderNo;
    this._accountSequence = accountSequence;
    this._userId = userId;
    this._amount = amount;
    this._fee = fee;
    this._chainType = chainType;
    this._toAddress = toAddress;
    this._txHash = txHash;
    this._isInternalTransfer = isInternalTransfer;
    this._toAccountSequence = toAccountSequence;
    this._toUserId = toUserId;
    this._status = status;
    this._errorMessage = errorMessage;
    this._frozenAt = frozenAt;
    this._broadcastedAt = broadcastedAt;
    this._confirmedAt = confirmedAt;
    this._createdAt = createdAt;
  }

  // Getters
  get id(): bigint { return this._id; }
  get orderNo(): string { return this._orderNo; }
  get accountSequence(): string { return this._accountSequence; }
  get userId(): UserId { return this._userId; }
  get amount(): Money { return this._amount; }
  get fee(): Money { return this._fee; }
  get netAmount(): Money { return this._amount; }  // 接收方收到完整金额，手续费由发送方额外承担
  get chainType(): ChainType { return this._chainType; }
  get toAddress(): string { return this._toAddress; }
  get txHash(): string | null { return this._txHash; }
  get isInternalTransfer(): boolean { return this._isInternalTransfer; }
  get toAccountSequence(): string | null { return this._toAccountSequence; }
  get toUserId(): UserId | null { return this._toUserId; }
  get status(): WithdrawalStatus { return this._status; }
  get errorMessage(): string | null { return this._errorMessage; }
  get frozenAt(): Date | null { return this._frozenAt; }
  get broadcastedAt(): Date | null { return this._broadcastedAt; }
  get confirmedAt(): Date | null { return this._confirmedAt; }
  get createdAt(): Date { return this._createdAt; }

  get isPending(): boolean { return this._status === WithdrawalStatus.PENDING; }
  get isFrozen(): boolean { return this._status === WithdrawalStatus.FROZEN; }
  get isBroadcasted(): boolean { return this._status === WithdrawalStatus.BROADCASTED; }
  get isConfirmed(): boolean { return this._status === WithdrawalStatus.CONFIRMED; }
  get isFailed(): boolean { return this._status === WithdrawalStatus.FAILED; }
  get isCancelled(): boolean { return this._status === WithdrawalStatus.CANCELLED; }
  get isFinished(): boolean {
    return this._status === WithdrawalStatus.CONFIRMED ||
           this._status === WithdrawalStatus.FAILED ||
           this._status === WithdrawalStatus.CANCELLED;
  }

  /**
   * 生成提现订单号
   */
  private static generateOrderNo(): string {
    const timestamp = Date.now();
    const random = Math.random().toString(36).substring(2, 8).toUpperCase();
    return `WD${timestamp}${random}`;
  }

  /**
   * 创建提现订单
   */
  static create(params: {
    accountSequence: string;
    userId: UserId;
    amount: Money;
    fee: Money;
    chainType: ChainType;
    toAddress: string;
    isInternalTransfer?: boolean;
    toAccountSequence?: string;
    toUserId?: UserId;
  }): WithdrawalOrder {
    // 验证金额
    if (params.amount.value <= 0) {
      throw new DomainError('Withdrawal amount must be positive');
    }

    // 验证手续费
    if (params.fee.value < 0) {
      throw new DomainError('Withdrawal fee cannot be negative');
    }

    // 验证净额大于0
    if (params.amount.value <= params.fee.value) {
      throw new DomainError('Withdrawal amount must be greater than fee');
    }

    // 验证地址格式 (简单的EVM地址检查)
    if (!params.toAddress.match(/^0x[a-fA-F0-9]{40}$/)) {
      throw new DomainError('Invalid withdrawal address format');
    }

    return new WithdrawalOrder(
      BigInt(0), // Will be set by database
      this.generateOrderNo(),
      params.accountSequence,
      params.userId,
      params.amount,
      params.fee,
      params.chainType,
      params.toAddress,
      null,
      params.isInternalTransfer ?? false,
      params.toAccountSequence ?? null,
      params.toUserId ?? null,
      WithdrawalStatus.PENDING,
      null,
      null,
      null,
      null,
      new Date(),
    );
  }

  /**
   * 从数据库重建
   */
  static reconstruct(params: {
    id: bigint;
    orderNo: string;
    accountSequence: string;
    userId: bigint;
    amount: Decimal;
    fee: Decimal;
    chainType: string;
    toAddress: string;
    txHash: string | null;
    isInternalTransfer: boolean;
    toAccountSequence: string | null;
    toUserId: bigint | null;
    status: string;
    errorMessage: string | null;
    frozenAt: Date | null;
    broadcastedAt: Date | null;
    confirmedAt: Date | null;
    createdAt: Date;
  }): WithdrawalOrder {
    return new WithdrawalOrder(
      params.id,
      params.orderNo,
      params.accountSequence,
      UserId.create(params.userId),
      Money.USDT(params.amount),
      Money.USDT(params.fee),
      params.chainType as ChainType,
      params.toAddress,
      params.txHash,
      params.isInternalTransfer,
      params.toAccountSequence,
      params.toUserId ? UserId.create(params.toUserId) : null,
      params.status as WithdrawalStatus,
      params.errorMessage,
      params.frozenAt,
      params.broadcastedAt,
      params.confirmedAt,
      params.createdAt,
    );
  }

  /**
   * 标记为已冻结 (资金已从可用余额冻结)
   */
  markAsFrozen(): void {
    if (this._status !== WithdrawalStatus.PENDING) {
      throw new DomainError('Only pending withdrawals can be frozen');
    }
    this._status = WithdrawalStatus.FROZEN;
    this._frozenAt = new Date();
  }

  /**
   * 标记为已广播
   */
  markAsBroadcasted(txHash: string): void {
    if (this._status !== WithdrawalStatus.FROZEN) {
      throw new DomainError('Only frozen withdrawals can be broadcasted');
    }
    this._status = WithdrawalStatus.BROADCASTED;
    this._txHash = txHash;
    this._broadcastedAt = new Date();
  }

  /**
   * 标记为已确认 (链上确认)
   */
  markAsConfirmed(): void {
    if (this._status !== WithdrawalStatus.BROADCASTED) {
      throw new DomainError('Only broadcasted withdrawals can be confirmed');
    }
    this._status = WithdrawalStatus.CONFIRMED;
    this._confirmedAt = new Date();
  }

  /**
   * 标记为失败
   */
  markAsFailed(errorMessage: string): void {
    if (this.isFinished) {
      throw new DomainError('Cannot fail a finished withdrawal');
    }
    this._status = WithdrawalStatus.FAILED;
    this._errorMessage = errorMessage;
  }

  /**
   * 取消提现
   */
  cancel(): void {
    if (this._status !== WithdrawalStatus.PENDING && this._status !== WithdrawalStatus.FROZEN) {
      throw new DomainError('Only pending or frozen withdrawals can be cancelled');
    }
    this._status = WithdrawalStatus.CANCELLED;
  }

  /**
   * 是否需要解冻资金 (失败或取消且已冻结)
   */
  needsUnfreeze(): boolean {
    return (this._status === WithdrawalStatus.FAILED || this._status === WithdrawalStatus.CANCELLED)
           && this._frozenAt !== null;
  }
}
