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

export type SystemAccountType = 'OPERATION' | 'PROVINCE' | 'CITY' | 'HEADQUARTERS';

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

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

  async findByTypeAndRegion(
    accountType: SystemAccountType,
    regionCode: string | null,
  ): Promise<SystemMiningAccountSnapshot | null> {
    // 使用 findFirst 替代 findUnique，因为 regionCode 可以为 null
    const record = await this.prisma.systemMiningAccount.findFirst({
      where: {
        accountType,
        regionCode: regionCode === null ? { equals: null } : regionCode,
      },
    });

    if (!record) {
      return null;
    }

    return this.toSnapshot(record);
  }

  /**
   * 根据类型查找所有账户
   */
  async findByType(accountType: SystemAccountType): Promise<SystemMiningAccountSnapshot[]> {
    const records = await this.prisma.systemMiningAccount.findMany({
      where: { accountType },
      orderBy: { regionCode: 'asc' },
    });

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

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

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

  /**
   * 确保基础账户存在
   */
  async ensureSystemAccountsExist(): Promise<void> {
    const accounts: { accountType: SystemAccountType; name: string }[] = [
      { accountType: 'OPERATION', name: '运营账户' },
      { accountType: 'HEADQUARTERS', name: '总部账户' },
    ];

    for (const account of accounts) {
      // 使用 findFirst + create 替代 upsert，因为 regionCode 可以为 null
      const existing = await this.prisma.systemMiningAccount.findFirst({
        where: {
          accountType: account.accountType,
          regionCode: { equals: null },
        },
      });

      if (!existing) {
        await this.prisma.systemMiningAccount.create({
          data: {
            accountType: account.accountType,
            regionCode: null,
            name: account.name,
            totalMined: 0,
            availableBalance: 0,
            totalContribution: 0,
          },
        });
      }
    }
  }

  /**
   * 更新系统账户算力（动态创建省市账户）
   */
  async updateContribution(
    accountType: SystemAccountType,
    regionCode: string | null,
    name: string,
    contribution: ShareAmount,
  ): Promise<void> {
    // 使用 findFirst + create/update 替代 upsert，因为 regionCode 可以为 null
    const existing = await this.prisma.systemMiningAccount.findFirst({
      where: {
        accountType,
        regionCode: regionCode === null ? { equals: null } : regionCode,
      },
    });

    if (existing) {
      await this.prisma.systemMiningAccount.update({
        where: { id: existing.id },
        data: {
          totalContribution: contribution.value,
          lastSyncedAt: new Date(),
        },
      });
    } else {
      await this.prisma.systemMiningAccount.create({
        data: {
          accountType,
          regionCode,
          name,
          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);
  }

  /**
   * 执行系统账户挖矿
   */
  async mine(
    accountType: SystemAccountType,
    regionCode: string | null,
    amount: ShareAmount,
    memo: string,
    tx?: TransactionClient,
    referenceId?: string,
    referenceType?: string,
  ): Promise<void> {
    const executeInTx = async (client: TransactionClient) => {
      // 使用 findFirst 替代 findUnique，因为 regionCode 可以为 null
      const account = await client.systemMiningAccount.findFirst({
        where: {
          accountType,
          regionCode: regionCode === null ? { equals: null } : regionCode,
        },
      });

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

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

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

      await client.systemMiningTransaction.create({
        data: {
          systemAccountId: account.id,
          type: 'MINE',
          amount: amount.value,
          balanceBefore,
          balanceAfter,
          referenceId: referenceId ?? null,
          referenceType: referenceType ?? null,
          memo,
        },
      });
    };

    if (tx) {
      await executeInTx(tx);
    } else {
      await this.prisma.$transaction(executeInTx);
    }
  }

  async saveMinuteRecord(
    accountType: SystemAccountType,
    regionCode: string | null,
    miningMinute: Date,
    contributionRatio: ShareAmount,
    totalContribution: ShareAmount,
    secondDistribution: ShareAmount,
    minedAmount: ShareAmount,
  ): Promise<void> {
    // 使用 findFirst 替代 findUnique，因为 regionCode 可以为 null
    const account = await this.prisma.systemMiningAccount.findFirst({
      where: {
        accountType,
        regionCode: regionCode === null ? { equals: null } : regionCode,
      },
    });

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

    await this.prisma.systemMiningRecord.upsert({
      where: {
        systemAccountId_miningMinute: {
          systemAccountId: account.id,
          miningMinute,
        },
      },
      create: {
        systemAccountId: account.id,
        miningMinute,
        contributionRatio: contributionRatio.value,
        totalContribution: totalContribution.value,
        secondDistribution: secondDistribution.value,
        minedAmount: minedAmount.value,
      },
      update: {
        minedAmount: { increment: minedAmount.value },
      },
    });
  }

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