/**
 * monthly-archive-messagerecords.ts
 *
 * 常态化归档 cron job：每月 1 日凌晨 2:30 自动将上个月的 messagerecords 数据
 * 归档到 messagerecords_archive_YYYYMM 集合。
 *
 * 触发条件：上月月初距今 >= HOT_DAYS（180天），才实际归档；否则跳过。
 * 幂等：重跑安全，使用 replaceOne+upsert by _id。
 */

import mongoose from "mongoose";

const HOT_DAYS = 180;
const BATCH_SIZE = 3000;
const MONGO_URI = process.env.MONGO_URI || "mongodb://oms:oms123.@localhost:27717/omsdb?authSource=admin";

export async function run(): Promise<string> {
  const startedAt = Date.now();
  console.log("[ArchiveCron] Starting monthly messagerecords archive job...");

  // 判断上个月是否已超出热数据窗口
  const now = new Date();
  const lastMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1);
  const lastMonthEnd = new Date(now.getFullYear(), now.getMonth(), 1); // 本月月初 = 上月半开区间结束

  const hotCutoff = new Date(now);
  hotCutoff.setDate(hotCutoff.getDate() - HOT_DAYS);
  // 保守：cutoff 取当月月初
  const hotCutoffMonthStart = new Date(hotCutoff.getFullYear(), hotCutoff.getMonth(), 1);

  if (lastMonthStart >= hotCutoffMonthStart) {
    const msg = `[ArchiveCron] Last month (${fmtYYYYMM(lastMonthStart)}) is still within hot window (cutoff=${fmtYYYYMM(hotCutoffMonthStart)}), skip.`;
    console.log(msg);
    return msg;
  }

  // 获取 MongoDB 连接（复用 mongoose 默认连接，cron 环境已初始化）
  let conn = mongoose.connection;
  if (conn.readyState !== 1) {
    // 若默认连接未就绪，创建专用连接
    conn = (await mongoose.createConnection(MONGO_URI).asPromise()) as any;
  }

  const hotCol = conn.collection("messagerecords");
  const yyyymm = fmtYYYYMM(lastMonthStart);
  const archiveCol = conn.collection(`messagerecords_archive_${yyyymm}`);

  const hotCount = await hotCol.countDocuments({ createdAt: { $gte: lastMonthStart, $lt: lastMonthEnd } });
  if (hotCount === 0) {
    const msg = `[ArchiveCron] ${yyyymm}: hotCount=0, nothing to archive.`;
    console.log(msg);
    return msg;
  }

  const archiveCount = await archiveCol.countDocuments({ createdAt: { $gte: lastMonthStart, $lt: lastMonthEnd } });
  if (archiveCount >= hotCount) {
    const msg = `[ArchiveCron] ${yyyymm}: already complete (hot=${hotCount} archive=${archiveCount}), skip.`;
    console.log(msg);
    return msg;
  }

  console.log(`[ArchiveCron] ${yyyymm}: archiving hot=${hotCount} archive=${archiveCount} pending=${hotCount - archiveCount}`);

  // 确保归档集合索引
  await archiveCol.createIndex({ createdAt: 1 }, { background: true });
  await archiveCol.createIndex({ uid: 1 }, { background: true });
  await archiveCol.createIndex({ strategyName: 1, createdAt: 1 }, { background: true });

  let batchInserted = 0;
  let batch: any[] = [];
  const cursor = hotCol.find({ createdAt: { $gte: lastMonthStart, $lt: lastMonthEnd } }).batchSize(BATCH_SIZE);

  for await (const doc of cursor) {
    batch.push(doc);
    if (batch.length >= BATCH_SIZE) {
      const result = await archiveCol.bulkWrite(
        batch.map((d) => ({ replaceOne: { filter: { _id: d._id }, replacement: d, upsert: true } })),
        { ordered: false },
      );
      batchInserted += result.upsertedCount + result.modifiedCount;
      batch = [];
    }
  }

  if (batch.length > 0) {
    const result = await archiveCol.bulkWrite(
      batch.map((d) => ({ replaceOne: { filter: { _id: d._id }, replacement: d, upsert: true } })),
      { ordered: false },
    );
    batchInserted += result.upsertedCount + result.modifiedCount;
  }

  const elapsed = Date.now() - startedAt;

  // 校验
  const newArchiveCount = await archiveCol.countDocuments({ createdAt: { $gte: lastMonthStart, $lt: lastMonthEnd } });
  const verified = newArchiveCount >= hotCount;
  console.log(`[ArchiveCron] ${yyyymm}: inserted/updated=${batchInserted} archiveTotal=${newArchiveCount} verified=${verified} elapsed=${elapsed}ms`);

  if (!verified) {
    const msg = `[ArchiveCron] ${yyyymm}: WARN archive incomplete (hot=${hotCount} archive=${newArchiveCount}). Will retry next run.`;
    console.warn(msg);
    return msg;
  }

  // 归档验证通过后自动清理热表中该月数据
  const deleteResult = await hotCol.deleteMany({ createdAt: { $gte: lastMonthStart, $lt: lastMonthEnd } });
  console.log(`[ArchiveCron] ${yyyymm}: purged hot table deleted=${deleteResult.deletedCount} elapsed=${Date.now() - startedAt}ms`);

  const msg = `[ArchiveCron] ${yyyymm}: archive+purge complete. archived=${batchInserted} purged=${deleteResult.deletedCount} elapsed=${Date.now() - startedAt}ms`;
  console.log(msg);
  return msg;
}

function fmtYYYYMM(d: Date): string {
  return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}`;
}
