import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { PrismaService } from '../../shared/prisma/prisma.service';
import { SystemConfigService } from '../../shared/config/system-config.service';
import { getUploadRoot } from '../../shared/uploads/upload-paths';
import { join, dirname } from 'path';
import { existsSync } from 'fs';
import { mkdir, stat, unlink, writeFile } from 'fs/promises';

@Injectable()
export class DepositScreenshotCleanupService implements OnModuleInit {
  private readonly logger = new Logger(DepositScreenshotCleanupService.name);

  constructor(
    private prisma: PrismaService,
    private systemConfigService: SystemConfigService,
  ) {}

  async onModuleInit() {
    await this.ensureExpiredPlaceholderExists();
  }

  /**
   * 确保已过期/已清理的截图占位图存在
   */
  async ensureExpiredPlaceholderExists() {
    // 1x1 像素透明 PNG
    const defaultExpiredPngBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
    const root = getUploadRoot();
    const expiredPath = join(root, 'defaults', 'expired.png');
    try {
      await mkdir(dirname(expiredPath), { recursive: true });
      // 强制写入以确保生成最新的透明 1x1 占位图
      await writeFile(expiredPath, Buffer.from(defaultExpiredPngBase64, 'base64'));
      this.logger.log('Ensured expired screenshot default placeholder (transparent 1x1).');
    } catch (err) {
      this.logger.error('Failed to create default expired screenshot placeholder', err);
    }
  }

  /**
   * 每日凌晨3点定时执行清理
   */
  @Cron('0 0 3 * * *')
  async handleScheduledCleanup() {
    this.logger.log('Scheduled deposit screenshot cleanup job started');
    try {
      const config = await this.systemConfigService.getDepositScreenshotCleanupConfig();
      if (!config.enabled) {
        this.logger.log('Scheduled cleanup is disabled, skipping');
        return;
      }

      const beforeDate = new Date();
      beforeDate.setDate(beforeDate.getDate() - config.keepDays);
      this.logger.log(`Cleaning deposit screenshots older than ${config.keepDays} days (before ${beforeDate.toISOString()})`);

      const result = await this.cleanOldDepositScreenshots(beforeDate);
      this.logger.log(`Scheduled cleanup completed. Cleaned: ${result.cleaned} screenshots, Freed: ${(result.freedBytes / 1024 / 1024).toFixed(2)} MB`);
    } catch (err) {
      this.logger.error('Scheduled cleanup failed', err);
    }
  }

  /**
   * 清理指定日期之前的已处理充值订单截图
   */
  async cleanOldDepositScreenshots(before: Date): Promise<{ cleaned: number; freedBytes: number }> {
    const orders = await this.prisma.depositOrder.findMany({
      where: {
        createdAt: { lt: before },
        status: { in: ['APPROVED', 'REJECTED'] },
        screenshotUrl: {
          startsWith: '/uploads/',
          not: { startsWith: '/uploads/defaults/' },
        },
      },
      select: {
        id: true,
        screenshotUrl: true,
      },
    });

    let cleaned = 0;
    let freedBytes = 0;
    const batchSize = 100;
    const root = getUploadRoot();

    for (let i = 0; i < orders.length; i += batchSize) {
      const chunk = orders.slice(i, i + batchSize);
      await Promise.all(
        chunk.map(async (order) => {
          const url = order.screenshotUrl;
          if (!url.startsWith('/uploads/')) return;
          const relative = url.slice('/uploads/'.length);
          // 安全路径校验，防止目录穿越
          if (!relative || relative.includes('..') || relative.includes('\\')) return;
          const filePath = join(root, relative);

          let size = 0;
          try {
            const fileStats = await stat(filePath);
            size = fileStats.size;
            await unlink(filePath);
            freedBytes += size;
          } catch {
            // 文件不存在或已被删除，静默跳过，但依然更新数据库
          }

          await this.prisma.depositOrder.update({
            where: { id: order.id },
            data: { screenshotUrl: '/uploads/defaults/expired.png' },
          });
          cleaned++;
        })
      );
    }

    return { cleaned, freedBytes };
  }
}
