/**
 * purge-messagerecords-hot.ts
 *
 * 从 messagerecords 热表删除已完整归档的历史数据。
 *
 * 安全要求（双重确认）：
 *   --execute    声明要真正执行
 *   --confirmed  在执行前打印操作计划后，再次用此参数确认
 *
 * 用法：
 *   # Step 1: 查看将要删除的月份和估算行数（dry-run，不删除）
 *   npx ts-node src/scripts/purge-messagerecords-hot.ts
 *
 *   # Step 2: 确认 verify 全部通过后，执行删除
 *   npx ts-node src/scripts/purge-messagerecords-hot.ts --execute --confirmed
 *
 *   # 仅删除指定月份
 *   npx ts-node src/scripts/purge-messagerecords-hot.ts --execute --confirmed --month 2024-06
 *
 * 内置保护：
 *   - 热表中仍有数据但无对应归档集合的月份，拒绝删除
 *   - 归档数 < 热表数的月份，拒绝删除（提示先运行 archive）
 *   - 近 HOT_DAYS 天的数据（热数据），即使有归档集合也拒绝删除
 */

import mongoose, { Connection } from "mongoose";

const MONGO_URI = process.env.MONGO_URI || "mongodb://oms:oms123.@localhost:27717/omsdb?authSource=admin";
const HOT_DAYS = 180;
const DELETE_BATCH_SIZE = 1000; // deleteMany 单次处理的上限，用时间窗切分

const isExecute = process.argv.includes("--execute");
const isConfirmed = process.argv.includes("--confirmed");

const monthArg = (() => {
  const idx = process.argv.indexOf("--month");
  return idx !== -1 ? process.argv[idx + 1] : null;
})();

// ---------- 工具 ----------
function parseArchiveMonthFromName(name: string): string | null {
  const m = name.match(/^messagerecords_archive_(\d{6})$/);
  return m ? m[1] : null;
}

function yyyymmToRange(yyyymm: string): { start: Date; end: Date } {
  const year = parseInt(yyyymm.slice(0, 4), 10);
  const month = parseInt(yyyymm.slice(4, 6), 10) - 1; // 0-indexed
  return {
    start: new Date(year, month, 1),
    end: new Date(year, month + 1, 1),
  };
}

function calcHotCutoffStart(): Date {
  const d = new Date();
  d.setDate(d.getDate() - HOT_DAYS);
  // 保守取当月月初，整月视为热数据
  return new Date(d.getFullYear(), d.getMonth(), 1);
}

