import { Injectable, Logger } from '@nestjs/common';
import { ContributionAccountRepository } from '../../infrastructure/persistence/repositories/contribution-account.repository';
import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository';
import { UnitOfWork } from '../../infrastructure/persistence/unit-of-work/unit-of-work';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
import { ContributionAmount } from '../../domain/value-objects/contribution-amount.vo';
import Decimal from 'decimal.js';

export interface UserContributionSnapshot {
  id: bigint;
  snapshotDate: Date;
  accountSequence: string;
  effectiveContribution: ContributionAmount;
  networkTotalContribution: ContributionAmount;
  contributionRatio: Decimal;
  createdAt: Date;
}

export interface DailySnapshotSummary {
  snapshotDate: Date;
  networkTotalContribution: ContributionAmount;
  totalAccounts: number;
  activeAccounts: number;
  createdAt: Date;
}

/**
 * 每日快照服务
 * 负责生成每日算力快照，供 mining-service 使用
 */
@Injectable()
export class SnapshotService {
  private readonly logger = new Logger(SnapshotService.name);

  constructor(
    private readonly contributionAccountRepository: ContributionAccountRepository,
    private readonly outboxRepository: OutboxRepository,
    private readonly unitOfWork: UnitOfWork,
    private readonly prisma: PrismaService,
  ) {}

  /**
   * 创建每日算力快照
   * 为所有有效算力大于0的账户创建快照记录
   */
  async createDailySnapshot(snapshotDate: Date): Promise<DailySnapshotSummary> {
    const dateStr = this.formatDate(snapshotDate);
    const snapshotDateOnly = new Date(dateStr);

    // 检查是否已存在快照
    const existingCount = await this.prisma.dailyContributionSnapshot.count({
      where: { snapshotDate: snapshotDateOnly },
    });

    if (existingCount > 0) {
      this.logger.warn(`Snapshot for ${dateStr} already exists with ${existingCount} records`);
      const existingSummary = await this.getSnapshotSummary(snapshotDate);
      if (!existingSummary) {
        throw new Error(`Snapshot exists but summary could not be retrieved for ${dateStr}`);
      }
      return existingSummary;
    }

    // 获取全网有效算力总和
    const aggregation = await this.prisma.contributionAccount.aggregate({
      _sum: { effectiveContribution: true },
      _count: { id: true },
    });

    const networkTotalContribution = aggregation._sum.effectiveContribution || new Decimal(0);
    const totalAccounts = aggregation._count.id;

    // 获取有效算力大于0的账户
    const activeAccounts = await this.prisma.contributionAccount.findMany({
      where: { effectiveContribution: { gt: 0 } },
      select: {
        accountSequence: true,
        effectiveContribution: true,
      },
    });

    await this.unitOfWork.executeInTransaction(async () => {
      // 批量创建每个账户的快照记录
      if (activeAccounts.length > 0) {
        await this.prisma.dailyContributionSnapshot.createMany({
          data: activeAccounts.map((account) => ({
            snapshotDate: snapshotDateOnly,
            accountSequence: account.accountSequence,
            effectiveContribution: account.effectiveContribution,
            networkTotalContribution: networkTotalContribution,
            contributionRatio: networkTotalContribution.isZero()
              ? new Decimal(0)
              : new Decimal(account.effectiveContribution).dividedBy(networkTotalContribution),
          })),
        });
      }

      // 发布快照创建事件
      await this.outboxRepository.save({
        aggregateType: 'DailySnapshot',
        aggregateId: dateStr,
        eventType: 'DailySnapshotCreated',
        payload: {
          snapshotId: dateStr,
          snapshotDate: dateStr,
          totalContribution: networkTotalContribution.toString(),
          networkTotalContribution: networkTotalContribution.toString(),
          activeAccounts: activeAccounts.length,
          accountCount: activeAccounts.length,
          totalAccounts,
          createdAt: new Date().toISOString(),
        },
      });
    });

    this.logger.log(
      `Daily snapshot created for ${dateStr}: networkTotal=${networkTotalContribution}, activeAccounts=${activeAccounts.length}`,
    );

    return {
      snapshotDate: snapshotDateOnly,
      networkTotalContribution: new ContributionAmount(networkTotalContribution),
      totalAccounts,
      activeAccounts: activeAccounts.length,
      createdAt: new Date(),
    };
  }

