import { Injectable } from '@nestjs/common';
import { ContributionAmount } from '../../../domain/value-objects/contribution-amount.vo';
import { UnitOfWork, TransactionClient } from '../unit-of-work/unit-of-work';

export interface UnallocatedContribution {
  id: bigint;
  unallocType: string;
  wouldBeAccountSequence: string | null;
  levelDepth: number | null;
  bonusTier: number | null;
  amount: ContributionAmount;
  reason: string | null;
  sourceAdoptionId: bigint;
  sourceAccountSequence: string;
  effectiveDate: Date;
  expireDate: Date;
  status: string;
  allocatedAt: Date | null;
  allocatedToAccountSequence: string | null;
  createdAt: Date;
}

@Injectable()
export class UnallocatedContributionRepository {
  constructor(private readonly unitOfWork: UnitOfWork) {}

  private get client(): TransactionClient {
    return this.unitOfWork.getClient();
  }

  async save(contribution: {
    type: string;
    wouldBeAccountSequence: string | null;
    levelDepth: number | null;
    amount: ContributionAmount;
    reason: string;
    sourceAdoptionId: bigint;
    sourceAccountSequence: string;
    effectiveDate: Date;
    expireDate: Date;
  }): Promise<void> {
    await this.client.unallocatedContribution.create({
      data: {
        unallocType: contribution.type,
        wouldBeAccountSequence: contribution.wouldBeAccountSequence,
        levelDepth: contribution.levelDepth,
        amount: contribution.amount.value,
        reason: contribution.reason,
        sourceAdoptionId: contribution.sourceAdoptionId,
        sourceAccountSequence: contribution.sourceAccountSequence,
        effectiveDate: contribution.effectiveDate,
        expireDate: contribution.expireDate,
      },
    });
  }

  async saveMany(contributions: {
    type: string;
    wouldBeAccountSequence: string | null;
    levelDepth: number | null;
    amount: ContributionAmount;
    reason: string;
    sourceAdoptionId: bigint;
    sourceAccountSequence: string;
    effectiveDate: Date;
    expireDate: Date;
  }[]): Promise<void> {
    if (contributions.length === 0) return;

    await this.client.unallocatedContribution.createMany({
      data: contributions.map((c) => ({
        unallocType: c.type,
        wouldBeAccountSequence: c.wouldBeAccountSequence,
        levelDepth: c.levelDepth,
        amount: c.amount.value,
        reason: c.reason,
        sourceAdoptionId: c.sourceAdoptionId,
        sourceAccountSequence: c.sourceAccountSequence,
        effectiveDate: c.effectiveDate,
        expireDate: c.expireDate,
      })),
    });
  }

