import { Injectable } from '@nestjs/common';
import { PrismaService } from '../database/prisma.service';
import {
  ITeamStatisticsRepository,
  TeamStatistics,
  TeamStatisticsProps,
  LeaderboardEntry,
  LeaderboardQueryOptions,
  DirectReferralStats,
} from '../../domain';

@Injectable()
export class TeamStatisticsRepository implements ITeamStatisticsRepository {
  constructor(private readonly prisma: PrismaService) {}

  async save(statistics: TeamStatistics): Promise<TeamStatistics> {
    const data = statistics.toPersistence();

    await this.prisma.$transaction(async (tx) => {
      // 保存主表
      await tx.teamStatistics.upsert({
        where: { userId: data.userId },
        update: {
          directReferralCount: data.directReferralCount,
          totalTeamCount: data.totalTeamCount,
          selfPlantingCount: data.personalPlantingCount,
          totalTeamPlantingCount: data.teamPlantingCount,
          effectivePlantingCountForRanking: data.leaderboardScore,
          maxSingleTeamPlantingCount: data.maxDirectTeamCount,
          provinceCityDistribution: data.provinceCityDistribution ?? {},
          lastCalcAt: data.lastCalculatedAt,
          updatedAt: new Date(),
        },
        create: {
          userId: data.userId,
          directReferralCount: data.directReferralCount,
          totalTeamCount: data.totalTeamCount,
          selfPlantingCount: data.personalPlantingCount,
          totalTeamPlantingCount: data.teamPlantingCount,
          effectivePlantingCountForRanking: data.leaderboardScore,
          maxSingleTeamPlantingCount: data.maxDirectTeamCount,
          provinceCityDistribution: data.provinceCityDistribution ?? {},
          lastCalcAt: data.lastCalculatedAt,
        },
      });

      // 更新直推统计明细
      if (data.directReferrals && data.directReferrals.length > 0) {
        // 批量查询直推用户的 accountSequence
        const referralIds = data.directReferrals.map((dr) => dr.referralId);
        const referralRelations = await tx.referralRelationship.findMany({
          where: { userId: { in: referralIds } },
          select: { userId: true, accountSequence: true },
        });
        const sequenceMap = new Map<bigint, string>();
        for (const rel of referralRelations) {
          sequenceMap.set(rel.userId, rel.accountSequence);
        }

        for (const dr of data.directReferrals) {
          const accountSequence = sequenceMap.get(dr.referralId) ?? dr.referralId.toString();
          await tx.directReferral.upsert({
            where: {
              uk_referrer_referral: {
                referrerId: data.userId,
                referralId: dr.referralId,
              },
            },
            update: {
              teamPlantingCount: dr.teamCount,
              updatedAt: new Date(),
            },
            create: {
              referrerId: data.userId,
              referralId: dr.referralId,
              referralSequence: accountSequence,  // 现在是字符串类型，不需要 BigInt 转换
              teamPlantingCount: dr.teamCount,
            },
          });
        }
      }
    });

    // 重新查询完整数据
    return this.findByUserId(data.userId) as Promise<TeamStatistics>;
  }

  async findByUserId(userId: bigint): Promise<TeamStatistics | null> {
    const record = await this.prisma.teamStatistics.findUnique({
      where: { userId },
      include: {
        referralRelationship: {
          select: { accountSequence: true },
        },
      },
    });

    if (!record) return null;

    // 查询直推统计
    const directReferrals = await this.prisma.directReferral.findMany({
      where: { referrerId: userId },
      select: { referralId: true, teamPlantingCount: true },
    });

    return TeamStatistics.reconstitute(
      this.mapToProps(record, directReferrals, record.referralRelationship?.accountSequence ?? ''),
    );
  }

  async findByUserIds(userIds: bigint[]): Promise<TeamStatistics[]> {
    const records = await this.prisma.teamStatistics.findMany({
      where: { userId: { in: userIds } },
      include: {
        referralRelationship: {
          select: { accountSequence: true },
        },
      },
    });

    const result: TeamStatistics[] = [];
    for (const record of records) {
      const directReferrals = await this.prisma.directReferral.findMany({
        where: { referrerId: record.userId },
        select: { referralId: true, teamPlantingCount: true },
      });
      result.push(TeamStatistics.reconstitute(
        this.mapToProps(record, directReferrals, record.referralRelationship?.accountSequence ?? ''),
      ));
    }

    return result;
  }

