import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import {
  UserWallet,
  UserWalletTransaction,
  UserWalletType,
  TransactionType,
  AssetType,
  CounterpartyType,
  PoolAccountType,
  Prisma,
} from '@prisma/client';
import Decimal from 'decimal.js';

export interface CreateUserWalletInput {
  accountSequence: string;
  walletType: UserWalletType;
}

export interface UserWalletTransactionInput {
  userWalletId: string;
  accountSequence: string;
  walletType: UserWalletType;
  transactionType: TransactionType;
  assetType: AssetType;
  amount: Decimal;
  balanceBefore: Decimal;
  balanceAfter: Decimal;
  counterpartyType?: CounterpartyType;
  counterpartyAccountSeq?: string;
  counterpartyUserId?: string;
  counterpartySystemId?: string;
  counterpartyPoolType?: PoolAccountType;
  counterpartyAddress?: string;
  referenceId?: string;
  referenceType?: string;
  txHash?: string;
  memo?: string;
  metadata?: any;
}

@Injectable()
export class UserWalletRepository {
  private readonly logger = new Logger(UserWalletRepository.name);

  constructor(private readonly prisma: PrismaService) {}

  /**
   * 创建用户钱包（如果不存在）
   */
  async createIfNotExists(input: CreateUserWalletInput): Promise<UserWallet> {
    return this.prisma.userWallet.upsert({
      where: {
        accountSequence_walletType: {
          accountSequence: input.accountSequence,
          walletType: input.walletType,
        },
      },
      create: {
        accountSequence: input.accountSequence,
        walletType: input.walletType,
      },
      update: {},
    });
  }

  /**
   * 为用户创建所有类型的钱包
   */
  async createAllWalletsForUser(accountSequence: string): Promise<UserWallet[]> {
    const walletTypes: UserWalletType[] = ['CONTRIBUTION', 'TOKEN_STORAGE', 'GREEN_POINTS'];

    return this.prisma.$transaction(
      walletTypes.map((walletType) =>
        this.prisma.userWallet.upsert({
          where: {
            accountSequence_walletType: {
              accountSequence,
              walletType,
            },
          },
          create: {
            accountSequence,
            walletType,
          },
          update: {},
        }),
      ),
    );
  }

  async findByAccountSequence(accountSequence: string): Promise<UserWallet[]> {
    return this.prisma.userWallet.findMany({
      where: { accountSequence },
    });
  }

  async findByAccountAndType(
    accountSequence: string,
    walletType: UserWalletType,
  ): Promise<UserWallet | null> {
    return this.prisma.userWallet.findUnique({
      where: {
        accountSequence_walletType: {
          accountSequence,
          walletType,
        },
      },
    });
  }

  async findById(id: string): Promise<UserWallet | null> {
    return this.prisma.userWallet.findUnique({
      where: { id },
    });
  }

  /**
   * 更新余额并记录交易（原子操作）
   */
  async updateBalanceWithTransaction(
    accountSequence: string,
    walletType: UserWalletType,
    assetType: AssetType,
    amount: Decimal,
    transactionInput: Omit<
      UserWalletTransactionInput,
      'userWalletId' | 'accountSequence' | 'walletType' | 'assetType' | 'amount' | 'balanceBefore' | 'balanceAfter'
    >,
  ): Promise<{ wallet: UserWallet; transaction: UserWalletTransaction }> {
    return this.prisma.$transaction(async (tx) => {
      // 确保钱包存在
      let wallet = await tx.userWallet.findUnique({
        where: {
          accountSequence_walletType: {
            accountSequence,
            walletType,
          },
        },
      });

      if (!wallet) {
        wallet = await tx.userWallet.create({
          data: {
            accountSequence,
            walletType,
          },
        });
      }

      const currentBalance = new Decimal(wallet.balance.toString());
      const newBalance = currentBalance.plus(amount);

      if (newBalance.lessThan(0)) {
        throw new Error(
          `Insufficient balance for user ${accountSequence} wallet ${walletType}: ${currentBalance} + ${amount} < 0`,
        );
      }

      // 更新余额
      const updatedWallet = await tx.userWallet.update({
        where: { id: wallet.id },
        data: {
          balance: newBalance.toFixed(8),
          totalInflow: amount.greaterThan(0)
            ? new Decimal(wallet.totalInflow.toString()).plus(amount).toFixed(8)
            : wallet.totalInflow,
          totalOutflow: amount.lessThan(0)
            ? new Decimal(wallet.totalOutflow.toString()).plus(amount.abs()).toFixed(8)
            : wallet.totalOutflow,
        },
      });

      // 记录交易
      const transaction = await tx.userWalletTransaction.create({
        data: {
          userWalletId: wallet.id,
          accountSequence,
          walletType,
          transactionType: transactionInput.transactionType,
          assetType,
          amount: amount.toFixed(8),
          balanceBefore: currentBalance.toFixed(8),
          balanceAfter: newBalance.toFixed(8),
          counterpartyType: transactionInput.counterpartyType,
          counterpartyAccountSeq: transactionInput.counterpartyAccountSeq,
          counterpartyUserId: transactionInput.counterpartyUserId,
          counterpartySystemId: transactionInput.counterpartySystemId,
          counterpartyPoolType: transactionInput.counterpartyPoolType,
          counterpartyAddress: transactionInput.counterpartyAddress,
          referenceId: transactionInput.referenceId,
          referenceType: transactionInput.referenceType,
          txHash: transactionInput.txHash,
          memo: transactionInput.memo,
          metadata: transactionInput.metadata,
        },
      });

      return { wallet: updatedWallet, transaction };
    });
  }

