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';

@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;
  }

  async save(aggregate: MiningAccountAggregate): Promise<void> {
    const snapshot = aggregate.toSnapshot();
    const transactions = aggregate.pendingTransactions;

    await this.prisma.$transaction(async (tx) => {
      // 保存账户
      await tx.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: {
          totalMined: snapshot.totalMined.value,
          availableBalance: snapshot.availableBalance.value,
          frozenBalance: snapshot.frozenBalance.value,
          totalContribution: snapshot.totalContribution.value,
          lastSyncedAt: snapshot.lastSyncedAt,
        },
      });

      // 保存交易流水
      if (transactions.length > 0) {
        await tx.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,
          })),
        });
      }
    });

    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,
    });
  }
}