  async getLeaderboard(options: LeaderboardQueryOptions = {}): Promise<LeaderboardEntry[]> {
    const { limit = 100, offset = 0 } = options;

    const records = await this.prisma.teamStatistics.findMany({
      where: {
        effectivePlantingCountForRanking: { gt: 0 },
      },
      orderBy: [
        { effectivePlantingCountForRanking: 'desc' },
        { totalTeamPlantingCount: 'desc' },
      ],
      skip: offset,
      take: limit,
      select: {
        userId: true,
        effectivePlantingCountForRanking: true,
        totalTeamPlantingCount: true,
        directReferralCount: true,
      },
    });

    return records.map((r, index) => ({
      userId: r.userId,
      score: r.effectivePlantingCountForRanking,
      rank: offset + index + 1,
      totalTeamCount: r.totalTeamPlantingCount,
      directReferralCount: r.directReferralCount,
    }));
  }

  async getUserRank(userId: bigint): Promise<number | null> {
    const userStats = await this.prisma.teamStatistics.findUnique({
      where: { userId },
      select: { effectivePlantingCountForRanking: true },
    });

    if (!userStats) return null;

    const higherRanks = await this.prisma.teamStatistics.count({
      where: {
        effectivePlantingCountForRanking: { gt: userStats.effectivePlantingCountForRanking },
      },
    });

    return higherRanks + 1;
  }

  async batchUpdateTeamCounts(
    updates: Array<{
      userId: bigint;
      countDelta: number;
      provinceCode: string;
      cityCode: string;
      fromDirectReferralId?: bigint;
    }>,
  ): Promise<void> {
    await this.prisma.$transaction(async (tx) => {
      for (const update of updates) {
        // 获取当前统计
        const current = await tx.teamStatistics.findUnique({
          where: { userId: update.userId },
        });

        if (!current) continue;

        // 更新省市分布
        const distribution = (current.provinceCityDistribution as Record<
          string,
          Record<string, number>
        >) ?? {};
        if (!distribution[update.provinceCode]) {
          distribution[update.provinceCode] = {};
        }
        distribution[update.provinceCode][update.cityCode] =
          (distribution[update.provinceCode][update.cityCode] ?? 0) + update.countDelta;

        // 更新直推团队统计
        let newMaxDirectTeamCount = current.maxSingleTeamPlantingCount;
        if (update.fromDirectReferralId) {
          const directRef = await tx.directReferral.upsert({
            where: {
              uk_referrer_referral: {
                referrerId: update.userId,
                referralId: update.fromDirectReferralId,
              },
            },
            update: {
              teamPlantingCount: { increment: update.countDelta },
            },
            create: {
              referrerId: update.userId,
              referralId: update.fromDirectReferralId,
              referralSequence: update.fromDirectReferralId.toString(),  // 转换为字符串
              teamPlantingCount: update.countDelta,
            },
          });

          if (directRef.teamPlantingCount > newMaxDirectTeamCount) {
            newMaxDirectTeamCount = directRef.teamPlantingCount;
          }
        }

        // 重新计算龙虎榜分值
        const newTotalTeamPlantingCount = current.totalTeamPlantingCount + update.countDelta;
        const newEffectiveScore = Math.max(0, newTotalTeamPlantingCount - newMaxDirectTeamCount);

        // 更新主表
        await tx.teamStatistics.update({
          where: { userId: update.userId },
          data: {
            totalTeamPlantingCount: newTotalTeamPlantingCount,
            effectivePlantingCountForRanking: newEffectiveScore,
            maxSingleTeamPlantingCount: newMaxDirectTeamCount,
            provinceCityDistribution: distribution,
            lastCalcAt: new Date(),
            updatedAt: new Date(),
          },
        });
      }
    });
  }

