import { Injectable } from '@nestjs/common';
import Decimal from 'decimal.js';
import {
  ISyncedDataRepository,
  SyncedUser,
  SyncedAdoption,
  SyncedReferral,
} from '../../../domain/repositories/synced-data.repository.interface';
import { UnitOfWork, TransactionClient } from '../unit-of-work/unit-of-work';

@Injectable()
export class SyncedDataRepository implements ISyncedDataRepository {
  constructor(private readonly unitOfWork: UnitOfWork) {}

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

  // ========== User 操作 ==========

  async upsertSyncedUser(data: {
    accountSequence: string;
    originalUserId: bigint;
    phone?: string | null;
    status?: string | null;
    sourceSequenceNum: bigint;
  }): Promise<SyncedUser> {
    const record = await this.client.syncedUser.upsert({
      where: { accountSequence: data.accountSequence },
      create: {
        originalUserId: data.originalUserId,
        accountSequence: data.accountSequence,
        phone: data.phone ?? null,
        status: data.status ?? null,
        sourceSequenceNum: data.sourceSequenceNum,
        syncedAt: new Date(),
      },
      update: {
        phone: data.phone ?? undefined,
        status: data.status ?? undefined,
        sourceSequenceNum: data.sourceSequenceNum,
        syncedAt: new Date(),
      },
    });

    return this.toSyncedUser(record);
  }

  async findSyncedUserByAccountSequence(accountSequence: string): Promise<SyncedUser | null> {
    const record = await this.client.syncedUser.findUnique({
      where: { accountSequence },
    });

    if (!record) {
      return null;
    }

    return this.toSyncedUser(record);
  }

