/**
 * Prisma Mock for Integration Tests
 * 不依赖真实数据库的模拟实现
 */

type MockReferralRelationship = {
  id: bigint;
  userId: bigint;
  accountSequence: string;  // 格式: D + YYMMDD + 5位序号
  referrerId: bigint | null;
  rootUserId: bigint | null;
  myReferralCode: string;
  usedReferralCode: string | null;
  ancestorPath: bigint[];
  depth: number;
  directReferralCount: number;
  activeDirectCount: number;
  createdAt: Date;
  updatedAt: Date;
};

type MockTeamStatistics = {
  id: bigint;
  userId: bigint;
  directReferralCount: number;
  totalTeamCount: number;
  selfPlantingCount: number;
  totalTeamPlantingCount: number;
  effectivePlantingCountForRanking: number;
  maxSingleTeamPlantingCount: number;
  provinceCityDistribution: Record<string, Record<string, number>>;
  lastCalcAt: Date | null;
  createdAt: Date;
  updatedAt: Date;
};

type MockDirectReferral = {
  id: bigint;
  referrerId: bigint;
  referralId: bigint;
  referralSequence: bigint;
  teamPlantingCount: number;
  createdAt: Date;
  updatedAt: Date;
};

export class MockPrismaService {
  private _referralRelationshipsStore: Map<bigint, MockReferralRelationship> = new Map();
  private _teamStatisticsStore: Map<bigint, MockTeamStatistics> = new Map();
  private _directReferralsStore: Map<string, MockDirectReferral> = new Map();
  private idCounter = 1n;

  // ReferralRelationship operations
  referralRelationship = {
    upsert: async (args: {
      where: { userId: bigint };
      create: Partial<MockReferralRelationship>;
      update: Partial<MockReferralRelationship>;
    }) => {
      const existing = this._referralRelationshipsStore.get(args.where.userId);
      if (existing) {
        const updated = { ...existing, ...args.update, updatedAt: new Date() };
        this._referralRelationshipsStore.set(args.where.userId, updated);
        return updated;
      }
      const created: MockReferralRelationship = {
        id: this.idCounter++,
        userId: args.where.userId,
        accountSequence: args.create.accountSequence ?? `D${Date.now()}`,
        referrerId: args.create.referrerId ?? null,
        rootUserId: args.create.rootUserId ?? null,
        myReferralCode: args.create.myReferralCode!,
        usedReferralCode: args.create.usedReferralCode ?? null,
        ancestorPath: args.create.ancestorPath ?? [],
        depth: args.create.depth ?? 0,
        directReferralCount: 0,
        activeDirectCount: 0,
        createdAt: new Date(),
        updatedAt: new Date(),
      };
      this._referralRelationshipsStore.set(args.where.userId, created);
      return created;
    },

    findUnique: async (args: { where: { userId?: bigint; myReferralCode?: string }; select?: unknown }) => {
      if (args.where.userId) {
        return this._referralRelationshipsStore.get(args.where.userId) ?? null;
      }
      if (args.where.myReferralCode) {
        for (const r of this._referralRelationshipsStore.values()) {
          if (r.myReferralCode === args.where.myReferralCode) return r;
        }
      }
      return null;
    },

    findMany: async (args: { where?: { referrerId?: bigint }; orderBy?: unknown }) => {
      const results: MockReferralRelationship[] = [];
      for (const r of this._referralRelationshipsStore.values()) {
        if (!args.where?.referrerId || r.referrerId === args.where.referrerId) {
          results.push(r);
        }
      }
      return results;
    },

    count: async (args: { where: { myReferralCode?: string; userId?: bigint } }) => {
      let count = 0;
      for (const r of this._referralRelationshipsStore.values()) {
        if (args.where.myReferralCode && r.myReferralCode === args.where.myReferralCode) count++;
        if (args.where.userId && r.userId === args.where.userId) count++;
      }
      return count;
    },
  };