  /**
   * [纯新增] 批量更新团队统计（转让场景）
   * 与 batchUpdateTeamCounts 的关键区别：
   * 负数 delta 时重新扫描所有直推的 teamPlantingCount 找最大支线
   * 因为减少后原来的"大区"可能不再是最大的
   */
  async batchUpdateTeamCountsForTransfer(
    updates: Array<{
      userId: bigint;
      countDelta: number;
      provinceCode: string;
      cityCode: string;
      fromDirectReferralId?: bigint;
    }>,
  ): Promise<void> {
    await this.prisma.$transaction(async (tx) => {
      for (const update of updates) {
        const current = await tx.teamStatistics.findUnique({
          where: { userId: update.userId },
        });

        if (!current) continue;

        // 更新省市分布
        const distribution = (current.provinceCityDistribution as Record<
          string,
          Record<string, number>
        >) ?? {};
        if (!distribution[update.provinceCode]) {
          distribution[update.provinceCode] = {};
        }
        const currentCityCount = distribution[update.provinceCode][update.cityCode] ?? 0;
        distribution[update.provinceCode][update.cityCode] = Math.max(
          0,
          currentCityCount + update.countDelta,
        );

        // 更新直推团队统计
        if (update.fromDirectReferralId) {
          await tx.directReferral.upsert({
            where: {
              uk_referrer_referral: {
                referrerId: update.userId,
                referralId: update.fromDirectReferralId,
              },
            },
            update: {
              teamPlantingCount: { increment: update.countDelta },
            },
            create: {
              referrerId: update.userId,
              referralId: update.fromDirectReferralId,
              referralSequence: update.fromDirectReferralId.toString(),
              teamPlantingCount: Math.max(0, update.countDelta),
            },
          });
        }

        // 关键差异：负数 delta 时重新扫描所有直推找最大支线
        const allDirectReferrals = await tx.directReferral.findMany({
          where: { referrerId: update.userId },
          select: { teamPlantingCount: true },
        });
        const newMaxDirectTeamCount =
          allDirectReferrals.length > 0
            ? Math.max(0, ...allDirectReferrals.map((r) => r.teamPlantingCount))
            : 0;

        // 计算新总量和龙虎榜分值
        const newTotalTeamPlantingCount = Math.max(
          0,
          current.totalTeamPlantingCount + update.countDelta,
        );
        const newEffectiveScore = Math.max(
          0,
          newTotalTeamPlantingCount - newMaxDirectTeamCount,
        );

        await tx.teamStatistics.update({
          where: { userId: update.userId },
          data: {
            totalTeamPlantingCount: newTotalTeamPlantingCount,
            effectivePlantingCountForRanking: newEffectiveScore,
            maxSingleTeamPlantingCount: newMaxDirectTeamCount,
            provinceCityDistribution: distribution,
            lastCalcAt: new Date(),
            updatedAt: new Date(),
          },
        });
      }
    });
  }

  async create(userId: bigint, accountSequence: string): Promise<TeamStatistics> {
    const created = await this.prisma.teamStatistics.create({
      data: {
        userId,
        directReferralCount: 0,
        totalTeamCount: 0,
        selfPlantingCount: 0,
        totalTeamPlantingCount: 0,
        effectivePlantingCountForRanking: 0,
        maxSingleTeamPlantingCount: 0,
        provinceCityDistribution: {},
        lastCalcAt: new Date(),
      },
    });

    return TeamStatistics.reconstitute(this.mapToProps(created, [], accountSequence));
  }

  private mapToProps(
    record: {
      id: bigint;
      userId: bigint;
      directReferralCount: number;
      totalTeamCount: number;
      selfPlantingCount: number;
      totalTeamPlantingCount: number;
      effectivePlantingCountForRanking: number;
      maxSingleTeamPlantingCount: number;
      provinceCityDistribution: unknown;
      lastCalcAt: Date | null;
      createdAt: Date;
      updatedAt: Date;
    },
    directReferrals: Array<{
      referralId: bigint;
      teamPlantingCount: number;
    }>,
    accountSequence: string,
  ): TeamStatisticsProps {
    const directReferralStats: DirectReferralStats[] = directReferrals.map((dr) => ({
      referralId: dr.referralId,
      teamCount: dr.teamPlantingCount,
    }));

    return {
      id: record.id,
      userId: record.userId,
      accountSequence,
      directReferralCount: record.directReferralCount,
      totalTeamCount: record.totalTeamCount,
      personalPlantingCount: record.selfPlantingCount,
      teamPlantingCount: record.totalTeamPlantingCount,
      leaderboardScore: record.effectivePlantingCountForRanking,
      maxDirectTeamCount: record.maxSingleTeamPlantingCount,
      provinceCityDistribution: record.provinceCityDistribution as Record<
        string,
        Record<string, number>
      > | null,
      lastCalculatedAt: record.lastCalcAt ?? new Date(),
      createdAt: record.createdAt,
      updatedAt: record.updatedAt,
      directReferrals: directReferralStats,
    };
  }
}