  async findUncalculatedUsers(limit: number = 100): Promise<SyncedUser[]> {
    const records = await this.client.syncedUser.findMany({
      where: { contributionCalculated: false },
      orderBy: { createdAt: 'asc' },
      take: limit,
    });

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

  async markUserContributionCalculated(accountSequence: string, tx?: any): Promise<void> {
    const client = tx ?? this.client;
    await client.syncedUser.update({
      where: { accountSequence },
      data: {
        contributionCalculated: true,
        contributionCalculatedAt: new Date(),
      },
    });
  }

  // ========== Adoption 操作 ==========

  async upsertSyncedAdoption(data: {
    originalAdoptionId: bigint;
    accountSequence: string;
    treeCount: number;
    adoptionDate: Date;
    status?: string | null;
    contributionPerTree: Decimal;
    sourceSequenceNum: bigint;
  }): Promise<SyncedAdoption> {
    const record = await this.client.syncedAdoption.upsert({
      where: { originalAdoptionId: data.originalAdoptionId },
      create: {
        originalAdoptionId: data.originalAdoptionId,
        accountSequence: data.accountSequence,
        treeCount: data.treeCount,
        adoptionDate: data.adoptionDate,
        status: data.status ?? null,
        contributionPerTree: data.contributionPerTree,
        sourceSequenceNum: data.sourceSequenceNum,
        syncedAt: new Date(),
      },
      update: {
        accountSequence: data.accountSequence,
        treeCount: data.treeCount,
        adoptionDate: data.adoptionDate,
        status: data.status ?? undefined,
        contributionPerTree: data.contributionPerTree,
        sourceSequenceNum: data.sourceSequenceNum,
        syncedAt: new Date(),
      },
    });

    return this.toSyncedAdoption(record);
  }

  async findSyncedAdoptionByOriginalId(originalAdoptionId: bigint): Promise<SyncedAdoption | null> {
    const record = await this.client.syncedAdoption.findUnique({
      where: { originalAdoptionId },
    });

    if (!record) {
      return null;
    }

    return this.toSyncedAdoption(record);
  }

  async findUndistributedAdoptions(limit: number = 100): Promise<SyncedAdoption[]> {
    const records = await this.client.syncedAdoption.findMany({
      where: { contributionDistributed: false },
      orderBy: { adoptionDate: 'asc' },
      take: limit,
    });

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

  async findAdoptionsByAccountSequence(accountSequence: string): Promise<SyncedAdoption[]> {
    const records = await this.client.syncedAdoption.findMany({
      where: { accountSequence },
      orderBy: { adoptionDate: 'asc' },
    });

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

  async markAdoptionContributionDistributed(originalAdoptionId: bigint, tx?: any): Promise<void> {
    const client = tx ?? this.client;
    await client.syncedAdoption.update({
      where: { originalAdoptionId },
      data: {
        contributionDistributed: true,
        contributionDistributedAt: new Date(),
      },
    });
  }

  async getTotalTreesByAccountSequence(accountSequence: string): Promise<number> {
    const result = await this.client.syncedAdoption.aggregate({
      where: { accountSequence },
      _sum: { treeCount: true },
    });
    return result._sum.treeCount ?? 0;
  }

  // ========== Referral 操作 ==========

  async upsertSyncedReferral(data: {
    accountSequence: string;
    referrerAccountSequence?: string | null;
    ancestorPath?: string | null;
    depth?: number;
    sourceSequenceNum: bigint;
  }): Promise<SyncedReferral> {
    const record = await this.client.syncedReferral.upsert({
      where: { accountSequence: data.accountSequence },
      create: {
        accountSequence: data.accountSequence,
        referrerAccountSequence: data.referrerAccountSequence ?? null,
        ancestorPath: data.ancestorPath ?? null,
        depth: data.depth ?? 0,
        sourceSequenceNum: data.sourceSequenceNum,
        syncedAt: new Date(),
      },
      update: {
        referrerAccountSequence: data.referrerAccountSequence ?? undefined,
        ancestorPath: data.ancestorPath ?? undefined,
        depth: data.depth ?? undefined,
        sourceSequenceNum: data.sourceSequenceNum,
        syncedAt: new Date(),
      },
    });

    return this.toSyncedReferral(record);
  }

  async findSyncedReferralByAccountSequence(accountSequence: string): Promise<SyncedReferral | null> {
    const record = await this.client.syncedReferral.findUnique({
      where: { accountSequence },
    });

    if (!record) {
      return null;
    }

    return this.toSyncedReferral(record);
  }

  async findDirectReferrals(referrerAccountSequence: string): Promise<SyncedReferral[]> {
    const records = await this.client.syncedReferral.findMany({
      where: { referrerAccountSequence },
      orderBy: { createdAt: 'asc' },
    });

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

  async findAncestorChain(accountSequence: string, maxLevel: number = 15): Promise<SyncedReferral[]> {
    const ancestors: SyncedReferral[] = [];
    let currentSequence = accountSequence;

    for (let i = 0; i < maxLevel; i++) {
      const referral = await this.findSyncedReferralByAccountSequence(currentSequence);
      if (!referral || !referral.referrerAccountSequence) {
        break;
      }

      const referrer = await this.findSyncedReferralByAccountSequence(referral.referrerAccountSequence);
      if (!referrer) {
        break;
      }

      ancestors.push(referrer);
      currentSequence = referrer.accountSequence;
    }

    return ancestors;
  }

  async getDirectReferralAdoptedCount(referrerAccountSequence: string): Promise<number> {
    const directReferrals = await this.client.syncedReferral.findMany({
      where: { referrerAccountSequence },
      select: { accountSequence: true },
    });

    if (directReferrals.length === 0) {
      return 0;
    }

    const accountSequences = directReferrals.map((r) => r.accountSequence);

    const adoptedCount = await this.client.syncedAdoption.findMany({
      where: { accountSequence: { in: accountSequences } },
      distinct: ['accountSequence'],
    });

    return adoptedCount.length;
  }

  async getTeamTreesByLevel(
    accountSequence: string,
    maxLevel: number = 15,
  ): Promise<Map<number, number>> {
    const result = new Map<number, number>();

    for (let level = 1; level <= maxLevel; level++) {
      result.set(level, 0);
    }

    const processLevel = async (sequences: string[], currentLevel: number): Promise<void> => {
      if (currentLevel > maxLevel || sequences.length === 0) return;

      const adoptions = await this.client.syncedAdoption.groupBy({
        by: ['accountSequence'],
        where: { accountSequence: { in: sequences } },
        _sum: { treeCount: true },
      });

      let levelTrees = 0;
      for (const adoption of adoptions) {
        levelTrees += adoption._sum.treeCount ?? 0;
      }
      result.set(currentLevel, levelTrees);

      const nextLevelReferrals = await this.client.syncedReferral.findMany({
        where: { referrerAccountSequence: { in: sequences } },
        select: { accountSequence: true },
      });

      if (nextLevelReferrals.length > 0) {
        await processLevel(
          nextLevelReferrals.map((r) => r.accountSequence),
          currentLevel + 1,
        );
      }
    };

    const directReferrals = await this.client.syncedReferral.findMany({
      where: { referrerAccountSequence: accountSequence },
      select: { accountSequence: true },
    });

    if (directReferrals.length > 0) {
      await processLevel(
        directReferrals.map((r) => r.accountSequence),
        1,
      );
    }

    return result;
  }

  // ========== 统计方法（用于查询服务）==========

  async countUsers(): Promise<number> {
    return this.client.syncedUser.count();
  }

  async countAdoptions(): Promise<number> {
    return this.client.syncedAdoption.count();
  }

  async countUndistributedAdoptions(): Promise<number> {
    return this.client.syncedAdoption.count({
      where: { contributionDistributed: false },
    });
  }

  // ========== 私有方法 ==========

  private toSyncedUser(record: any): SyncedUser {
    return {
      id: record.id,
      accountSequence: record.accountSequence,
      originalUserId: record.originalUserId,
      phone: record.phone,
      status: record.status,
      sourceSequenceNum: record.sourceSequenceNum,
      syncedAt: record.syncedAt,
      contributionCalculated: record.contributionCalculated,
      contributionCalculatedAt: record.contributionCalculatedAt,
      createdAt: record.createdAt,
    };
  }

  private toSyncedAdoption(record: any): SyncedAdoption {
    return {
      id: record.id,
      originalAdoptionId: record.originalAdoptionId,
      accountSequence: record.accountSequence,
      treeCount: record.treeCount,
      adoptionDate: record.adoptionDate,
      status: record.status,
      contributionPerTree: record.contributionPerTree,
      sourceSequenceNum: record.sourceSequenceNum,
      syncedAt: record.syncedAt,
      contributionDistributed: record.contributionDistributed,
      contributionDistributedAt: record.contributionDistributedAt,
      createdAt: record.createdAt,
    };
  }

  private toSyncedReferral(record: any): SyncedReferral {
    return {
      id: record.id,
      accountSequence: record.accountSequence,
      referrerAccountSequence: record.referrerAccountSequence,
      ancestorPath: record.ancestorPath,
      depth: record.depth,
      sourceSequenceNum: record.sourceSequenceNum,
      syncedAt: record.syncedAt,
      createdAt: record.createdAt,
    };
  }
}
