import { internalMutation } from "./_generated/server";
import { v } from "convex/values";
import { internal } from "./_generated/api";

const WEEK_MS = 7 * 24 * 60 * 60 * 1000;

async function cleanupByCreationTime(ctx: any, table: string, cutoffMs: number, maxDeletes: number, dryRun: boolean) {
  const batchSize = 200;
  let deleted = 0;

  while (deleted < maxDeletes) {
    const take = Math.min(batchSize, maxDeletes - deleted);
    const rows = await ctx.db.query(table as any).order("asc").take(take);
    if (!rows.length) break;

    let reachedNewer = false;
    for (const row of rows) {
      const created = typeof row._creationTime === "number" ? row._creationTime : 0;
      if (created >= cutoffMs) {
        reachedNewer = true;
        break;
      }
      if (!dryRun) {
        await ctx.db.delete(row._id);
      }
      deleted += 1;
      if (deleted >= maxDeletes) break;
    }

    if (reachedNewer) break;
  }

  // 说明：若本次达到上限，可能还有更多旧数据；由调用方决定是否继续调度。
  return deleted;
}

async function cleanupOrphanConvexFiles(
  ctx: any,
  args: { cutoffMs: number; maxDeletes: number; dryRun: boolean },
): Promise<{ deleted: number; scanned: number; hitLimit: boolean }> {
  const batchSize = 200;
  let deleted = 0;
  let scanned = 0;
  let lastCreationTime: number | null = null;
  let lastId: any = null;

  while (deleted < args.maxDeletes) {
    let q = ctx.db.system.query("_storage").order("asc");
    if (lastCreationTime != null && lastId != null) {
      // 说明：Convex 的 `paginate` 每个函数只能调用一次，这里用“游标过滤 + take”模拟分页。
      // 使用 (_creationTime, _id) 作为游标，避免同一毫秒内多条记录导致遗漏/死循环。
      q = q.filter((qb: any) =>
        qb.or(
          qb.gt(qb.field("_creationTime"), lastCreationTime),
          qb.and(qb.eq(qb.field("_creationTime"), lastCreationTime), qb.gt(qb.field("_id"), lastId)),
        ),
      );
    }

    const rows = await q.take(batchSize);
    if (!rows.length) break;

    let reachedNewer = false;
    for (const row of rows) {
      scanned += 1;
      const created = typeof row._creationTime === "number" ? row._creationTime : 0;
      lastCreationTime = created;
      lastId = row._id;
      if (created >= args.cutoffMs) {
        reachedNewer = true;
        break;
      }

      // 说明：当前仓库里，Convex Files 仅用于 media_assets.storage_id。
      // 若未来新增其它引用表，需要把引用检查一并补齐，避免误删仍被使用的文件。
      const ref = await ctx.db
        .query("media_assets")
        .withIndex("by_storage_id", (q: any) => q.eq("storage_id", row._id))
        .first();
      if (ref) continue;

      if (!args.dryRun) {
        try {
          await ctx.storage.delete(row._id);
        } catch {
          // ignore
        }
      }
      deleted += 1;
      if (deleted >= args.maxDeletes) break;
    }

    if (reachedNewer) break;
  }

  return { deleted, scanned, hitLimit: deleted >= args.maxDeletes };
}

export const cleanupWeekly = internalMutation({
  args: {
    dryRun: v.optional(v.boolean()),
    maxDeletes: v.optional(v.number()),
    maxFileDeletes: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    const dryRun = Boolean(args.dryRun);
    const maxDeletes = Math.max(100, Math.min(50_000, Math.floor(args.maxDeletes ?? 20_000)));
    const maxFileDeletes = Math.max(100, Math.min(50_000, Math.floor(args.maxFileDeletes ?? 10_000)));

    const cutoffMs = Date.now() - WEEK_MS;

    // 说明：
    // - authRefreshTokens/authSessions：来自 @convex-dev/auth，按“只保留最近 7 天”的要求直接清理旧记录。
    // - jobs：异步任务表，保留最近 7 天即可（避免长期堆积）。
    const perTable = Math.max(100, Math.floor(maxDeletes / 3));

    const deletedAuthRefreshTokens = await cleanupByCreationTime(
      ctx,
      "authRefreshTokens",
      cutoffMs,
      perTable,
      dryRun,
    );
    const deletedAuthSessions = await cleanupByCreationTime(ctx, "authSessions", cutoffMs, perTable, dryRun);
    const deletedJobs = await cleanupByCreationTime(ctx, "jobs", cutoffMs, maxDeletes - perTable * 2, dryRun);

    // 说明：清理 Convex Files 孤儿数据（_storage 里“7 天前且无引用”的文件）。
    const files = await cleanupOrphanConvexFiles(ctx, { cutoffMs, maxDeletes: maxFileDeletes, dryRun });

    // 若达到上限，兜底再跑一轮（避免一次删太多导致超时）
    const hitLimit =
      deletedAuthRefreshTokens >= perTable ||
      deletedAuthSessions >= perTable ||
      deletedJobs >= maxDeletes - perTable * 2 ||
      files.hitLimit;
    if (!dryRun && hitLimit) {
      await ctx.scheduler.runAfter(60_000, (internal as any).maintenance.cleanupWeekly, {
        dryRun: false,
        maxDeletes,
        maxFileDeletes,
      });
    }

    return {
      ok: true,
      dryRun,
      cutoffMs,
      deleted: {
        authRefreshTokens: deletedAuthRefreshTokens,
        authSessions: deletedAuthSessions,
        jobs: deletedJobs,
        convexFiles: files.deleted,
      },
      scanned: {
        convexFiles: files.scanned,
      },
      rescheduled: !dryRun && hitLimit,
    };
  },
});
