import mongoose, { Schema, Document } from "mongoose";
import { User, IUser } from "../../src/models/userModel";

// 确保先导入并注册 MessageTemplate 模型
import { IMessageTemplate, MessageTemplate } from "../../src/models/messageTemplateModel";
// 确保 MessageTemplate 模型已被注册
mongoose.model("MessageTemplate", MessageTemplate.schema);

// 然后再导入和使用 MessageStrategy 模型
import { MessageStrategy, IMessageStrategy } from "../../src/models/messageStrategyModel";

import { MessageRecord, IMessageRecord } from "../../src/models/messageRecordModel";
import { FCMService } from "../../src/services/fcmService";
import Art, { IArt } from "../../src/models/artModel";

const strategyName = "active_new_content_notify";
const fcmService = FCMService.getInstance();

// 国家代码到语言的映射表
const countryCodeToLanguageMap: { [key: string]: string } = {
  CN: "zh-cn",
  US: "en",
  JP: "ja",
  FR: "fr",
  DE: "de",
  ES: "es", // Spain
  MX: "es", // Mexico
  CL: "es", // Chile
  BR: "pt", // Brazil
  RU: "ru", // Russia
  IN: "hi", // India
  ID: "id", // Indonesia
  IT: "it",
  KR: "ko",
  TH: "th",
  TR: "tr",
  VN: "vi",
};

/**
 * 根据用户的 lang 或 cc 字段推断其语言。
 * @param user 用户对象
 * @returns 推断出的语言代码
 */
function getUserLanguage(user: IUser): string {
  if (user.lang) {
    return user.lang;
  }
  if (user.cc && countryCodeToLanguageMap[user.cc]) {
    return countryCodeToLanguageMap[user.cc];
  }
  return "en"; // 最终默认语言为英语
}

/**
 * 将多语言模板转换为 FCM 消息数据格式。
 * @param template 消息模板
 * @param userLang 用户的语言代码
 */
function getMessageDataFromTemplate(template: IMessageTemplate, userLang: string): { [key: string]: string } {
  // 优先使用用户的语言，如果没有，则回退到默认语言 'en'
  const lang = template.messageContent[userLang] ? userLang : "en";

  return {
    title: template.messageTitle[lang] || template.messageTitle["en"] || "",
    content: template.messageContent[lang] || template.messageContent["en"] || "",
    image: template.image || "",
    bigger: String(template.bigger || false),
    action: template.action || "go/app",
    param: template.param || "",
    extend: template.extend || "",
  };
}

/**
 * 获取今日的用于消息推送的2幅画作。
 * 查询 art 表，查找 tags 标签等于 'fcm' 的记录，按 publishTime 倒序排，取头部的2个 art。
 * @returns 包含两个画作 ID 的数组 [art_id1, art_id2]
 */
async function getTodaysArtworksForFCM(): Promise<string[]> {
  try {
    const artworks = await Art.find({ tags: "fcm" }).sort({ publishTime: -1 }).limit(2).lean<IArt[]>();

    return artworks.map((art) => art._id.toString());
  } catch (error) {
    console.error("查询今日画作失败:", error);
    return [];
  }
}

/**
 * 检查错误是否为 FCM 令牌失效错误。
 * @param error 错误对象
 * @returns 如果是令牌失效错误则返回 true
 */
function isTokenInvalidationError(error: any): boolean {
  return error && (error.code === "messaging/registration-token-not-registered" || error.code === "messaging/invalid-registration-token");
}

/**
 * 发送并记录FCM消息。
 * 此函数现在将首先创建数据库记录，然后使用该记录的 _id 作为 msgid 进行发送。
 * @param uid 用户ID
 * @param cc 用户国家代码
 * @param fcmToken 用户的FCM Token
 * @param template 消息模板
 * @param messageData 消息数据
 * @param strategyId 消息策略ID
 * @param strategyName 消息策略名称
 */
const sendAndRecordMessage = async (
  uid: string,
  cc: string | undefined,
  fcmToken: string,
  template: IMessageTemplate,
  messageData: { [key: string]: string },
  strategyId: mongoose.Types.ObjectId,
  strategyName: string
) => {
  let messageRecord: IMessageRecord | null = null;
  let fcmReceipt = null;
  let messageStatus = 0;
  let errorMessage = null;

  try {
    // 1. 先创建 MessageRecord 记录，状态为 0 (未发送)
    messageRecord = await MessageRecord.create({
      uid: uid,
      cc: cc,
      templateId: template._id,
      templateName: template.templateName,
      strategyId: strategyId,
      strategyName: strategyName,
      title: messageData.title,
      content: messageData.content,
      image: messageData.image,
      bigger: messageData.bigger === "true",
      action: messageData.action,
      param: messageData.param,
      extend: messageData.extend,
      plannedSendAt: new Date(),
      status: 0,
    });

    // 2. 将记录的 _id 添加到消息数据中作为 msgid
    const finalMessageData = {
      ...messageData,
      msgid: messageRecord._id.toString(),
    };

    // 3. 尝试发送消息
    const sendResult = await fcmService.sendMessage(fcmToken, finalMessageData);
    if (sendResult instanceof Error) {
      throw sendResult;
    }
    fcmReceipt = sendResult;
    messageStatus = 1;

    console.log(`成功发送消息给用户 ${uid}。`);
  } catch (error) {
    const errorInfo = (error as any).errorInfo;
    const isInvalidToken = isTokenInvalidationError(error);

    messageStatus = -1;
    errorMessage = errorInfo ? errorInfo.code : (error as Error).message;

    if (isInvalidToken) {
      // 如果是无效令牌错误，清空该用户的 fmToken
      await User.findOneAndUpdate({ uid: uid }, { fmToken: null });
      console.warn(`[FCM] 检测到无效令牌，自动清除 UID ${uid} 的 fmToken。`);
      errorMessage += " (Token cleared)";
    } else {
      console.error(`发送消息给用户 ${uid} 失败:`, error);
    }
  } finally {
    // 4. 不论成功与否，更新 MessageRecord 的状态和结果
    if (messageRecord) {
      await MessageRecord.findByIdAndUpdate(messageRecord._id, {
        status: messageStatus,
        fcmReceipt: fcmReceipt as any,
        actualSendAt: new Date(),
        errno: errorMessage,
      });
    }
  }
};