  /**
   * 冻结/解冻余额
   */
  async freezeBalance(
    accountSequence: string,
    walletType: UserWalletType,
    assetType: AssetType,
    amount: Decimal,
    freeze: boolean,
    transactionInput: Omit<
      UserWalletTransactionInput,
      'userWalletId' | 'accountSequence' | 'walletType' | 'assetType' | 'amount' | 'balanceBefore' | 'balanceAfter' | 'transactionType'
    >,
  ): Promise<{ wallet: UserWallet; transaction: UserWalletTransaction }> {
    return this.prisma.$transaction(async (tx) => {
      const wallet = await tx.userWallet.findUnique({
        where: {
          accountSequence_walletType: {
            accountSequence,
            walletType,
          },
        },
      });

      if (!wallet) {
        throw new Error(`User wallet not found: ${accountSequence} - ${walletType}`);
      }

      const currentBalance = new Decimal(wallet.balance.toString());
      const currentFrozen = new Decimal(wallet.frozenBalance.toString());

      let newBalance: Decimal;
      let newFrozen: Decimal;

      if (freeze) {
        if (currentBalance.lessThan(amount)) {
          throw new Error(`Insufficient balance to freeze: ${currentBalance} < ${amount}`);
        }
        newBalance = currentBalance.minus(amount);
        newFrozen = currentFrozen.plus(amount);
      } else {
        if (currentFrozen.lessThan(amount)) {
          throw new Error(`Insufficient frozen balance to unfreeze: ${currentFrozen} < ${amount}`);
        }
        newBalance = currentBalance.plus(amount);
        newFrozen = currentFrozen.minus(amount);
      }

      const updatedWallet = await tx.userWallet.update({
        where: { id: wallet.id },
        data: {
          balance: newBalance.toFixed(8),
          frozenBalance: newFrozen.toFixed(8),
        },
      });

      const transaction = await tx.userWalletTransaction.create({
        data: {
          userWalletId: wallet.id,
          accountSequence,
          walletType,
          transactionType: freeze ? 'FREEZE' : 'UNFREEZE',
          assetType,
          amount: amount.toFixed(8),
          balanceBefore: currentBalance.toFixed(8),
          balanceAfter: newBalance.toFixed(8),
          counterpartyType: transactionInput.counterpartyType,
          counterpartyAccountSeq: transactionInput.counterpartyAccountSeq,
          counterpartyUserId: transactionInput.counterpartyUserId,
          referenceId: transactionInput.referenceId,
          referenceType: transactionInput.referenceType,
          memo: transactionInput.memo,
          metadata: transactionInput.metadata,
        },
      });

      return { wallet: updatedWallet, transaction };
    });
  }