  // TeamStatistics operations
  teamStatistics = {
    upsert: async (args: {
      where: { userId: bigint };
      create: Partial<MockTeamStatistics>;
      update: Partial<MockTeamStatistics>;
    }) => {
      const existing = this._teamStatisticsStore.get(args.where.userId);
      if (existing) {
        const updated = { ...existing, ...args.update, updatedAt: new Date() };
        this._teamStatisticsStore.set(args.where.userId, updated);
        return updated;
      }
      const created: MockTeamStatistics = {
        id: this.idCounter++,
        userId: args.where.userId,
        directReferralCount: args.create.directReferralCount ?? 0,
        totalTeamCount: args.create.totalTeamCount ?? 0,
        selfPlantingCount: args.create.selfPlantingCount ?? 0,
        totalTeamPlantingCount: args.create.totalTeamPlantingCount ?? 0,
        effectivePlantingCountForRanking: args.create.effectivePlantingCountForRanking ?? 0,
        maxSingleTeamPlantingCount: args.create.maxSingleTeamPlantingCount ?? 0,
        provinceCityDistribution: (args.create.provinceCityDistribution as Record<string, Record<string, number>>) ?? {},
        lastCalcAt: args.create.lastCalcAt ?? new Date(),
        createdAt: new Date(),
        updatedAt: new Date(),
      };
      this._teamStatisticsStore.set(args.where.userId, created);
      return created;
    },

    create: async (args: { data: Partial<MockTeamStatistics> }) => {
      const created: MockTeamStatistics = {
        id: this.idCounter++,
        userId: args.data.userId!,
        directReferralCount: args.data.directReferralCount ?? 0,
        totalTeamCount: args.data.totalTeamCount ?? 0,
        selfPlantingCount: args.data.selfPlantingCount ?? 0,
        totalTeamPlantingCount: args.data.totalTeamPlantingCount ?? 0,
        effectivePlantingCountForRanking: args.data.effectivePlantingCountForRanking ?? 0,
        maxSingleTeamPlantingCount: args.data.maxSingleTeamPlantingCount ?? 0,
        provinceCityDistribution: (args.data.provinceCityDistribution as Record<string, Record<string, number>>) ?? {},
        lastCalcAt: args.data.lastCalcAt ?? new Date(),
        createdAt: new Date(),
        updatedAt: new Date(),
      };
      this._teamStatisticsStore.set(created.userId, created);
      return created;
    },

    findUnique: async (args: { where: { userId: bigint } }) => {
      return this._teamStatisticsStore.get(args.where.userId) ?? null;
    },

    findMany: async (args: {
      where?: { userId?: { in: bigint[] }; effectivePlantingCountForRanking?: { gt: number } };
      orderBy?: unknown[];
      skip?: number;
      take?: number;
      select?: unknown;
    }) => {
      let results = Array.from(this._teamStatisticsStore.values());

      if (args.where?.userId?.in) {
        results = results.filter((s) => args.where!.userId!.in.includes(s.userId));
      }
      if (args.where?.effectivePlantingCountForRanking?.gt !== undefined) {
        results = results.filter(
          (s) => s.effectivePlantingCountForRanking > args.where!.effectivePlantingCountForRanking!.gt,
        );
      }

      // Sort by score descending
      results.sort((a, b) => b.effectivePlantingCountForRanking - a.effectivePlantingCountForRanking);

      if (args.skip) results = results.slice(args.skip);
      if (args.take) results = results.slice(0, args.take);

      return results;
    },

    count: async (args: { where: { effectivePlantingCountForRanking?: { gt: number } } }) => {
      let count = 0;
      for (const s of this._teamStatisticsStore.values()) {
        if (
          args.where.effectivePlantingCountForRanking?.gt !== undefined &&
          s.effectivePlantingCountForRanking > args.where.effectivePlantingCountForRanking.gt
        ) {
          count++;
        }
      }
      return count;
    },

    update: async (args: { where: { userId: bigint }; data: Partial<MockTeamStatistics> }) => {
      const existing = this._teamStatisticsStore.get(args.where.userId);
      if (!existing) throw new Error('Not found');
      const updated = { ...existing, ...args.data, updatedAt: new Date() };
      this._teamStatisticsStore.set(args.where.userId, updated);
      return updated;
    },
  };

  // DirectReferral operations
  directReferral = {
    upsert: async (args: {
      where: { uk_referrer_referral: { referrerId: bigint; referralId: bigint } };
      create: Partial<MockDirectReferral>;
      update: Partial<MockDirectReferral> | { teamPlantingCount: { increment: number } };
    }) => {
      const key = `${args.where.uk_referrer_referral.referrerId}-${args.where.uk_referrer_referral.referralId}`;
      const existing = this._directReferralsStore.get(key);

      if (existing) {
        let teamPlantingCount = existing.teamPlantingCount;
        if ('teamPlantingCount' in args.update) {
          if (typeof args.update.teamPlantingCount === 'object' && 'increment' in args.update.teamPlantingCount) {
            teamPlantingCount += args.update.teamPlantingCount.increment;
          } else {
            teamPlantingCount = args.update.teamPlantingCount as number;
          }
        }
        const updated = { ...existing, teamPlantingCount, updatedAt: new Date() };
        this._directReferralsStore.set(key, updated);
        return updated;
      }

      const created: MockDirectReferral = {
        id: this.idCounter++,
        referrerId: args.where.uk_referrer_referral.referrerId,
        referralId: args.where.uk_referrer_referral.referralId,
        referralSequence: args.create.referralSequence!,
        teamPlantingCount: args.create.teamPlantingCount ?? 0,
        createdAt: new Date(),
        updatedAt: new Date(),
      };
      this._directReferralsStore.set(key, created);
      return created;
    },

    findMany: async (args: { where: { referrerId: bigint }; select?: unknown }) => {
      const results: MockDirectReferral[] = [];
      for (const dr of this._directReferralsStore.values()) {
        if (dr.referrerId === args.where.referrerId) {
          results.push(dr);
        }
      }
      return results;
    },
  };

  // Transaction support
  $transaction = async <T>(fn: (tx: MockPrismaService) => Promise<T>): Promise<T> => {
    return fn(this);
  };

  // Cleanup for tests
  $cleanup() {
    this._referralRelationshipsStore.clear();
    this._teamStatisticsStore.clear();
    this._directReferralsStore.clear();
    this.idCounter = 1n;
  }

  // Lifecycle methods
  async onModuleInit() {}
  async onModuleDestroy() {}
}