/**
 * 脚本的入口方法，用于筛选用户并发送每日FCM通知。
 * 此方法通过cron外部调用。
 *
 * @returns {Promise<void>} 返回一个 Promise，当所有任务（包括定时任务）完成后解决。
 */
export async function run(): Promise<void> {
  console.log("脚本开始：发送活跃用户每日通知...");

  try {
    const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);

    // 性能优化：只查询需要的字段（_id, fmToken, lang, cc），避免加载整个文档
    const activeUsers = await User.find({
      lastActiveAt: { $gte: sevenDaysAgo },
      fmToken: { $nin: [null, ""] },
      //   versionName: { $in: ["5.8.0-debug"] },
      // versionName: { $in: ["5.8.0", "5.8.0-debug"] },
      // versionCode: { $gte: 347 },
      $or: [{ versionCode: { $gte: 347 } }, { versionCode: -1 }],
    })
      .select("_id uid fmToken lang cc")
      .lean<IUser[]>();

    if (activeUsers.length === 0) {
      console.log("未找到符合条件的用户，脚本结束。");
      return;
    }
    console.log(`找到 ${activeUsers.length} 位活跃用户。`);

    const strategy = await MessageStrategy.findOne({ name: strategyName }).populate("templates");
    if (!strategy || !strategy.templates || strategy.templates.length < 2) {
      console.error(`未找到策略 '${strategyName}' 或其绑定的消息模板，或模板数量不足2个。`);
      return;
    }
    const templates = strategy.templates as IMessageTemplate[];
    console.log(`找到 ${templates.length} 个消息模板。`);

    const todaysArtworks = await getTodaysArtworksForFCM();
    if (todaysArtworks.length < 2) {
      console.warn("今日用于FCM消息推送的画作数量不足2个，无法执行双消息策略。脚本结束。");
      return;
    }
    const artwork1Id = todaysArtworks[0];
    const artwork2Id = todaysArtworks[1];
    console.log(`今日画作ID：${artwork1Id} 和 ${artwork2Id}`);

    // 为每个用户预先选择并存储两条消息的模板
    const messagesToSend = activeUsers.map((user) => {
      const randomIndex1 = Math.floor(Math.random() * templates.length);
      let randomIndex2 = Math.floor(Math.random() * templates.length);
      while (randomIndex2 === randomIndex1) {
        randomIndex2 = Math.floor(Math.random() * templates.length);
      }
      return {
        user,
        template1: templates[randomIndex1],
        template2: templates[randomIndex2],
      };
    });

    // --- 立即发送第一批消息 ---
    console.log("\n开始发送第一批消息...");
    for (const messageData of messagesToSend) {
      const user = messageData.user;
      const userLang = getUserLanguage(user);
      const fcmToken = user.fmToken as string;

      const data1 = getMessageDataFromTemplate(messageData.template1, userLang);

      // 添加随机选择逻辑，随机选择 fcm 或 page 路径
      const isFcmPath = Math.random() < 0.5;
      data1.image = isFcmPath ? `https://d1e6q48ob2nxw1.cloudfront.net/thumbs/v2/fcm/640/${artwork1Id}.png` : `https://d1e6q48ob2nxw1.cloudfront.net/thumbs/v2/page/640/${artwork1Id}.png`;

      data1.bigger = "true";
      data1.action = "go/art";
      data1.param = artwork1Id;

      await sendAndRecordMessage(user.uid, user.cc, fcmToken, messageData.template1, data1, strategy._id, strategy.name);
    }
    console.log("第一批消息发送完成。");

    // 返回一个 Promise，该 Promise 将在 30 分钟后执行并完成所有后续操作
    // return new Promise<void>((resolve) => {
    //   setTimeout(async () => {
    //     console.log("\n定时任务触发：开始发送第二批消息...");
    //     for (const messageData of messagesToSend) {
    //       const user = messageData.user;
    //       const userLang = getUserLanguage(user);
    //       const fcmToken = user.fmToken as string;

    //       const data2 = getMessageDataFromTemplate(messageData.template2, userLang);
    //       data2.image = `https://d1e6q48ob2nxw1.cloudfront.net/thumbs/v2/page/640/${artwork2Id}.png`;
    //       data2.bigger = "true";
    //       data2.action = "go/art";
    //       data2.param = artwork2Id;

    //       await sendAndRecordMessage(user.uid, fcmToken, messageData.template2, data2, strategy._id, strategy.name);
    //     }
    //     console.log("第二批消息发送完成。");

    //     // 所有任务完成后，安全地断开数据库连接
    //     await disconnectFromDatabase();
    //     resolve();
    //   }, 30 * 60 * 1000);
    // });
  } catch (error) {
    console.error("脚本执行过程中发生致命错误:", error);
    throw error;
  }
}

// 这个 if 块确保只有在直接运行此文件时才调用 run() 函数
if (require.main === module) {
  run()
    .then(() => {
      console.log("脚本执行完毕，退出进程。");
      process.exit(0);
    })
    .catch((err) => {
      console.error("脚本执行失败:", err);
      process.exit(1);
    });
}