  /**
   * 获取指定日期的快照汇总
   */
  async getSnapshotSummary(snapshotDate: Date): Promise<DailySnapshotSummary | null> {
    const dateStr = this.formatDate(snapshotDate);
    const snapshotDateOnly = new Date(dateStr);

    const firstRecord = await this.prisma.dailyContributionSnapshot.findFirst({
      where: { snapshotDate: snapshotDateOnly },
      orderBy: { id: 'asc' },
    });

    if (!firstRecord) {
      return null;
    }

    const activeCount = await this.prisma.dailyContributionSnapshot.count({
      where: { snapshotDate: snapshotDateOnly },
    });

    const totalAccounts = await this.prisma.contributionAccount.count();

    return {
      snapshotDate: snapshotDateOnly,
      networkTotalContribution: new ContributionAmount(firstRecord.networkTotalContribution),
      totalAccounts,
      activeAccounts: activeCount,
      createdAt: firstRecord.createdAt,
    };
  }

  /**
   * 获取最新的快照日期
   */
  async getLatestSnapshotDate(): Promise<Date | null> {
    const snapshot = await this.prisma.dailyContributionSnapshot.findFirst({
      orderBy: { snapshotDate: 'desc' },
      select: { snapshotDate: true },
    });

    return snapshot?.snapshotDate ?? null;
  }

  /**
   * 获取用户在指定日期的算力快照
   */
  async getUserSnapshot(
    accountSequence: string,
    snapshotDate: Date,
  ): Promise<UserContributionSnapshot | null> {
    const dateStr = this.formatDate(snapshotDate);
    const snapshotDateOnly = new Date(dateStr);

    const snapshot = await this.prisma.dailyContributionSnapshot.findUnique({
      where: {
        snapshotDate_accountSequence: {
          snapshotDate: snapshotDateOnly,
          accountSequence,
        },
      },
    });

    if (!snapshot) {
      return null;
    }

    return this.toUserSnapshot(snapshot);
  }

  /**
   * 获取用户在指定日期的算力占比
   */
  async getUserContributionRatio(
    accountSequence: string,
    snapshotDate: Date,
  ): Promise<{ contribution: ContributionAmount; ratio: number } | null> {
    const snapshot = await this.getUserSnapshot(accountSequence, snapshotDate);
    if (!snapshot) {
      return null;
    }

    return {
      contribution: snapshot.effectiveContribution,
      ratio: snapshot.contributionRatio.toNumber(),
    };
  }

  /**
   * 批量获取用户算力占比
   */
  async batchGetUserContributionRatios(
    snapshotDate: Date,
    page: number = 1,
    pageSize: number = 1000,
  ): Promise<{
    data: Array<{ accountSequence: string; contribution: string; ratio: number }>;
    total: number;
    totalContribution: string;
  }> {
    const dateStr = this.formatDate(snapshotDate);
    const snapshotDateOnly = new Date(dateStr);

    const [snapshots, total] = await Promise.all([
      this.prisma.dailyContributionSnapshot.findMany({
        where: { snapshotDate: snapshotDateOnly },
        skip: (page - 1) * pageSize,
        take: pageSize,
        orderBy: { effectiveContribution: 'desc' },
      }),
      this.prisma.dailyContributionSnapshot.count({
        where: { snapshotDate: snapshotDateOnly },
      }),
    ]);

    if (snapshots.length === 0) {
      return { data: [], total: 0, totalContribution: '0' };
    }

    const networkTotal = snapshots[0].networkTotalContribution;

    const ratios = snapshots.map((snapshot) => ({
      accountSequence: snapshot.accountSequence,
      contribution: snapshot.effectiveContribution.toString(),
      ratio: new Decimal(snapshot.contributionRatio).toNumber(),
    }));

    return {
      data: ratios,
      total,
      totalContribution: networkTotal.toString(),
    };
  }

  private formatDate(date: Date): string {
    return date.toISOString().split('T')[0];
  }

  private toUserSnapshot(record: any): UserContributionSnapshot {
    return {
      id: record.id,
      snapshotDate: record.snapshotDate,
      accountSequence: record.accountSequence,
      effectiveContribution: new ContributionAmount(record.effectiveContribution),
      networkTotalContribution: new ContributionAmount(record.networkTotalContribution),
      contributionRatio: new Decimal(record.contributionRatio),
      createdAt: record.createdAt,
    };
  }
}
