import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TradingAccountRepository } from '../../infrastructure/persistence/repositories/trading-account.repository';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import { TradingAccountAggregate } from '../../domain/aggregates/trading-account.aggregate';
import { Money } from '../../domain/value-objects/money.vo';

@Injectable()
export class TransferService {
  private readonly logger = new Logger(TransferService.name);
  private readonly minTransferAmount: number;
  private readonly miningServiceUrl: string;

  constructor(
    private readonly accountRepository: TradingAccountRepository,
    private readonly prisma: PrismaService,
    private readonly configService: ConfigService,
  ) {
    this.minTransferAmount = this.configService.get<number>('MIN_TRANSFER_AMOUNT', 0.01);
    this.miningServiceUrl = this.configService.get<string>('MINING_SERVICE_URL', 'http://localhost:3021');
  }

  /**
   * 从挖矿账户划入积分股到交易账户
   */
  async transferIn(accountSequence: string, amount: string): Promise<{ transferNo: string }> {
    const transferAmount = new Money(amount);

    if (transferAmount.value.lessThan(this.minTransferAmount)) {
      throw new BadRequestException(`最低划转数量为 ${this.minTransferAmount}`);
    }

    const transferNo = this.generateTransferNo();

    // 创建划转记录
    await this.prisma.transferRecord.create({
      data: {
        transferNo,
        accountSequence,
        direction: 'IN',
        amount: transferAmount.value,
        status: 'PENDING',
      },
    });

    try {
      // 调用挖矿服务冻结/扣减用户余额
      const response = await this.callMiningServiceTransferOut(accountSequence, amount, transferNo);

      if (!response.success) {
        throw new BadRequestException(response.message || '挖矿服务划转失败');
      }

      // 增加交易账户余额
      let account = await this.accountRepository.findByAccountSequence(accountSequence);
      if (!account) {
        account = TradingAccountAggregate.create(accountSequence);
      }

      account.transferSharesIn(transferAmount, transferNo);
      await this.accountRepository.save(account);

      // 更新划转记录
      await this.prisma.transferRecord.update({
        where: { transferNo },
        data: {
          status: 'COMPLETED',
          miningTxId: response.txId,
          completedAt: new Date(),
        },
      });

      this.logger.log(`Transfer in completed: ${transferNo}, amount=${amount}`);

      return { transferNo };
    } catch (error: any) {
      // 更新划转记录为失败
      await this.prisma.transferRecord.update({
        where: { transferNo },
        data: {
          status: 'FAILED',
          errorMessage: error.message,
        },
      });

      throw error;
    }
  }

  /**
   * 从交易账户划出积分股到挖矿账户
   */
  async transferOut(accountSequence: string, amount: string): Promise<{ transferNo: string }> {
    const transferAmount = new Money(amount);

    if (transferAmount.value.lessThan(this.minTransferAmount)) {
      throw new BadRequestException(`最低划转数量为 ${this.minTransferAmount}`);
    }

    const account = await this.accountRepository.findByAccountSequence(accountSequence);
    if (!account) {
      throw new BadRequestException('交易账户不存在');
    }

    if (account.availableShares.isLessThan(transferAmount)) {
      throw new BadRequestException('可用积分股不足（部分积分股可能已被 C2C 订单冻结）');
    }

    const transferNo = this.generateTransferNo();

    // 创建划转记录
    await this.prisma.transferRecord.create({
      data: {
        transferNo,
        accountSequence,
        direction: 'OUT',
        amount: transferAmount.value,
        status: 'PENDING',
      },
    });

    try {
      // 先扣减交易账户余额
      account.transferSharesOut(transferAmount, transferNo);
      await this.accountRepository.save(account);

      // 调用挖矿服务增加用户余额
      const response = await this.callMiningServiceTransferIn(accountSequence, amount, transferNo);

      if (!response.success) {
        // 回滚交易账户余额
        account.transferSharesIn(transferAmount, `${transferNo}_rollback`);
        await this.accountRepository.save(account);
        throw new BadRequestException(response.message || '挖矿服务划转失败');
      }

      // 更新划转记录
      await this.prisma.transferRecord.update({
        where: { transferNo },
        data: {
          status: 'COMPLETED',
          miningTxId: response.txId,
          completedAt: new Date(),
        },
      });

      this.logger.log(`Transfer out completed: ${transferNo}, amount=${amount}`);

      return { transferNo };
    } catch (error: any) {
      await this.prisma.transferRecord.update({
        where: { transferNo },
        data: {
          status: 'FAILED',
          errorMessage: error.message,
        },
      });

      throw error;
    }
  }

  async getTransferHistory(
    accountSequence: string,
    page: number = 1,
    pageSize: number = 50,
  ): Promise<{ data: any[]; total: number }> {
    const [records, total] = await Promise.all([
      this.prisma.transferRecord.findMany({
        where: { accountSequence },
        orderBy: { createdAt: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      this.prisma.transferRecord.count({ where: { accountSequence } }),
    ]);

    return { data: records, total };
  }

  private async callMiningServiceTransferOut(
    accountSequence: string,
    amount: string,
    transferNo: string,
  ): Promise<{ success: boolean; txId?: string; message?: string }> {
    try {
      const response = await fetch(`${this.miningServiceUrl}/api/v2/mining/accounts/${accountSequence}/transfer-out`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ amount, transferNo }),
      });

      const result = await response.json();
      return { success: response.ok, txId: result.data?.txId, message: result.error?.message };
    } catch (error: any) {
      return { success: false, message: error.message };
    }
  }

  private async callMiningServiceTransferIn(
    accountSequence: string,
    amount: string,
    transferNo: string,
  ): Promise<{ success: boolean; txId?: string; message?: string }> {
    try {
      const response = await fetch(`${this.miningServiceUrl}/api/v2/mining/accounts/${accountSequence}/transfer-in`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ amount, transferNo }),
      });

      const result = await response.json();
      return { success: response.ok, txId: result.data?.txId, message: result.error?.message };
    } catch (error: any) {
      return { success: false, message: error.message };
    }
  }

  private generateTransferNo(): string {
    const timestamp = Date.now().toString(36);
    const random = Math.random().toString(36).substring(2, 8);
    return `TR${timestamp}${random}`.toUpperCase();
  }
}
