import { Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { PrePlantingPrismaService } from '../../infrastructure/prisma/pre-planting-prisma.service';
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
import { RedisService } from '@/infrastructure/redis/redis.service';
import {
  PRE_PLANTING_SOURCE_ID_OFFSET,
  PRE_PLANTING_FREEZE_PERIOD_YEARS,
  PRE_PLANTING_POST_UNFREEZE_VALIDITY_YEARS,
} from '../../domain/constants';

/**
 * 预种算力冻结调度器
 *
 * [2026-02-17] 新增：每日检查预种用户的冻结/解冻条件
 *
 * === 冻结规则 ===
 * - firstPurchaseAt + 1 年后，仍未满 10 份（totalPortions < 10）→ 冻结所有预种算力
 * - 冻结方式：将预种算力记录标记为 expired（is_expired=true）
 *
 * === 解冻规则 ===
 * - 冻结后累积满 10 份（totalPortions >= 10）→ 解冻
 * - 解冻后的失效期 = 解冻日 + 2 年
 *
 * === 正常到期（未被冻结过）===
 * - 首次产生挖矿收益日 + 2 年后失效（由现有 processExpiredRecords 调度器处理）
 *
 * === 对现有系统的影响 ===
 * - 零修改现有代码。冻结/解冻直接使用 PrismaService 查询 contribution_records 表。
 * - 只操作 sourceAdoptionId >= 10,000,000,000 的预种记录。
 * - 不调用现有 ContributionRecordRepository 的任何新方法，避免修改现有文件。
 */
@Injectable()
export class PrePlantingFreezeScheduler {
  private readonly logger = new Logger(PrePlantingFreezeScheduler.name);
  private readonly LOCK_KEY = 'pre-planting:freeze:lock';

  constructor(
    private readonly prePlantingPrisma: PrePlantingPrismaService,
    private readonly prisma: PrismaService, // 主 PrismaService，直接查询 contribution_records
    private readonly redis: RedisService,
  ) {}

  /**
   * 每日凌晨 5 点检查预种冻结条件
   *
   * 选择 5 点是因为：
   * - 现有调度器在 1-4 点运行（snapshot, expire, full-sync）
   * - 错开时间避免资源竞争
   */
  @Cron('0 5 * * *')
  async checkFreezeConditions(): Promise<void> {
    const lockValue = await this.redis.acquireLock(`${this.LOCK_KEY}:check`, 300);
    if (!lockValue) return;

    try {
      const now = new Date();
      let frozenCount = 0;
      let unfrozenCount = 0;

      // 1. 检查需要冻结的用户
      frozenCount = await this.processFreezes(now);

      // 2. 检查需要解冻的用户
      unfrozenCount = await this.processUnfreezes(now);

      if (frozenCount > 0 || unfrozenCount > 0) {
        this.logger.log(
          `[PRE-PLANTING-FREEZE] Check complete: frozen=${frozenCount}, unfrozen=${unfrozenCount}`,
        );
      }
    } catch (error) {
      this.logger.error('[PRE-PLANTING-FREEZE] Check failed', error);
    } finally {
      await this.redis.releaseLock(`${this.LOCK_KEY}:check`, lockValue);
    }
  }

  /**
   * 处理冻结：查找 firstPurchaseAt + 1 年已过且未满 10 份的用户
   */
  private async processFreezes(now: Date): Promise<number> {
    const freezeDeadline = new Date(now);
    freezeDeadline.setFullYear(freezeDeadline.getFullYear() - PRE_PLANTING_FREEZE_PERIOD_YEARS);

    const candidates = await this.prePlantingPrisma.prePlantingFreezeState.findMany({
      where: {
        firstPurchaseAt: { lte: freezeDeadline },
        totalPortions: { lt: 10 },
        isFrozen: false,
      },
      take: 100,
    });

    let count = 0;
    for (const candidate of candidates) {
      try {
        await this.freezeContributions(candidate.accountSequence);

        await this.prePlantingPrisma.prePlantingFreezeState.update({
          where: { accountSequence: candidate.accountSequence },
          data: {
            isFrozen: true,
            frozenAt: now,
          },
        });

        count++;
        this.logger.log(
          `[PRE-PLANTING-FREEZE] Frozen: ${candidate.accountSequence}, ` +
          `portions=${candidate.totalPortions}, firstPurchase=${candidate.firstPurchaseAt?.toISOString()}`,
        );
      } catch (error) {
        this.logger.error(
          `[PRE-PLANTING-FREEZE] Failed to freeze ${candidate.accountSequence}`,
          error,
        );
      }
    }

    return count;
  }

  /**
   * 处理解冻：查找已冻结但现在满 10 份的用户
   */
  private async processUnfreezes(now: Date): Promise<number> {
    const candidates = await this.prePlantingPrisma.prePlantingFreezeState.findMany({
      where: {
        isFrozen: true,
        totalPortions: { gte: 10 },
      },
      take: 100,
    });

    let count = 0;
    for (const candidate of candidates) {
      try {
        const postUnfreezeExpireDate = new Date(now);
        postUnfreezeExpireDate.setFullYear(
          postUnfreezeExpireDate.getFullYear() + PRE_PLANTING_POST_UNFREEZE_VALIDITY_YEARS,
        );

        await this.unfreezeContributions(candidate.accountSequence, postUnfreezeExpireDate);

        await this.prePlantingPrisma.prePlantingFreezeState.update({
          where: { accountSequence: candidate.accountSequence },
          data: {
            isFrozen: false,
            unfrozenAt: now,
            postUnfreezeExpireDate,
          },
        });

        count++;
        this.logger.log(
          `[PRE-PLANTING-FREEZE] Unfrozen: ${candidate.accountSequence}, ` +
          `portions=${candidate.totalPortions}, newExpire=${postUnfreezeExpireDate.toISOString()}`,
        );
      } catch (error) {
        this.logger.error(
          `[PRE-PLANTING-FREEZE] Failed to unfreeze ${candidate.accountSequence}`,
          error,
        );
      }
    }

    return count;
  }

  /**
   * 冻结用户的预种算力记录
   *
   * 直接使用 PrismaService 操作 contribution_records 表，
   * 通过 sourceAdoptionId >= 10B 精确定位预种记录，不影响正常认种记录。
   */
  private async freezeContributions(accountSequence: string): Promise<void> {
    const result = await this.prisma.contributionRecord.updateMany({
      where: {
        accountSequence,
        sourceAdoptionId: { gte: PRE_PLANTING_SOURCE_ID_OFFSET },
        isExpired: false,
      },
      data: {
        isExpired: true,
        expiredAt: new Date(),
      },
    });

    if (result.count > 0) {
      this.logger.log(
        `[PRE-PLANTING-FREEZE] Froze ${result.count} records for ${accountSequence}`,
      );
    }
  }

  /**
   * 解冻用户的预种算力记录
   *
   * 恢复 expired 标记并更新过期日期为 解冻日 + 2 年
   */
  private async unfreezeContributions(
    accountSequence: string,
    newExpireDate: Date,
  ): Promise<void> {
    const result = await this.prisma.contributionRecord.updateMany({
      where: {
        accountSequence,
        sourceAdoptionId: { gte: PRE_PLANTING_SOURCE_ID_OFFSET },
        isExpired: true,
      },
      data: {
        isExpired: false,
        expiredAt: null,
        expireDate: newExpireDate,
      },
    });

    if (result.count > 0) {
      this.logger.log(
        `[PRE-PLANTING-FREEZE] Unfroze ${result.count} records for ${accountSequence}, ` +
        `newExpire=${newExpireDate.toISOString()}`,
      );
    }
  }
}
