import { Injectable, Logger } from '@nestjs/common';
import { UserWalletRepository } from '../../infrastructure/persistence/repositories/user-wallet.repository';
import { RegionRepository } from '../../infrastructure/persistence/repositories/region.repository';
import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository';
import { UserWallet, UserWalletType, AssetType, TransactionType } from '@prisma/client';
import Decimal from 'decimal.js';
import { DomainException } from '../../shared/filters/domain-exception.filter';

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

  constructor(
    private readonly userWalletRepo: UserWalletRepository,
    private readonly regionRepo: RegionRepository,
    private readonly outboxRepo: OutboxRepository,
  ) {}

  /**
   * 为新用户创建所有钱包
   */
  async createWalletsForUser(accountSequence: string): Promise<UserWallet[]> {
    const wallets = await this.userWalletRepo.createAllWalletsForUser(accountSequence);

    await this.outboxRepo.create({
      aggregateType: 'UserWallet',
      aggregateId: accountSequence,
      eventType: 'USER_WALLETS_CREATED',
      payload: {
        accountSequence,
        walletTypes: wallets.map((w) => w.walletType),
        createdAt: new Date().toISOString(),
      },
    });

    this.logger.log(`Created ${wallets.length} wallets for user ${accountSequence}`);
    return wallets;
  }

  /**
   * 获取用户所有钱包
   */
  async getUserWallets(accountSequence: string): Promise<UserWallet[]> {
    return this.userWalletRepo.findByAccountSequence(accountSequence);
  }

  /**
   * 获取用户特定类型钱包
   */
  async getUserWallet(
    accountSequence: string,
    walletType: UserWalletType,
  ): Promise<UserWallet | null> {
    return this.userWalletRepo.findByAccountAndType(accountSequence, walletType);
  }

  /**
   * 获取用户钱包汇总
   */
  async getUserWalletSummary(accountSequence: string) {
    return this.userWalletRepo.getUserWalletSummary(accountSequence);
  }

  /**
   * 增加算力（来自挖矿系统同步）
   */
  async addContribution(
    accountSequence: string,
    amount: Decimal,
    source: {
      referenceId: string;
      referenceType: string;
      memo?: string;
    },
  ): Promise<UserWallet> {
    const { wallet } = await this.userWalletRepo.updateBalanceWithTransaction(
      accountSequence,
      'CONTRIBUTION',
      'CONTRIBUTION',
      amount,
      {
        transactionType: 'TRANSFER_IN',
        counterpartyType: 'SYSTEM_ACCOUNT',
        referenceId: source.referenceId,
        referenceType: source.referenceType,
        memo: source.memo || `算力增加, 数量${amount.toFixed(8)}`,
      },
    );

    await this.outboxRepo.create({
      aggregateType: 'UserWallet',
      aggregateId: accountSequence,
      eventType: 'CONTRIBUTION_ADDED',
      payload: {
        accountSequence,
        amount: amount.toString(),
        newBalance: wallet.balance.toString(),
        referenceId: source.referenceId,
      },
    });

    return wallet;
  }

  /**
   * 接收挖矿奖励（积分股）
   */
  async receiveMiningReward(
    accountSequence: string,
    amount: Decimal,
    miningInfo: {
      miningMinute: Date;
      contributionRatio: Decimal;
      referenceId?: string;
    },
  ): Promise<UserWallet> {
    const memo = `挖矿奖励, 贡献值占比${miningInfo.contributionRatio.mul(100).toFixed(4)}%, 分钟${miningInfo.miningMinute.toISOString()}`;

    const { wallet } = await this.userWalletRepo.updateBalanceWithTransaction(
      accountSequence,
      'TOKEN_STORAGE',
      'SHARE',
      amount,
      {
        transactionType: 'MINING_REWARD',
        counterpartyType: 'POOL',
        counterpartyPoolType: 'SHARE_POOL_A',
        referenceId: miningInfo.referenceId,
        referenceType: 'MINING_RECORD',
        memo,
        metadata: {
          miningMinute: miningInfo.miningMinute.toISOString(),
          contributionRatio: miningInfo.contributionRatio.toString(),
        },
      },
    );

    await this.outboxRepo.create({
      aggregateType: 'UserWallet',
      aggregateId: accountSequence,
      eventType: 'MINING_REWARD_RECEIVED',
      payload: {
        accountSequence,
        amount: amount.toString(),
        newBalance: wallet.balance.toString(),
        miningMinute: miningInfo.miningMinute.toISOString(),
        contributionRatio: miningInfo.contributionRatio.toString(),
      },
    });

    return wallet;
  }

  /**
   * 接收手工补发挖矿奖励（积分股）
   */
  async receiveManualMiningReward(
    accountSequence: string,
    amount: Decimal,
    manualMiningInfo: {
      recordId: string;
      adoptionDate: string;
      effectiveDate: string;
      totalSeconds: string;
      contributionRatio: string;
      operatorId: string;
      operatorName: string;
      reason: string;
    },
  ): Promise<UserWallet> {
    const memo = `手工补发挖矿奖励 - 认种日期:${manualMiningInfo.adoptionDate} - 补发秒数:${manualMiningInfo.totalSeconds} - 操作人:${manualMiningInfo.operatorName} - 原因:${manualMiningInfo.reason}`;

    const { wallet } = await this.userWalletRepo.updateBalanceWithTransaction(
      accountSequence,
      'TOKEN_STORAGE',
      'SHARE',
      amount,
      {
        transactionType: 'MANUAL_MINING_REWARD',
        counterpartyType: 'POOL',
        counterpartyPoolType: 'SHARE_POOL_A',
        referenceId: manualMiningInfo.recordId,
        referenceType: 'MANUAL_MINING',
        memo,
        metadata: {
          recordId: manualMiningInfo.recordId,
          adoptionDate: manualMiningInfo.adoptionDate,
          effectiveDate: manualMiningInfo.effectiveDate,
          totalSeconds: manualMiningInfo.totalSeconds,
          contributionRatio: manualMiningInfo.contributionRatio,
          operatorId: manualMiningInfo.operatorId,
          operatorName: manualMiningInfo.operatorName,
          reason: manualMiningInfo.reason,
        },
      },
    );

    await this.outboxRepo.create({
      aggregateType: 'UserWallet',
      aggregateId: accountSequence,
      eventType: 'MANUAL_MINING_REWARD_RECEIVED',
      payload: {
        accountSequence,
        amount: amount.toString(),
        newBalance: wallet.balance.toString(),
        recordId: manualMiningInfo.recordId,
        adoptionDate: manualMiningInfo.adoptionDate,
        operatorName: manualMiningInfo.operatorName,
      },
    });

    this.logger.log(
      `Manual mining reward received: account=${accountSequence}, amount=${amount.toFixed(8)}, operator=${manualMiningInfo.operatorName}`,
    );

    return wallet;
  }

  /**
   * 增加绿色积分
   */
  async addGreenPoints(
    accountSequence: string,
    amount: Decimal,
    source: {
      referenceId: string;
      referenceType: string;
      memo?: string;
    },
  ): Promise<UserWallet> {
    const { wallet } = await this.userWalletRepo.updateBalanceWithTransaction(
      accountSequence,
      'GREEN_POINTS',
      'GREEN_POINT',
      amount,
      {
        transactionType: 'TRANSFER_IN',
        referenceId: source.referenceId,
        referenceType: source.referenceType,
        memo: source.memo || `绿色积分增加, 数量${amount.toFixed(8)}`,
      },
    );

    await this.outboxRepo.create({
      aggregateType: 'UserWallet',
      aggregateId: accountSequence,
      eventType: 'GREEN_POINTS_ADDED',
      payload: {
        accountSequence,
        amount: amount.toString(),
        newBalance: wallet.balance.toString(),
        referenceId: source.referenceId,
      },
    });

    return wallet;
  }

  /**
   * 用户间转账
   */
  async transferBetweenUsers(
    fromAccountSeq: string,
    toAccountSeq: string,
    walletType: UserWalletType,
    assetType: AssetType,
    amount: Decimal,
    referenceInfo?: {
      referenceId?: string;
      referenceType?: string;
      memo?: string;
    },
  ): Promise<{ fromWallet: UserWallet; toWallet: UserWallet }> {
    const result = await this.userWalletRepo.transferBetweenUsers(
      fromAccountSeq,
      toAccountSeq,
      walletType,
      assetType,
      amount,
      {
        referenceId: referenceInfo?.referenceId,
        referenceType: referenceInfo?.referenceType,
        memo: referenceInfo?.memo,
      },
    );

    await this.outboxRepo.create({
      aggregateType: 'UserWallet',
      aggregateId: fromAccountSeq,
      eventType: 'USER_TRANSFER',
      payload: {
        fromAccountSeq,
        toAccountSeq,
        walletType,
        assetType,
        amount: amount.toString(),
        referenceId: referenceInfo?.referenceId,
      },
    });

    return {
      fromWallet: result.fromWallet,
      toWallet: result.toWallet,
    };
  }

  /**
   * 冻结余额（准备提现或交易）
   */
  async freezeBalance(
    accountSequence: string,
    walletType: UserWalletType,
    assetType: AssetType,
    amount: Decimal,
    referenceInfo: {
      referenceId: string;
      referenceType: string;
      memo?: string;
    },
  ): Promise<UserWallet> {
    const { wallet } = await this.userWalletRepo.freezeBalance(
      accountSequence,
      walletType,
      assetType,
      amount,
      true,
      {
        referenceId: referenceInfo.referenceId,
        referenceType: referenceInfo.referenceType,
        memo: referenceInfo.memo || `冻结${amount.toFixed(8)}用于${referenceInfo.referenceType}`,
      },
    );

    return wallet;
  }

  /**
   * 解冻余额
   */
  async unfreezeBalance(
    accountSequence: string,
    walletType: UserWalletType,
    assetType: AssetType,
    amount: Decimal,
    referenceInfo: {
      referenceId: string;
      referenceType: string;
      memo?: string;
    },
  ): Promise<UserWallet> {
    const { wallet } = await this.userWalletRepo.freezeBalance(
      accountSequence,
      walletType,
      assetType,
      amount,
      false,
      {
        referenceId: referenceInfo.referenceId,
        referenceType: referenceInfo.referenceType,
        memo: referenceInfo.memo || `解冻${amount.toFixed(8)}, ${referenceInfo.referenceType}取消`,
      },
    );

    return wallet;
  }

  /**
   * 扣减余额（如提现成功、交易扣款）
   */
  async deductBalance(
    accountSequence: string,
    walletType: UserWalletType,
    assetType: AssetType,
    amount: Decimal,
    transactionInput: {
      transactionType: TransactionType;
      counterpartyType?: 'USER' | 'SYSTEM_ACCOUNT' | 'POOL' | 'EXTERNAL';
      counterpartyAccountSeq?: string;
      counterpartyUserId?: string;
      counterpartyAddress?: string;
      referenceId?: string;
      referenceType?: string;
      txHash?: string;
      memo?: string;
    },
  ): Promise<UserWallet> {
    const { wallet } = await this.userWalletRepo.updateBalanceWithTransaction(
      accountSequence,
      walletType,
      assetType,
      amount.negated(),
      transactionInput,
    );

    return wallet;
  }

  /**
   * 获取用户交易记录
   */
  async getTransactions(
    accountSequence: string,
    options?: {
      walletType?: UserWalletType;
      transactionType?: TransactionType;
      assetType?: AssetType;
      startDate?: Date;
      endDate?: Date;
      limit?: number;
      offset?: number;
    },
  ) {
    return this.userWalletRepo.getTransactions(accountSequence, options);
  }
}
