import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { Money } from '../../../domain/value-objects/money.vo';
import Decimal from 'decimal.js';

export interface SharePoolEntity {
  id: string;
  greenPoints: Money;
  totalInflow: Money;
  totalOutflow: Money;
  createdAt: Date;
  updatedAt: Date;
}

export type SharePoolTransactionType = 'INJECT' | 'TRADE_IN' | 'TRADE_OUT' | 'FEE_IN';

export interface SharePoolTransactionEntity {
  id: string;
  poolId: string;
  type: SharePoolTransactionType;
  amount: Money;
  balanceBefore: Money;
  balanceAfter: Money;
  referenceId: string | null;
  referenceType: string | null;
  memo: string | null;
  createdAt: Date;
}

@Injectable()
export class SharePoolRepository {
  constructor(private readonly prisma: PrismaService) {}

  async getPool(): Promise<SharePoolEntity | null> {
    const record = await this.prisma.sharePool.findFirst();
    if (!record) {
      return null;
    }
    return this.toDomain(record);
  }

  async initializePool(initialGreenPoints: Money = Money.zero()): Promise<SharePoolEntity> {
    const existing = await this.prisma.sharePool.findFirst();
    if (existing) {
      return this.toDomain(existing);
    }

    const record = await this.prisma.sharePool.create({
      data: {
        greenPoints: initialGreenPoints.value,
        totalInflow: initialGreenPoints.value,
        totalOutflow: 0,
      },
    });

    return this.toDomain(record);
  }

  /**
   * 注入绿积分
   */
  async inject(amount: Money, referenceId?: string, memo?: string): Promise<void> {
    await this.updateBalance('INJECT', amount, true, referenceId, memo);
  }

  /**
   * 交易流入（买入时绿积分进入股池）
   */
  async tradeIn(amount: Money, tradeId: string): Promise<void> {
    await this.updateBalance('TRADE_IN', amount, true, tradeId, `交易买入流入 ${amount.toFixed(8)}`);
  }

  /**
   * 交易手续费流入（交易成交时10%手续费进入积分股池）
   * @param amount 手续费金额
   * @param tradeNo 交易号
   * @param buyerSequence 买方账户序号
   * @param sellerSequence 卖方账户序号
   * @param buyerSource 买方来源（USER/MARKET_MAKER/DEX_BOT/SYSTEM）
   * @param sellerSource 卖方来源（USER/MARKET_MAKER/DEX_BOT/SYSTEM）
   */
  async feeIn(
    amount: Money,
    tradeNo: string,
    buyerSequence: string,
    sellerSequence: string,
    buyerSource: string,
    sellerSource: string,
  ): Promise<void> {
    const buyerLabel = buyerSource === 'MARKET_MAKER' ? '做市商' : buyerSource === 'USER' ? '用户' : buyerSource;
    const sellerLabel = sellerSource === 'MARKET_MAKER' ? '做市商' : sellerSource === 'USER' ? '用户' : sellerSource;
    await this.updateBalance(
      'FEE_IN',
      amount,
      true,
      tradeNo,
      `交易手续费(10%) ${amount.toFixed(8)}, 买方: ${buyerSequence}(${buyerLabel}), 卖方: ${sellerSequence}(${sellerLabel}), 交易号: ${tradeNo}`,
    );
  }

  /**
   * 交易流出（卖出时绿积分从股池流出）
   */
  async tradeOut(amount: Money, tradeId: string): Promise<void> {
    await this.updateBalance(
      'TRADE_OUT',
      amount,
      false,
      tradeId,
      `交易卖出流出 ${amount.toFixed(8)}`,
    );
  }

  private async updateBalance(
    type: SharePoolTransactionType,
    amount: Money,
    isInflow: boolean,
    referenceId?: string,
    memo?: string,
  ): Promise<void> {
    const pool = await this.prisma.sharePool.findFirst();
    if (!pool) {
      throw new Error('Share pool not initialized');
    }

    const balanceBefore = new Decimal(pool.greenPoints.toString());
    const balanceAfter = isInflow
      ? balanceBefore.plus(amount.value)
      : balanceBefore.minus(amount.value);

    if (balanceAfter.isNegative()) {
      throw new Error('Insufficient green points in share pool');
    }

    const newTotalInflow = isInflow
      ? new Decimal(pool.totalInflow.toString()).plus(amount.value)
      : pool.totalInflow;
    const newTotalOutflow = !isInflow
      ? new Decimal(pool.totalOutflow.toString()).plus(amount.value)
      : pool.totalOutflow;

    await this.prisma.$transaction([
      this.prisma.sharePool.update({
        where: { id: pool.id },
        data: {
          greenPoints: balanceAfter,
          totalInflow: newTotalInflow,
          totalOutflow: newTotalOutflow,
        },
      }),
      this.prisma.sharePoolTransaction.create({
        data: {
          poolId: pool.id,
          type,
          amount: amount.value,
          balanceBefore,
          balanceAfter,
          referenceId,
          referenceType: type === 'INJECT' ? 'INJECT' : 'TRADE',
          memo,
        },
      }),
    ]);
  }

  async getTransactions(
    page: number,
    pageSize: number,
  ): Promise<{
    data: SharePoolTransactionEntity[];
    total: number;
  }> {
    const pool = await this.prisma.sharePool.findFirst();
    if (!pool) {
      return { data: [], total: 0 };
    }

    const [records, total] = await Promise.all([
      this.prisma.sharePoolTransaction.findMany({
        where: { poolId: pool.id },
        orderBy: { createdAt: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      this.prisma.sharePoolTransaction.count({ where: { poolId: pool.id } }),
    ]);

    return {
      data: records.map((r) => this.toTransactionDomain(r)),
      total,
    };
  }

  private toDomain(record: any): SharePoolEntity {
    return {
      id: record.id,
      greenPoints: new Money(record.greenPoints),
      totalInflow: new Money(record.totalInflow),
      totalOutflow: new Money(record.totalOutflow),
      createdAt: record.createdAt,
      updatedAt: record.updatedAt,
    };
  }

  private toTransactionDomain(record: any): SharePoolTransactionEntity {
    return {
      id: record.id,
      poolId: record.poolId,
      type: record.type as SharePoolTransactionType,
      amount: new Money(record.amount),
      balanceBefore: new Money(record.balanceBefore),
      balanceAfter: new Money(record.balanceAfter),
      referenceId: record.referenceId,
      referenceType: record.referenceType,
      memo: record.memo,
      createdAt: record.createdAt,
    };
  }
}