  async findBySourceAdoptionId(sourceAdoptionId: bigint): Promise<UnallocatedContribution[]> {
    const records = await this.client.unallocatedContribution.findMany({
      where: { sourceAdoptionId },
      orderBy: { createdAt: 'asc' },
    });

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

  async getTotalUnallocated(): Promise<ContributionAmount> {
    const result = await this.client.unallocatedContribution.aggregate({
      _sum: { amount: true },
    });

    return new ContributionAmount(result._sum.amount || 0);
  }

  async getTotalUnallocatedByType(): Promise<Map<string, ContributionAmount>> {
    const result = await this.client.unallocatedContribution.groupBy({
      by: ['unallocType'],
      _sum: { amount: true },
    });

    const map = new Map<string, ContributionAmount>();
    for (const item of result) {
      map.set(item.unallocType, new ContributionAmount(item._sum.amount || 0));
    }

    return map;
  }

  async findWithPagination(page: number, pageSize: number): Promise<{
    data: UnallocatedContribution[];
    total: number;
  }> {
    const [records, total] = await Promise.all([
      this.client.unallocatedContribution.findMany({
        skip: (page - 1) * pageSize,
        take: pageSize,
        orderBy: { createdAt: 'desc' },
      }),
      this.client.unallocatedContribution.count(),
    ]);

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

  /**
   * 查询用户待领取的奖励档位贡献值
   * @param accountSequence 用户账号
   * @param bonusTier 奖励档位 (2 或 3)
   */
  async findPendingBonusByAccountSequence(
    accountSequence: string,
    bonusTier: number,
  ): Promise<UnallocatedContribution[]> {
    const records = await this.client.unallocatedContribution.findMany({
      where: {
        wouldBeAccountSequence: accountSequence,
        unallocType: `BONUS_TIER_${bonusTier}`,
        status: 'PENDING',
      },
      orderBy: { createdAt: 'asc' },
    });

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

  /**
   * 领取奖励档位 - 将待领取记录标记为已分配给用户
   * @param ids 记录ID列表
   * @param accountSequence 分配给的用户账号
   */
  async claimBonusRecords(ids: bigint[], accountSequence: string): Promise<void> {
    if (ids.length === 0) return;

    await this.client.unallocatedContribution.updateMany({
      where: {
        id: { in: ids },
        status: 'PENDING',
      },
      data: {
        status: 'ALLOCATED_TO_USER',
        allocatedAt: new Date(),
        allocatedToAccountSequence: accountSequence,
      },
    });
  }

  /**
   * 查询用户所有待领取的奖励（所有档位）
   */
  async findAllPendingBonusByAccountSequence(
    accountSequence: string,
  ): Promise<UnallocatedContribution[]> {
    const records = await this.client.unallocatedContribution.findMany({
      where: {
        wouldBeAccountSequence: accountSequence,
        unallocType: { startsWith: 'BONUS_TIER_' },
        status: 'PENDING',
      },
      orderBy: { createdAt: 'asc' },
    });

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

  /**
   * 查询用户待领取的层级贡献值（按层级范围）
   * @param accountSequence 用户账号
   * @param minLevel 最小层级（包含）
   * @param maxLevel 最大层级（包含）
   */
  async findPendingLevelByAccountSequence(
    accountSequence: string,
    minLevel: number,
    maxLevel: number,
  ): Promise<UnallocatedContribution[]> {
    const records = await this.client.unallocatedContribution.findMany({
      where: {
        wouldBeAccountSequence: accountSequence,
        unallocType: 'LEVEL_OVERFLOW',
        levelDepth: {
          gte: minLevel,
          lte: maxLevel,
        },
        status: 'PENDING',
      },
      orderBy: { levelDepth: 'asc' },
    });

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

  /**
   * 领取层级贡献值 - 将待领取记录标记为已分配给用户
   * @param ids 记录ID列表
   * @param accountSequence 分配给的用户账号
   */
  async claimLevelRecords(ids: bigint[], accountSequence: string): Promise<void> {
    if (ids.length === 0) return;

    await this.client.unallocatedContribution.updateMany({
      where: {
        id: { in: ids },
        status: 'PENDING',
      },
      data: {
        status: 'ALLOCATED_TO_USER',
        allocatedAt: new Date(),
        allocatedToAccountSequence: accountSequence,
      },
    });
  }

  /**
   * 获取分层级的未分配算力统计
   */
  async getUnallocatedByLevelTier(): Promise<{
    tier1: string; // 1-5级未分配
    tier2: string; // 6-10级未分配
    tier3: string; // 11-15级未分配
  }> {
    const results = await this.client.unallocatedContribution.groupBy({
      by: ['levelDepth'],
      where: {
        levelDepth: { not: null },
        status: 'PENDING',
      },
      _sum: { amount: true },
    });

    let tier1 = new ContributionAmount(0);
    let tier2 = new ContributionAmount(0);
    let tier3 = new ContributionAmount(0);

    for (const item of results) {
      const depth = item.levelDepth!;
      const amount = new ContributionAmount(item._sum.amount || 0);
      if (depth >= 1 && depth <= 5) {
        tier1 = tier1.add(amount);
      } else if (depth >= 6 && depth <= 10) {
        tier2 = tier2.add(amount);
      } else if (depth >= 11 && depth <= 15) {
        tier3 = tier3.add(amount);
      }
    }

    return {
      tier1: tier1.value.toString(),
      tier2: tier2.value.toString(),
      tier3: tier3.value.toString(),
    };
  }

  /**
   * 查询有待回收 LEVEL_OVERFLOW 记录的用户账号列表
   * 用于定时任务扫描：当用户已解锁到足够层级，但之前的 overflow 尚未回收时
   */
  async findAccountSequencesWithPendingLevelOverflow(limit: number): Promise<string[]> {
    const records = await this.client.unallocatedContribution.findMany({
      where: {
        unallocType: 'LEVEL_OVERFLOW',
        status: 'PENDING',
        wouldBeAccountSequence: { not: null },
      },
      select: { wouldBeAccountSequence: true },
      distinct: ['wouldBeAccountSequence'],
      take: limit,
    });

    return records.map((r) => r.wouldBeAccountSequence!);
  }

  /**
   * 获取分档位的未分配奖励统计
   */
  async getUnallocatedByBonusTier(): Promise<{
    tier1: string;
    tier2: string;
    tier3: string;
  }> {
    const results = await this.client.unallocatedContribution.groupBy({
      by: ['unallocType'],
      where: {
        unallocType: { startsWith: 'BONUS_TIER_' },
        status: 'PENDING',
      },
      _sum: { amount: true },
    });

    let tier1 = '0';
    let tier2 = '0';
    let tier3 = '0';

    for (const item of results) {
      const amount = (item._sum.amount || 0).toString();
      if (item.unallocType === 'BONUS_TIER_1') {
        tier1 = amount;
      } else if (item.unallocType === 'BONUS_TIER_2') {
        tier2 = amount;
      } else if (item.unallocType === 'BONUS_TIER_3') {
        tier3 = amount;
      }
    }

    return { tier1, tier2, tier3 };
  }

  private toDomain(record: any): UnallocatedContribution {
    return {
      id: record.id,
      unallocType: record.unallocType,
      wouldBeAccountSequence: record.wouldBeAccountSequence,
      levelDepth: record.levelDepth,
      bonusTier: record.bonusTier,
      amount: new ContributionAmount(record.amount),
      reason: record.reason,
      sourceAdoptionId: record.sourceAdoptionId,
      sourceAccountSequence: record.sourceAccountSequence,
      effectiveDate: record.effectiveDate,
      expireDate: record.expireDate,
      status: record.status,
      allocatedAt: record.allocatedAt,
      allocatedToAccountSequence: record.allocatedToAccountSequence,
      createdAt: record.createdAt,
    };
  }
}
