import dayjs from "dayjs";
import { clickhouseService } from "../../src/services/clients";
import mongoose from "mongoose";
import TotalDoneRate from "../../src/models/totalDoneRateModel"; // 导入 TotalDoneRate 模型 (已包含 totalTipCount)
import DoneRateModel from "../../src/models/doneRateModel"; // 导入 DoneRateModel 用于聚合历史数据

// ClickHouse 表名
const CLICKHOUSE_EVENTS_TABLE = "events"; // 确保与 ClickHouseService 中的表名一致

/**
 * ClickHouse 查询结果接口：每日每个作品的开始、完成和道具使用统计。
 */
interface ClickHouseDoneRateAggregateResult {
  res: string; // 作品 ID
  unique_starts: number; // 独立开始用户数
  unique_dones: number; // 独立完成用户数
  tip_count: number; // 道具使用次数
}

const TOTAL_DONE_RATE_BULK_SIZE = 1000;
const DAILY_DONE_RATE_BULK_SIZE = 1000;

/**
 * 每日统计作品完成率。
 * 如果提供了 dateStr (YYYY-MM-DD 或 YYYYMMDD)，则统计该日的数据；否则，默认统计昨天的数据。
 * 统计逻辑从 ClickHouse 中提取数据，得到每个作品的完成情况、道具使用情况，并更新到 doneRateModel。
 * 随后，通过聚合这些日统计数据，更新本地的 totalDoneRate 表 (包括 totalTipCount)。
 * @param dateStr 可选参数，指定要统计的日期 (例如: '2023-10-25')。
 * @returns Promise<string> - 返回统计结果的摘要信息。
 */
