import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { ShareAmount } from '../../../domain/value-objects/share-amount.vo';
import { SystemAccountType } from '@prisma/client';
import { TransactionClient } from '../unit-of-work/unit-of-work';

export interface SystemMiningAccountSnapshot {
  accountType: SystemAccountType;
  name: string;
  totalMined: ShareAmount;
  availableBalance: ShareAmount;
  totalContribution: ShareAmount;
  lastSyncedAt: Date | null;
}

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

  async findByType(accountType: SystemAccountType): Promise<SystemMiningAccountSnapshot | null> {
    const record = await this.prisma.systemMiningAccount.findUnique({
      where: { accountType },
    });

    if (!record) {
      return null;
    }

    return this.toSnapshot(record);
  }

  async findAll(): Promise<SystemMiningAccountSnapshot[]> {
    const records = await this.prisma.systemMiningAccount.findMany({
      orderBy: { accountType: 'asc' },
    });

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

  async ensureSystemAccountsExist(): Promise<void> {
    const accounts = [
      { accountType: SystemAccountType.OPERATION, name: '运营账户' },
      { accountType: SystemAccountType.PROVINCE, name: '省公司账户' },
      { accountType: SystemAccountType.CITY, name: '市公司账户' },
      { accountType: SystemAccountType.HEADQUARTERS, name: '总部账户' },
    ];

    for (const account of accounts) {
      await this.prisma.systemMiningAccount.upsert({
        where: { accountType: account.accountType },
        create: {
          accountType: account.accountType,
          name: account.name,
          totalMined: 0,
          availableBalance: 0,
          totalContribution: 0,
        },
        update: {},
      });
    }
  }

  async updateContribution(
    accountType: SystemAccountType,
    contribution: ShareAmount,
  ): Promise<void> {
    await this.prisma.systemMiningAccount.upsert({
      where: { accountType },
      create: {
        accountType,
        name: this.getAccountName(accountType),
        totalContribution: contribution.value,
        lastSyncedAt: new Date(),
      },
      update: {
        totalContribution: contribution.value,
        lastSyncedAt: new Date(),
      },
    });
  }

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

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

  /**
   * 执行系统账户挖矿（带外部事务支持）
   * @param accountType 账户类型
   * @param amount 挖矿数量
   * @param memo 备注
   * @param tx 可选的外部事务客户端，如果不传则自动创建事务
   */
  async mine(
    accountType: SystemAccountType,
    amount: ShareAmount,
    memo: string,
    tx?: TransactionClient,
  ): Promise<void> {
    const executeInTx = async (client: TransactionClient) => {
      const account = await client.systemMiningAccount.findUnique({
        where: { accountType },
      });

      if (!account) {
        throw new Error(`System account ${accountType} not found`);
      }

      const balanceBefore = account.availableBalance;
      const balanceAfter = balanceBefore.plus(amount.value);
      const totalMined = account.totalMined.plus(amount.value);

      await client.systemMiningAccount.update({
        where: { accountType },
        data: {
          totalMined,
          availableBalance: balanceAfter,
        },
      });

      await client.systemMiningTransaction.create({
        data: {
          accountType,
          type: 'MINE',
          amount: amount.value,
          balanceBefore,
          balanceAfter,
          memo,
        },
      });
    };

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

  async saveMinuteRecord(
    accountType: SystemAccountType,
    miningMinute: Date,
    contributionRatio: ShareAmount,
    totalContribution: ShareAmount,
    secondDistribution: ShareAmount,
    minedAmount: ShareAmount,
  ): Promise<void> {
    await this.prisma.systemMiningRecord.upsert({
      where: {
        accountType_miningMinute: {
          accountType,
          miningMinute,
        },
      },
      create: {
        accountType,
        miningMinute,
        contributionRatio: contributionRatio.value,
        totalContribution: totalContribution.value,
        secondDistribution: secondDistribution.value,
        minedAmount: minedAmount.value,
      },
      update: {
        minedAmount: { increment: minedAmount.value },
      },
    });
  }

  private getAccountName(accountType: SystemAccountType): string {
    switch (accountType) {
      case SystemAccountType.OPERATION:
        return '运营账户';
      case SystemAccountType.PROVINCE:
        return '省公司账户';
      case SystemAccountType.CITY:
        return '市公司账户';
      case SystemAccountType.HEADQUARTERS:
        return '总部账户';
      default:
        return accountType;
    }
  }

  private toSnapshot(record: any): SystemMiningAccountSnapshot {
    return {
      accountType: record.accountType,
      name: record.name,
      totalMined: new ShareAmount(record.totalMined),
      availableBalance: new ShareAmount(record.availableBalance),
      totalContribution: new ShareAmount(record.totalContribution),
      lastSyncedAt: record.lastSyncedAt,
    };
  }
}
