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 CirculationPoolEntity {
  id: string;
  totalShares: Money;
  totalCash: Money;
  totalInflow: Money;
  totalOutflow: Money;
  createdAt: Date;
  updatedAt: Date;
}

export type CirculationPoolTransactionType =
  | 'SHARE_IN'
  | 'SHARE_OUT'
  | 'CASH_IN'
  | 'CASH_OUT'
  | 'TRADE_BUY'
  | 'TRADE_SELL';

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

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

  async initializePool(): Promise<CirculationPoolEntity> {
    const existing = await this.prisma.circulationPool.findFirst();
    if (existing) {
      return this.toDomain(existing);
    }

    const record = await this.prisma.circulationPool.create({
      data: {
        totalShares: 0,
        totalCash: 0,
        totalInflow: 0,
        totalOutflow: 0,
      },
    });

    return this.toDomain(record);
  }

  /**
   * 卖出时积分股进入流通池
   */
  async addSharesFromSell(
    amount: Money,
    accountSeq: string,
    orderId: string,
    memo?: string,
  ): Promise<void> {
    const pool = await this.prisma.circulationPool.findFirst();
    if (!pool) {
      throw new Error('Circulation pool not initialized');
    }

    const balanceBefore = new Decimal(pool.totalShares.toString());
    const balanceAfter = balanceBefore.plus(amount.value);

    await this.prisma.$transaction([
      this.prisma.circulationPool.update({
        where: { id: pool.id },
        data: {
          totalShares: balanceAfter,
          totalInflow: new Decimal(pool.totalInflow.toString()).plus(amount.value),
        },
      }),
      this.prisma.circulationPoolTransaction.create({
        data: {
          poolId: pool.id,
          type: 'TRADE_SELL',
          assetType: 'SHARE',
          amount: amount.value,
          balanceBefore,
          balanceAfter,
          counterpartyType: 'USER',
          counterpartyAccountSeq: accountSeq,
          referenceId: orderId,
          referenceType: 'ORDER',
          memo: memo || `卖出积分股进入流通池 ${amount.toFixed(8)}`,
        },
      }),
    ]);
  }

  /**
   * 买入时积分股从流通池流出
   */
  async removeSharesForBuy(
    amount: Money,
    accountSeq: string,
    orderId: string,
    memo?: string,
  ): Promise<void> {
    const pool = await this.prisma.circulationPool.findFirst();
    if (!pool) {
      throw new Error('Circulation pool not initialized');
    }

    const balanceBefore = new Decimal(pool.totalShares.toString());
    const balanceAfter = balanceBefore.minus(amount.value);

    if (balanceAfter.isNegative()) {
      throw new Error('Insufficient shares in circulation pool');
    }

    await this.prisma.$transaction([
      this.prisma.circulationPool.update({
        where: { id: pool.id },
        data: {
          totalShares: balanceAfter,
          totalOutflow: new Decimal(pool.totalOutflow.toString()).plus(amount.value),
        },
      }),
      this.prisma.circulationPoolTransaction.create({
        data: {
          poolId: pool.id,
          type: 'TRADE_BUY',
          assetType: 'SHARE',
          amount: amount.value,
          balanceBefore,
          balanceAfter,
          counterpartyType: 'USER',
          counterpartyAccountSeq: accountSeq,
          referenceId: orderId,
          referenceType: 'ORDER',
          memo: memo || `买入积分股从流通池流出 ${amount.toFixed(8)}`,
        },
      }),
    ]);
  }

  /**
   * 获取流通池积分股数量
   */
  async getSharesAmount(): Promise<Money> {
    const pool = await this.getPool();
    if (!pool) {
      return Money.zero();
    }
    return pool.totalShares;
  }

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

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

    return {
      data: records.map((r) => ({
        ...r,
        amount: r.amount.toString(),
        balanceBefore: r.balanceBefore.toString(),
        balanceAfter: r.balanceAfter.toString(),
      })),
      total,
    };
  }

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