async function run(dateStr?: string | dayjs.Dayjs): Promise<string> {
  console.log("[DoneRate Cron] Starting done-rate calculation...");

  // --- 确定目标日期 ---
  let targetDay: dayjs.Dayjs;

  if (dateStr) {
    targetDay = dayjs(dateStr).startOf("day");
    if (!targetDay.isValid()) {
      throw new Error(`Invalid date format provided: ${dateStr}`);
    }
    console.log(`[DoneRate Cron] Running for specified date: ${targetDay.format("YYYY-MM-DD")}`);
  } else {
    // 默认执行逻辑：统计昨天
    targetDay = dayjs().subtract(1, "day").startOf("day");
    console.log(`[DoneRate Cron] Running for default date (yesterday): ${targetDay.format("YYYY-MM-DD")}`);
  }

  // 使用 targetDay 变量名以兼容后续代码
  const yesterday = targetDay;
  const yesterdayYYYYMMDD = yesterday.format("YYYYMMDD");
  const yesterdayStart = yesterday.toDate();
  const nextDayStart = yesterday.add(1, "day").startOf("day").toDate();

  // 格式化日期字符串，使其符合 ClickHouse 的 toDateTime() 函数要求
  const yesterdayStartString = dayjs(yesterdayStart).format("YYYY-MM-DD HH:mm:ss");
  const nextDayStartString = dayjs(nextDayStart).format("YYYY-MM-DD HH:mm:ss");

  console.log(`[DoneRate Cron] Processing data for date: ${yesterdayYYYYMMDD}`);

  try {
    // --- 1. 从 ClickHouse 中提取数据 (Start, Done, Tip Counts) ---
    const aggregateQuery = `
      SELECT
          res,
          uniqIf(uid, event = 'color_start') AS unique_starts,
          uniqIf(uid, event = 'color_done') AS unique_dones,
          countIf(event = 'color_tip') AS tip_count
      FROM ${CLICKHOUSE_EVENTS_TABLE}
      WHERE event IN ('color_start', 'color_done', 'color_tip')
        AND time >= toDateTime('${yesterdayStartString}')
        AND time < toDateTime('${nextDayStartString}')
      GROUP BY res
      HAVING res IS NOT NULL
    `;
    const clickhouseQueryStartTime = Date.now();
    const aggregateResults = await clickhouseService.queryEvents<ClickHouseDoneRateAggregateResult>(aggregateQuery);
    const clickhouseQueryElapsedMs = Date.now() - clickhouseQueryStartTime;

    const artworkStartCounts = new Map<string, number>();
    const artworkDoneCounts = new Map<string, number>();
    const artworkTipCounts = new Map<string, number>();
    let invalidArtworkRowCount = 0;

    aggregateResults.forEach((row) => {
      if (row.res && mongoose.Types.ObjectId.isValid(row.res)) {
        artworkStartCounts.set(row.res, Number(row.unique_starts));
        artworkDoneCounts.set(row.res, Number(row.unique_dones));
        artworkTipCounts.set(row.res, Number(row.tip_count));
      } else {
        invalidArtworkRowCount++;
        console.warn(`[DoneRate Cron] Invalid artwork ID found in aggregate result: ${row.res}`);
      }
    });

    let totalStartUsers = 0;
    let totalDoneUsers = 0;
    let totalTipEvents = 0;
    aggregateResults.forEach((row) => {
      totalStartUsers += Number(row.unique_starts) || 0;
      totalDoneUsers += Number(row.unique_dones) || 0;
      totalTipEvents += Number(row.tip_count) || 0;
    });

    console.log(
      `[DoneRate Cron] ClickHouse aggregate query completed in ${clickhouseQueryElapsedMs}ms. Rows: ${aggregateResults.length}, valid artworks: ${artworkStartCounts.size}, invalid rows: ${invalidArtworkRowCount}, unique starts: ${totalStartUsers}, unique dones: ${totalDoneUsers}, tip events: ${totalTipEvents}.`,
    );

    // --- 2. 合并数据并批量更新 DoneRate 模型 (每日记录) ---
    let updatedRecordsCount = 0;
    let createdRecordsCount = 0;

    // 获取所有需要处理的作品 ID 集合 (Start + Done + Tip)
    const allResIds = [...new Set([...artworkStartCounts.keys(), ...artworkDoneCounts.keys(), ...artworkTipCounts.keys()])];

    const doneRateBulkStartedAt = Date.now();
    console.log(`[DoneRate Cron] 开始通过 bulkWrite 更新每日 DoneRate。总计 ${allResIds.length} 个作品，batchSize=${DAILY_DONE_RATE_BULK_SIZE}。`);

    for (let i = 0; i < allResIds.length; i += DAILY_DONE_RATE_BULK_SIZE) {
      const chunk = allResIds.slice(i, i + DAILY_DONE_RATE_BULK_SIZE);
      const ops = chunk.map((resIdStr) => {
        const startCount = artworkStartCounts.get(resIdStr) || 0;
        const doneCount = artworkDoneCounts.get(resIdStr) || 0;
        const tipCount = artworkTipCounts.get(resIdStr) || 0;
        const completionRate = startCount > 0 ? (doneCount / startCount) * 100 : 0;
        const resObjectId = new mongoose.Types.ObjectId(resIdStr);

        return {
          updateOne: {
            filter: { date: yesterdayYYYYMMDD, res: resObjectId },
            update: {
              $set: {
                startCount,
                doneCount,
                tipCount,
                completionRate,
              },
              $setOnInsert: {
                date: yesterdayYYYYMMDD,
                res: resObjectId,
              },
            },
            upsert: true,
          },
        };
      });

      const result = await DoneRateModel.bulkWrite(ops, { ordered: false });
      createdRecordsCount += result.upsertedCount || 0;
      updatedRecordsCount += chunk.length - (result.upsertedCount || 0);

      const processed = Math.min(i + DAILY_DONE_RATE_BULK_SIZE, allResIds.length);
      const elapsed = Date.now() - doneRateBulkStartedAt;
      console.log(`[DoneRate Cron] 每日DoneRate进度: 已处理 ${processed}/${allResIds.length} 条，耗时 ${elapsed}ms`);
    }

    const totalProcessedArtworks = createdRecordsCount + updatedRecordsCount;

    console.log(
      `[DoneRate Cron] DoneRate model (Daily) update completed. Total artworks processed: ${totalProcessedArtworks}. Created: ${createdRecordsCount}, Updated: ${updatedRecordsCount}. Elapsed: ${Date.now() - doneRateBulkStartedAt}ms.`,
    );

    // --- 3. 重新聚合并更新本地的 TotalDoneRate 表 (累计记录) ---
    // 通过聚合 DoneRate 日记录来计算最新的累计总数，确保幂等性。

    // 3.1 聚合所有历史 DoneRate 记录直到昨天 (即 targetDay)
    const aggregationPipeline = [
      // 匹配所有小于或等于目标日期的记录
      { $match: { date: { $lte: yesterdayYYYYMMDD } } },
      {
        $group: {
          _id: "$res", // 按作品ID分组
          totalStartCount: { $sum: "$startCount" },
          totalDoneCount: { $sum: "$doneCount" },
          totalTipCount: { $sum: "$tipCount" },
        },
      },
    ];

    const totalAggregateStartedAt = Date.now();
    const aggregatedTotals = await DoneRateModel.aggregate(aggregationPipeline);
    const totalAggregateElapsedMs = Date.now() - totalAggregateStartedAt;

    console.log(`[DoneRate Cron] TotalDoneRate 聚合完成。作品数 ${aggregatedTotals.length}，聚合耗时 ${totalAggregateElapsedMs}ms。`);

    const totalUpdateStartedAt = Date.now();
    let updatedTotalDoneRateCount = 0;

    console.log(`[DoneRate Cron] 开始通过 bulkWrite 更新本地 TotalDoneRate 表。总计 ${aggregatedTotals.length} 个作品，batchSize=${TOTAL_DONE_RATE_BULK_SIZE}。`);

    for (let i = 0; i < aggregatedTotals.length; i += TOTAL_DONE_RATE_BULK_SIZE) {
      const chunk = aggregatedTotals.slice(i, i + TOTAL_DONE_RATE_BULK_SIZE);
      const ops = chunk.map((totalData) => {
        const artworkId = totalData._id;
        const totalStartCount = Number(totalData.totalStartCount) || 0;
        const totalDoneCount = Number(totalData.totalDoneCount) || 0;
        const totalTipCount = Number(totalData.totalTipCount) || 0;
        const completionRate = totalStartCount > 0 ? (totalDoneCount / totalStartCount) * 100 : 0;

        return {
          updateOne: {
            filter: { _id: artworkId },
            update: {
              $set: {
                totalStartCount,
                totalDoneCount,
                totalTipCount,
                completionRate,
              },
            },
            upsert: true,
          },
        };
      });

      await TotalDoneRate.bulkWrite(ops, { ordered: false });
      updatedTotalDoneRateCount += chunk.length;

      const processed = Math.min(i + TOTAL_DONE_RATE_BULK_SIZE, aggregatedTotals.length);
      const elapsed = Date.now() - totalUpdateStartedAt;
      console.log(`[DoneRate Cron] 进度: 已处理 ${processed}/${aggregatedTotals.length} 条 TotalDoneRate，累计耗时 ${elapsed}ms`);
    }

    const totalUpdateElapsedMs = Date.now() - totalUpdateStartedAt;
    console.log(`[DoneRate Cron] 本地 TotalDoneRate 表更新完成。处理记录数 ${aggregatedTotals.length}，写入阶段耗时 ${totalUpdateElapsedMs}ms。`);

    const summary = `[DoneRate Cron] Daily done-rate calculation for ${yesterdayYYYYMMDD} completed. Total DoneRate processed: ${totalProcessedArtworks}. Created DoneRate: ${createdRecordsCount}, Updated DoneRate: ${updatedRecordsCount}. Updated TotalDoneRate records: ${updatedTotalDoneRateCount}.`;
    console.log(summary);
    return summary;
  } catch (error) {
    console.error(`[DoneRate Cron] Error during done-rate calculation for ${yesterdayYYYYMMDD}:`, error);
    // 重新抛出错误，以便 cron 调度器能够捕获并记录失败
    throw new Error(`Failed to calculate daily done-rates for ${yesterdayYYYYMMDD}: ${error instanceof Error ? error.message : String(error)}`);
  } finally {
  }
}

export = { run }; // 导出 run 函数以供 cron-jobs/index.ts 使用

if (require.main === module) {
  run().catch((err) => {
    console.error(err);
    process.exit(1);
  });
}
