import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { SkipAssertAccess } from '@common/decorators/skip-assert-access.decorator';

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

  constructor(private readonly prisma: PrismaService) {}

  /**
   * 每月 1 日 03:00 UTC 把超过 1 年的 event 聚合到 daily_rollups + 物理删除
   * 顺手清理 30 天前的 DLQ 行
   */
  @SkipAssertAccess('系统 cron 任务：按 ts 时间窗清理全 org 历史数据，无 user 上下文，无 IDOR 风险')
  @Cron('0 3 1 * *', { timeZone: 'UTC' })
  async runMonthlyArchival() {
    const cutoff = new Date();
    cutoff.setUTCFullYear(cutoff.getUTCFullYear() - 1);
    try {
      const rolledUp = await this.prisma.$executeRaw`
        INSERT INTO platform_ai_usage.ai_usage_daily_rollups
          (id, date, user_id, project_basename, tool, model,
           total_tokens, input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
           total_cost_usd, event_count, organization_id, created_by_id, updated_at)
        SELECT gen_random_uuid(),
               (ts AT TIME ZONE 'UTC')::date,
               user_id,
               project_basename,
               tool,
               model,
               SUM(total_tokens)::bigint,
               SUM(input_tokens)::bigint,
               SUM(output_tokens)::bigint,
               SUM(cache_creation_tokens)::bigint,
               SUM(cache_read_tokens)::bigint,
               SUM(estimated_cost_usd),
               COUNT(*)::int,
               organization_id,
               user_id,
               NOW()
        FROM platform_ai_usage.ai_usage_events
        WHERE ts < ${cutoff}
        GROUP BY (ts AT TIME ZONE 'UTC')::date, user_id, project_basename, tool, model, organization_id
        ON CONFLICT (date, user_id, project_basename, tool, model) DO NOTHING
      `;
      const deletedEvents = await this.prisma.aiUsageEvent.deleteMany({
        where: { ts: { lt: cutoff } },
      });
      const dlqCutoff = new Date();
      dlqCutoff.setUTCDate(dlqCutoff.getUTCDate() - 30);
      const deletedDlq = await this.prisma.aiUsageEventDlq.deleteMany({
        where: { createdAt: { lt: dlqCutoff } },
      });
      this.logger.log(
        `Monthly archival completed: rolled up ${rolledUp} dimension rows, deleted ${deletedEvents.count} events, cleaned ${deletedDlq.count} DLQ rows`,
      );
    } catch (err: any) {
      this.logger.error(`Monthly archival failed: ${err.message}`, err.stack);
    }
  }
}