  /**
   * 用户间转账
   */
  async transferBetweenUsers(
    fromAccountSeq: string,
    toAccountSeq: string,
    walletType: UserWalletType,
    assetType: AssetType,
    amount: Decimal,
    referenceInfo: {
      referenceId?: string;
      referenceType?: string;
      memo?: string;
    },
  ): Promise<{
    fromWallet: UserWallet;
    toWallet: UserWallet;
    fromTransaction: UserWalletTransaction;
    toTransaction: UserWalletTransaction;
  }> {
    return this.prisma.$transaction(async (tx) => {
      // 获取发送方钱包
      const fromWallet = await tx.userWallet.findUnique({
        where: {
          accountSequence_walletType: {
            accountSequence: fromAccountSeq,
            walletType,
          },
        },
      });

      if (!fromWallet) {
        throw new Error(`From wallet not found: ${fromAccountSeq} - ${walletType}`);
      }

      const fromBalance = new Decimal(fromWallet.balance.toString());
      if (fromBalance.lessThan(amount)) {
        throw new Error(`Insufficient balance: ${fromBalance} < ${amount}`);
      }

      // 确保接收方钱包存在
      let toWallet = await tx.userWallet.findUnique({
        where: {
          accountSequence_walletType: {
            accountSequence: toAccountSeq,
            walletType,
          },
        },
      });

      if (!toWallet) {
        toWallet = await tx.userWallet.create({
          data: {
            accountSequence: toAccountSeq,
            walletType,
          },
        });
      }

      const toBalance = new Decimal(toWallet.balance.toString());

      // 更新发送方
      const newFromBalance = fromBalance.minus(amount);
      const updatedFromWallet = await tx.userWallet.update({
        where: { id: fromWallet.id },
        data: {
          balance: newFromBalance.toFixed(8),
          totalOutflow: new Decimal(fromWallet.totalOutflow.toString()).plus(amount).toFixed(8),
        },
      });

      // 更新接收方
      const newToBalance = toBalance.plus(amount);
      const updatedToWallet = await tx.userWallet.update({
        where: { id: toWallet.id },
        data: {
          balance: newToBalance.toFixed(8),
          totalInflow: new Decimal(toWallet.totalInflow.toString()).plus(amount).toFixed(8),
        },
      });

      // 记录发送方交易
      const fromTransaction = await tx.userWalletTransaction.create({
        data: {
          userWalletId: fromWallet.id,
          accountSequence: fromAccountSeq,
          walletType,
          transactionType: 'TRANSFER_OUT',
          assetType,
          amount: amount.negated().toFixed(8),
          balanceBefore: fromBalance.toFixed(8),
          balanceAfter: newFromBalance.toFixed(8),
          counterpartyType: 'USER',
          counterpartyAccountSeq: toAccountSeq,
          referenceId: referenceInfo.referenceId,
          referenceType: referenceInfo.referenceType,
          memo: referenceInfo.memo || `划转给用户[${toAccountSeq}], 数量${amount.toFixed(8)}`,
        },
      });

      // 记录接收方交易
      const toTransaction = await tx.userWalletTransaction.create({
        data: {
          userWalletId: toWallet.id,
          accountSequence: toAccountSeq,
          walletType,
          transactionType: 'TRANSFER_IN',
          assetType,
          amount: amount.toFixed(8),
          balanceBefore: toBalance.toFixed(8),
          balanceAfter: newToBalance.toFixed(8),
          counterpartyType: 'USER',
          counterpartyAccountSeq: fromAccountSeq,
          referenceId: referenceInfo.referenceId,
          referenceType: referenceInfo.referenceType,
          memo: referenceInfo.memo || `收到用户[${fromAccountSeq}]划转, 数量${amount.toFixed(8)}`,
        },
      });

      return {
        fromWallet: updatedFromWallet,
        toWallet: updatedToWallet,
        fromTransaction,
        toTransaction,
      };
    });
  }

  async getTransactions(
    accountSequence: string,
    options?: {
      walletType?: UserWalletType;
      transactionType?: TransactionType;
      assetType?: AssetType;
      startDate?: Date;
      endDate?: Date;
      limit?: number;
      offset?: number;
    },
  ): Promise<{ transactions: UserWalletTransaction[]; total: number }> {
    const where: Prisma.UserWalletTransactionWhereInput = {
      accountSequence,
    };

    if (options?.walletType) {
      where.walletType = options.walletType;
    }
    if (options?.transactionType) {
      where.transactionType = options.transactionType;
    }
    if (options?.assetType) {
      where.assetType = options.assetType;
    }
    if (options?.startDate || options?.endDate) {
      where.createdAt = {};
      if (options.startDate) where.createdAt.gte = options.startDate;
      if (options.endDate) where.createdAt.lte = options.endDate;
    }

    const [transactions, total] = await Promise.all([
      this.prisma.userWalletTransaction.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        take: options?.limit || 50,
        skip: options?.offset || 0,
      }),
      this.prisma.userWalletTransaction.count({ where }),
    ]);

    return { transactions, total };
  }

  /**
   * 获取用户所有钱包余额汇总
   */
  async getUserWalletSummary(accountSequence: string): Promise<{
    walletType: UserWalletType;
    balance: string;
    frozenBalance: string;
    totalInflow: string;
    totalOutflow: string;
  }[]> {
    const wallets = await this.prisma.userWallet.findMany({
      where: { accountSequence },
      select: {
        walletType: true,
        balance: true,
        frozenBalance: true,
        totalInflow: true,
        totalOutflow: true,
      },
    });

    return wallets.map((w) => ({
      walletType: w.walletType,
      balance: w.balance.toString(),
      frozenBalance: w.frozenBalance.toString(),
      totalInflow: w.totalInflow.toString(),
      totalOutflow: w.totalOutflow.toString(),
    }));
  }
}
