import { Injectable, Logger, OnModuleInit, Inject } from '@nestjs/common';
import {
  WithdrawalEventConsumerService,
  WithdrawalConfirmedPayload,
  WithdrawalFailedPayload,
} from '@/infrastructure/kafka/withdrawal-event-consumer.service';
import {
  IWithdrawalOrderRepository,
  WITHDRAWAL_ORDER_REPOSITORY,
  IWalletAccountRepository,
  WALLET_ACCOUNT_REPOSITORY,
  ILedgerEntryRepository,
  LEDGER_ENTRY_REPOSITORY,
} from '@/domain/repositories';
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
import { WithdrawalOrder, WalletAccount, LedgerEntry } from '@/domain/aggregates';
import { WithdrawalStatus, Money, UserId, LedgerEntryType } from '@/domain/value-objects';
import { OptimisticLockError } from '@/shared/exceptions/domain.exception';
import Decimal from 'decimal.js';

/**
 * Withdrawal Status Handler
 *
 * Handles withdrawal status events from blockchain-service.
 * Updates withdrawal order status and handles fund refunds on failure.
 *
 * IMPORTANT:
 * - All operations use database transactions for atomicity.
 * - Wallet balance updates use optimistic locking to prevent concurrent modification issues.
 */
@Injectable()
export class WithdrawalStatusHandler implements OnModuleInit {
  private readonly logger = new Logger(WithdrawalStatusHandler.name);

  // Max retry count for optimistic lock conflicts
  private readonly MAX_RETRIES = 3;

  constructor(
    private readonly withdrawalEventConsumer: WithdrawalEventConsumerService,
    @Inject(WITHDRAWAL_ORDER_REPOSITORY)
    private readonly withdrawalRepo: IWithdrawalOrderRepository,
    @Inject(WALLET_ACCOUNT_REPOSITORY)
    private readonly walletRepo: IWalletAccountRepository,
    @Inject(LEDGER_ENTRY_REPOSITORY)
    private readonly ledgerRepo: ILedgerEntryRepository,
    private readonly prisma: PrismaService,
  ) {}

  onModuleInit() {
    this.withdrawalEventConsumer.onWithdrawalConfirmed(
      this.handleWithdrawalConfirmed.bind(this),
    );
    this.withdrawalEventConsumer.onWithdrawalFailed(
      this.handleWithdrawalFailed.bind(this),
    );
    this.logger.log(`[INIT] WithdrawalStatusHandler registered`);
  }

