import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { MiningAccountAggregate, MiningTransactionType } from '../../../domain/aggregates/mining-account.aggregate';
import { ShareAmount } from '../../../domain/value-objects/share-amount.vo';
import { TransactionClient } from '../unit-of-work/unit-of-work';

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

  async findByAccountSequence(accountSequence: string): Promise<MiningAccountAggregate | null> {
    const record = await this.prisma.miningAccount.findUnique({
      where: { accountSequence },
    });

    if (!record) {
      return null;
    }

    return this.toDomain(record);
  }

  async findManyByAccountSequences(accountSequences: string[]): Promise<Map<string, MiningAccountAggregate>> {
    const records = await this.prisma.miningAccount.findMany({
      where: { accountSequence: { in: accountSequences } },
    });

    const result = new Map<string, MiningAccountAggregate>();
    for (const record of records) {
      result.set(record.accountSequence, this.toDomain(record));
    }

    return result;
  }

  /**
   * 保存账户（带外部事务支持）
   * @param aggregate 账户聚合
   * @param tx 可选的外部事务客户端，如果不传则自动创建事务
   * @param options.skipContributionUpdate 为 true 时不写 totalContribution/lastSyncedAt，
   *   用于挖矿分配路径避免覆盖贡献值同步的更新（解决并发 Lost Update 问题）
   */
  async save(
    aggregate: MiningAccountAggregate,
    tx?: TransactionClient,
    options?: { skipContributionUpdate?: boolean },
  ): Promise<void> {
    const snapshot = aggregate.toSnapshot();
    const transactions = aggregate.pendingTransactions;

    const executeInTx = async (client: TransactionClient) => {
      // 构建 update 数据：挖矿分配路径不写 totalContribution/lastSyncedAt
      const updateData: Record<string, any> = {
        totalMined: snapshot.totalMined.value,
        availableBalance: snapshot.availableBalance.value,
        frozenBalance: snapshot.frozenBalance.value,
      };
      if (!options?.skipContributionUpdate) {
        updateData.totalContribution = snapshot.totalContribution.value;
        updateData.lastSyncedAt = snapshot.lastSyncedAt;
      }

      // 保存账户
      await client.miningAccount.upsert({
        where: { accountSequence: snapshot.accountSequence },
        create: {
          accountSequence: snapshot.accountSequence,
          totalMined: snapshot.totalMined.value,
          availableBalance: snapshot.availableBalance.value,
          frozenBalance: snapshot.frozenBalance.value,
          totalContribution: snapshot.totalContribution.value,
          lastSyncedAt: snapshot.lastSyncedAt,
        },
        update: updateData,
      });

      // 保存交易流水
      if (transactions.length > 0) {
        await client.miningTransaction.createMany({
          data: transactions.map((t) => ({
            accountSequence: snapshot.accountSequence,
            type: t.type,
            amount: t.amount.value,
            balanceBefore: t.balanceBefore.value,
            balanceAfter: t.balanceAfter.value,
            referenceId: t.referenceId,
            referenceType: t.referenceType,
            description: t.description,
          })),
        });
      }
    };

    if (tx) {
      // 使用外部事务
      await executeInTx(tx);
    } else {
      // 自动创建事务（向后兼容）
      await this.prisma.$transaction(executeInTx);
    }

    aggregate.clearPendingTransactions();
  }

  async findAllWithContribution(page: number, pageSize: number): Promise<{
    data: MiningAccountAggregate[];
    total: number;
  }> {
    const [records, total] = await Promise.all([
      this.prisma.miningAccount.findMany({
        where: { totalContribution: { gt: 0 } },
        orderBy: { totalContribution: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      this.prisma.miningAccount.count({
        where: { totalContribution: { gt: 0 } },
      }),
    ]);

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

  async getTotalContribution(): Promise<ShareAmount> {
    const result = await this.prisma.miningAccount.aggregate({
      _sum: { totalContribution: true },
    });

    return new ShareAmount(result._sum.totalContribution || 0);
  }

  async countAccountsWithContribution(): Promise<number> {
    return this.prisma.miningAccount.count({
      where: { totalContribution: { gt: 0 } },
    });
  }

  async getTopMiners(limit: number): Promise<MiningAccountAggregate[]> {
    const records = await this.prisma.miningAccount.findMany({
      where: { totalMined: { gt: 0 } },
      orderBy: { totalMined: 'desc' },
      take: limit,
    });

    return records.map((r) => this.toDomain(r));
  }

  private toDomain(record: any): MiningAccountAggregate {
    return MiningAccountAggregate.reconstitute({
      id: record.id,
      accountSequence: record.accountSequence,
      totalMined: new ShareAmount(record.totalMined),
      availableBalance: new ShareAmount(record.availableBalance),
      frozenBalance: new ShareAmount(record.frozenBalance),
      totalContribution: new ShareAmount(record.totalContribution),
      lastSyncedAt: record.lastSyncedAt,
    });
  }
}