// ---------- 主流程 ----------
async function run() {
  const dryRun = !(isExecute && isConfirmed);
  console.log(`[Purge] mode=${dryRun ? "DRY-RUN" : "EXECUTE"} hotDays=${HOT_DAYS}`);

  if (isExecute && !isConfirmed) {
    console.warn("[Purge] WARNING: --execute requires --confirmed as a second confirmation. Running in DRY-RUN mode.");
  }

  const conn = await mongoose.createConnection(MONGO_URI).asPromise();
  console.log("[Purge] Connected to MongoDB.");

  const hotCol = conn.collection("messagerecords");
  const hotCutoff = calcHotCutoffStart();

  // 获取所有 archive 集合
  const allCollections = await conn.db.listCollections().toArray();
  const archiveMap = new Map<string, string>(); // yyyymm -> collectionName
  for (const c of allCollections) {
    const yyyymm = parseArchiveMonthFromName(c.name);
    if (yyyymm) archiveMap.set(yyyymm, c.name);
  }

  // 找出热表中所有有数据、且早于 hotCutoff 的月份
  // 用聚合按月分组统计
  const pipeline = [
    { $match: { createdAt: { $lt: hotCutoff } } },
    {
      $group: {
        _id: { $dateToString: { format: "%Y%m", date: "$createdAt" } },
        count: { $sum: 1 },
      },
    },
    { $sort: { _id: 1 } },
  ];

  console.log("[Purge] Scanning hot table for purgeable months (this may take a moment)...");
  const hotMonths: Array<{ yyyymm: string; hotCount: number }> = [];
  const cursor = hotCol.aggregate(pipeline, { allowDiskUse: true, maxTimeMS: 120000 });
  for await (const row of cursor) {
    hotMonths.push({ yyyymm: row._id as string, hotCount: row.count as number });
  }

  if (hotMonths.length === 0) {
    console.log("[Purge] No purgeable data found in hot table (all within hot window or already purged).");
    await conn.close();
    process.exit(0);
  }

  // 过滤单月
  let targetMonths = hotMonths;
  if (monthArg) {
    const yyyymm = monthArg.replace("-", "");
    targetMonths = hotMonths.filter((m) => m.yyyymm === yyyymm);
    if (targetMonths.length === 0) {
      console.error(`[Purge] ERROR: month ${monthArg} has no purgeable data in hot table.`);
      await conn.close();
      process.exit(1);
    }
  }

  // 预检：每个目标月份都必须有完整归档
  console.log(`\n[Purge] Pre-check: verifying archive completeness for ${targetMonths.length} month(s)...`);
  const blockers: string[] = [];
  const plan: Array<{ yyyymm: string; hotCount: number; archiveCount: number }> = [];

  for (const { yyyymm, hotCount } of targetMonths) {
    const { start, end } = yyyymmToRange(yyyymm);
    if (!archiveMap.has(yyyymm)) {
      console.log(`[Purge] ${yyyymm}: NO ARCHIVE COLLECTION → BLOCKED`);
      blockers.push(yyyymm);
      continue;
    }
    const archiveCount = await conn.collection(archiveMap.get(yyyymm)!).countDocuments({ createdAt: { $gte: start, $lt: end } });
    if (archiveCount < hotCount) {
      console.log(`[Purge] ${yyyymm}: INCOMPLETE (hot=${hotCount} archive=${archiveCount}) → BLOCKED`);
      blockers.push(yyyymm);
    } else {
      console.log(`[Purge] ${yyyymm}: OK (hot=${hotCount.toLocaleString()} archive=${archiveCount.toLocaleString()})`);
      plan.push({ yyyymm, hotCount, archiveCount });
    }
  }

  if (blockers.length > 0) {
    console.error(`\n[Purge] ABORT: ${blockers.length} month(s) blocked (${blockers.join(", ")}). Run archive-messagerecords.ts --execute first.`);
    await conn.close();
    process.exit(1);
  }

  // 打印执行计划
  const totalRows = plan.reduce((s, p) => s + p.hotCount, 0);
  console.log(`\n[Purge] PLAN: will delete ${totalRows.toLocaleString()} rows from messagerecords across ${plan.length} month(s):`);
  for (const { yyyymm, hotCount } of plan) {
    console.log(`  ${yyyymm}: ${hotCount.toLocaleString()} rows`);
  }

  if (dryRun) {
    console.log("\n[Purge] DRY-RUN: no data deleted. Re-run with --execute --confirmed to apply.");
    await conn.close();
    process.exit(0);
  }

  // 执行删除
  console.log("\n[Purge] Starting deletion...");
  let totalDeleted = 0;

  for (const { yyyymm, hotCount } of plan) {
    const { start, end } = yyyymmToRange(yyyymm);
    const t0 = Date.now();
    const result = await hotCol.deleteMany({ createdAt: { $gte: start, $lt: end } });
    const elapsed = Date.now() - t0;
    console.log(`[Purge] ${yyyymm}: deleted=${result.deletedCount.toLocaleString()} (expected≈${hotCount.toLocaleString()}) elapsed=${elapsed}ms`);
    totalDeleted += result.deletedCount;
  }

  console.log(`\n[Purge] DONE: total deleted=${totalDeleted.toLocaleString()} rows from messagerecords hot table.`);
  console.log("[Purge] Recommend running verify-messagerecords-archive.ts again to confirm integrity.");

  await conn.close();
}

run().catch((err) => {
  console.error("[Purge] FATAL:", err);
  process.exit(1);
});