  /**
   * Handle withdrawal confirmed event
   * Update order status to CONFIRMED, store txHash, and deduct frozen balance
   *
   * Uses database transaction + optimistic locking to ensure atomicity and prevent race conditions.
   */
  private async handleWithdrawalConfirmed(
    payload: WithdrawalConfirmedPayload,
  ): Promise<void> {
    this.logger.log(`[CONFIRMED] Processing withdrawal confirmation`);
    this.logger.log(`[CONFIRMED] orderNo: ${payload.orderNo}`);
    this.logger.log(`[CONFIRMED] txHash: ${payload.txHash}`);

    let retries = 0;
    while (retries < this.MAX_RETRIES) {
      try {
        await this.executeWithdrawalConfirmed(payload);
        return; // Success, exit
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[CONFIRMED] Optimistic lock conflict for ${payload.orderNo}, retry ${retries}/${this.MAX_RETRIES}`);
          if (retries >= this.MAX_RETRIES) {
            this.logger.error(`[CONFIRMED] Max retries exceeded for ${payload.orderNo}`);
            throw error;
          }
          // Brief delay before retry
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }
  }

  /**
   * Execute the withdrawal confirmed logic within a transaction
   */
  private async executeWithdrawalConfirmed(
    payload: WithdrawalConfirmedPayload,
  ): Promise<void> {
    try {
      // Use transaction to ensure atomicity
      await this.prisma.$transaction(async (tx) => {
        // Find the withdrawal order
        const orderRecord = await tx.withdrawalOrder.findUnique({
          where: { orderNo: payload.orderNo },
        });

        if (!orderRecord) {
          this.logger.error(`[CONFIRMED] Order not found: ${payload.orderNo}`);
          return;
        }

        // Check if already confirmed (idempotency)
        if (orderRecord.status === WithdrawalStatus.CONFIRMED) {
          this.logger.log(`[CONFIRMED] Order ${payload.orderNo} already confirmed, skipping`);
          return;
        }

        // Determine new status based on current status
        let newStatus = orderRecord.status;
        let txHash = orderRecord.txHash;
        let broadcastedAt = orderRecord.broadcastedAt;
        let confirmedAt = orderRecord.confirmedAt;

        // FROZEN -> BROADCASTED -> CONFIRMED
        if (orderRecord.status === WithdrawalStatus.FROZEN) {
          newStatus = WithdrawalStatus.BROADCASTED;
          txHash = payload.txHash;
          broadcastedAt = new Date();
        }

        if (newStatus === WithdrawalStatus.BROADCASTED || orderRecord.status === WithdrawalStatus.BROADCASTED) {
          newStatus = WithdrawalStatus.CONFIRMED;
          confirmedAt = new Date();
        }

        // Update order status
        await tx.withdrawalOrder.update({
          where: { id: orderRecord.id },
          data: {
            status: newStatus,
            txHash,
            broadcastedAt,
            confirmedAt,
          },
        });

        // Find wallet and deduct frozen balance with optimistic lock
        let walletRecord = await tx.walletAccount.findUnique({
          where: { accountSequence: orderRecord.accountSequence },
        });

        if (!walletRecord) {
          walletRecord = await tx.walletAccount.findUnique({
            where: { userId: orderRecord.userId },
          });
        }

        if (walletRecord) {
          // Deduct the total frozen amount (amount + fee)
          const totalAmount = new Decimal(orderRecord.amount.toString()).add(new Decimal(orderRecord.fee.toString()));
          const currentFrozen = new Decimal(walletRecord.usdtFrozen.toString());

          if (currentFrozen.lessThan(totalAmount)) {
            this.logger.error(`[CONFIRMED] Insufficient frozen balance: have ${currentFrozen}, need ${totalAmount}`);
            throw new Error(`Insufficient frozen balance for withdrawal ${payload.orderNo}`);
          }

          const newFrozen = currentFrozen.minus(totalAmount);
          const currentVersion = walletRecord.version;

          // Optimistic lock: update only if version matches
          const updateResult = await tx.walletAccount.updateMany({
            where: {
              id: walletRecord.id,
              version: currentVersion, // Optimistic lock condition
            },
            data: {
              usdtFrozen: newFrozen,
              version: currentVersion + 1, // Increment version
              updatedAt: new Date(),
            },
          });

          if (updateResult.count === 0) {
            // Version mismatch - another transaction modified the record
            throw new OptimisticLockError(`Optimistic lock conflict for wallet ${walletRecord.id}`);
          }

          this.logger.log(`[CONFIRMED] Deducted ${totalAmount.toString()} USDT from frozen balance for ${orderRecord.accountSequence} (version: ${currentVersion} -> ${currentVersion + 1})`);

          // 记录流水：根据是否内部转账决定流水类型
          if (orderRecord.isInternalTransfer && orderRecord.toAccountSequence) {
            // 内部转账：给转出方记录 TRANSFER_OUT
            await tx.ledgerEntry.create({
              data: {
                accountSequence: orderRecord.accountSequence,
                userId: orderRecord.userId,
                entryType: LedgerEntryType.TRANSFER_OUT,
                amount: new Decimal(orderRecord.amount.toString()).negated(),
                assetType: 'USDT',
                balanceAfter: walletRecord.usdtAvailable,  // 冻结余额扣除后可用余额不变
                refOrderId: orderRecord.orderNo,
                refTxHash: payload.txHash,
                memo: `转账至 ${orderRecord.toAccountSequence}`,
                payloadJson: {
                  toAccountSequence: orderRecord.toAccountSequence,
                  toUserId: orderRecord.toUserId?.toString(),
                  fee: orderRecord.fee.toString(),
                },
              },
            });

            // 内部转账：给接收方记录 TRANSFER_IN 并增加余额
            if (orderRecord.toUserId) {
              // 查找接收方钱包
              let toWalletRecord = await tx.walletAccount.findUnique({
                where: { accountSequence: orderRecord.toAccountSequence },
              });

              if (!toWalletRecord) {
                toWalletRecord = await tx.walletAccount.findUnique({
                  where: { userId: orderRecord.toUserId },
                });
              }

              // 如果接收方钱包不存在，自动创建（使用 upsert 避免并发问题）
              if (!toWalletRecord) {
                this.logger.log(`[CONFIRMED] Receiver wallet not found, auto-creating for: ${orderRecord.toAccountSequence}`);
                toWalletRecord = await tx.walletAccount.upsert({
                  where: { accountSequence: orderRecord.toAccountSequence },
                  create: {
                    accountSequence: orderRecord.toAccountSequence,
                    userId: orderRecord.toUserId,
                    usdtAvailable: new Decimal(0),
                    usdtFrozen: new Decimal(0),
                    dstAvailable: new Decimal(0),
                    dstFrozen: new Decimal(0),
                    bnbAvailable: new Decimal(0),
                    bnbFrozen: new Decimal(0),
                    ogAvailable: new Decimal(0),
                    ogFrozen: new Decimal(0),
                    rwadAvailable: new Decimal(0),
                    rwadFrozen: new Decimal(0),
                    hashpower: new Decimal(0),
                    pendingUsdt: new Decimal(0),
                    pendingHashpower: new Decimal(0),
                    settleableUsdt: new Decimal(0),
                    settleableHashpower: new Decimal(0),
                    settledTotalUsdt: new Decimal(0),
                    settledTotalHashpower: new Decimal(0),
                    expiredTotalUsdt: new Decimal(0),
                    expiredTotalHashpower: new Decimal(0),
                    status: 'ACTIVE',
                    hasPlanted: false,
                    version: 0,
                  },
                  update: {}, // 如果已存在，不做任何更新
                });
                this.logger.log(`[CONFIRMED] Auto-created/found wallet for receiver: ${orderRecord.toAccountSequence} (id=${toWalletRecord.id})`);
              }

              const transferAmount = new Decimal(orderRecord.amount.toString());
              const toCurrentAvailable = new Decimal(toWalletRecord.usdtAvailable.toString());
              const toNewAvailable = toCurrentAvailable.add(transferAmount);
              const toCurrentVersion = toWalletRecord.version;

              // 更新接收方余额
              const toUpdateResult = await tx.walletAccount.updateMany({
                where: {
                  id: toWalletRecord.id,
                  version: toCurrentVersion,
                },
                data: {
                  usdtAvailable: toNewAvailable,
                  version: toCurrentVersion + 1,
                  updatedAt: new Date(),
                },
              });

              if (toUpdateResult.count === 0) {
                throw new OptimisticLockError(`Optimistic lock conflict for receiver wallet ${toWalletRecord.id}`);
              }

              // 给接收方记录 TRANSFER_IN 流水
              await tx.ledgerEntry.create({
                data: {
                  accountSequence: orderRecord.toAccountSequence,
                  userId: orderRecord.toUserId,
                  entryType: LedgerEntryType.TRANSFER_IN,
                  amount: transferAmount,
                  assetType: 'USDT',
                  balanceAfter: toNewAvailable,
                  refOrderId: orderRecord.orderNo,
                  refTxHash: payload.txHash,
                  memo: `来自 ${orderRecord.accountSequence} 的转账`,
                  payloadJson: {
                    fromAccountSequence: orderRecord.accountSequence,
                    fromUserId: orderRecord.userId.toString(),
                  },
                },
              });

              this.logger.log(`[CONFIRMED] Internal transfer: ${orderRecord.accountSequence} -> ${orderRecord.toAccountSequence}, amount: ${transferAmount.toString()}`);
            }
          } else {
            // 普通提现：记录 WITHDRAWAL
            await tx.ledgerEntry.create({
              data: {
                accountSequence: orderRecord.accountSequence,
                userId: orderRecord.userId,
                entryType: LedgerEntryType.WITHDRAWAL,
                amount: new Decimal(orderRecord.amount.toString()).negated(),
                assetType: 'USDT',
                balanceAfter: walletRecord.usdtAvailable,
                refOrderId: orderRecord.orderNo,
                refTxHash: payload.txHash,
                memo: `提现至 ${orderRecord.toAddress}`,
                payloadJson: {
                  toAddress: orderRecord.toAddress,
                  chainType: orderRecord.chainType,
                  fee: orderRecord.fee.toString(),
                },
              },
            });
          }
        } else {
          this.logger.error(`[CONFIRMED] Wallet not found for accountSequence: ${orderRecord.accountSequence}, userId: ${orderRecord.userId}`);
        }
      });

      this.logger.log(`[CONFIRMED] Order ${payload.orderNo} confirmed successfully`);
    } catch (error) {
      this.logger.error(`[CONFIRMED] Failed to process confirmation for ${payload.orderNo}`, error);
      throw error;
    }
  }

  /**
   * Handle withdrawal failed event
   * Update order status to FAILED and refund frozen funds (amount + fee)
   *
   * Uses database transaction + optimistic locking to ensure atomicity and prevent race conditions.
   */
  private async handleWithdrawalFailed(
    payload: WithdrawalFailedPayload,
  ): Promise<void> {
    this.logger.log(`[FAILED] Processing withdrawal failure`);
    this.logger.log(`[FAILED] orderNo: ${payload.orderNo}`);
    this.logger.log(`[FAILED] error: ${payload.error}`);

    let retries = 0;
    while (retries < this.MAX_RETRIES) {
      try {
        await this.executeWithdrawalFailed(payload);
        return; // Success, exit
      } catch (error) {
        if (this.isOptimisticLockError(error)) {
          retries++;
          this.logger.warn(`[FAILED] Optimistic lock conflict for ${payload.orderNo}, retry ${retries}/${this.MAX_RETRIES}`);
          if (retries >= this.MAX_RETRIES) {
            this.logger.error(`[FAILED] Max retries exceeded for ${payload.orderNo}`);
            throw error;
          }
          // Brief delay before retry
          await this.sleep(50 * retries);
        } else {
          throw error;
        }
      }
    }
  }

  /**
   * Execute the withdrawal failed logic within a transaction
   */
  private async executeWithdrawalFailed(
    payload: WithdrawalFailedPayload,
  ): Promise<void> {
    try {
      // Use transaction to ensure atomicity
      await this.prisma.$transaction(async (tx) => {
        // Find the withdrawal order
        const orderRecord = await tx.withdrawalOrder.findUnique({
          where: { orderNo: payload.orderNo },
        });

        if (!orderRecord) {
          this.logger.error(`[FAILED] Order not found: ${payload.orderNo}`);
          return;
        }

        // Check if already in terminal state (idempotency)
        if (orderRecord.status === WithdrawalStatus.CONFIRMED ||
            orderRecord.status === WithdrawalStatus.FAILED ||
            orderRecord.status === WithdrawalStatus.CANCELLED) {
          this.logger.log(`[FAILED] Order ${payload.orderNo} already in terminal state: ${orderRecord.status}, skipping`);
          return;
        }

        // Check if needs unfreeze (was frozen)
        const needsUnfreeze = orderRecord.frozenAt !== null;

        // Update order status to FAILED
        await tx.withdrawalOrder.update({
          where: { id: orderRecord.id },
          data: {
            status: WithdrawalStatus.FAILED,
            errorMessage: payload.error,
          },
        });

        // Refund frozen funds back to available balance if needed
        if (needsUnfreeze) {
          let walletRecord = await tx.walletAccount.findUnique({
            where: { accountSequence: orderRecord.accountSequence },
          });

          if (!walletRecord) {
            walletRecord = await tx.walletAccount.findUnique({
              where: { userId: orderRecord.userId },
            });
          }

          if (walletRecord) {
            // Unfreeze the total amount (amount + fee)
            const totalAmount = new Decimal(orderRecord.amount.toString()).add(new Decimal(orderRecord.fee.toString()));
            const currentFrozen = new Decimal(walletRecord.usdtFrozen.toString());
            const currentAvailable = new Decimal(walletRecord.usdtAvailable.toString());
            const currentVersion = walletRecord.version;

            // Validate frozen balance
            let newFrozen: Decimal;
            let newAvailable: Decimal;

            if (currentFrozen.lessThan(totalAmount)) {
              this.logger.warn(`[FAILED] Frozen balance (${currentFrozen}) less than refund amount (${totalAmount}), refunding what's available`);
              // Refund whatever is frozen (shouldn't happen in normal flow)
              const refundAmount = Decimal.min(currentFrozen, totalAmount);
              newFrozen = currentFrozen.minus(refundAmount);
              newAvailable = currentAvailable.add(refundAmount);
            } else {
              newFrozen = currentFrozen.minus(totalAmount);
              newAvailable = currentAvailable.add(totalAmount);
            }

            // Optimistic lock: update only if version matches
            const updateResult = await tx.walletAccount.updateMany({
              where: {
                id: walletRecord.id,
                version: currentVersion, // Optimistic lock condition
              },
              data: {
                usdtFrozen: newFrozen,
                usdtAvailable: newAvailable,
                version: currentVersion + 1, // Increment version
                updatedAt: new Date(),
              },
            });

            if (updateResult.count === 0) {
              // Version mismatch - another transaction modified the record
              throw new OptimisticLockError(`Optimistic lock conflict for wallet ${walletRecord.id}`);
            }

            this.logger.log(`[FAILED] Refunded ${totalAmount.toString()} USDT (amount + fee) to account ${orderRecord.accountSequence} (version: ${currentVersion} -> ${currentVersion + 1})`);

            // 记录 UNFREEZE 流水（修复：之前缺失导致审计缺口）
            await tx.ledgerEntry.create({
              data: {
                accountSequence: orderRecord.accountSequence,
                userId: orderRecord.userId,
                entryType: LedgerEntryType.UNFREEZE,
                amount: newAvailable.minus(currentAvailable),
                assetType: 'USDT',
                balanceAfter: newAvailable,
                refOrderId: orderRecord.orderNo,
                memo: `提现失败退款: ${payload.error}`,
                payloadJson: {
                  toAddress: orderRecord.toAddress,
                  chainType: orderRecord.chainType,
                  error: payload.error,
                },
              },
            });
          } else {
            this.logger.error(`[FAILED] Wallet not found for accountSequence: ${orderRecord.accountSequence}, userId: ${orderRecord.userId}`);
          }
        }
      });

      this.logger.log(`[FAILED] Order ${payload.orderNo} marked as failed`);
    } catch (error) {
      this.logger.error(`[FAILED] Failed to process failure for ${payload.orderNo}`, error);
      throw error;
    }
  }

  /**
   * Check if error is an optimistic lock error
   */
  private isOptimisticLockError(error: unknown): boolean {
    return error instanceof OptimisticLockError;
  }

  /**
   * Sleep for specified milliseconds
   */
  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}
