import mongoose, { Schema, Document } from "mongoose";
import { User, IUser } from "../../src/models/userModel";
import { IMessageTemplate, MessageTemplate } from "../../src/models/messageTemplateModel";
mongoose.model("MessageTemplate", MessageTemplate.schema);
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";
import { shuffle } from "lodash"; // 使用 lodash 的 shuffle 函数来打乱数组

// 定义两个不同的策略名称
const immediateStrategyName = "active_new_content_notify";
const scheduledStrategyName = "notify-by-last-active";
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 || "",
  };
}

/**
 * 获取今日的用于消息推送的1幅画作。
 * 查询 art 表，查找 tags 标签等于 'fcm' 的记录，按 publishTime 倒序排，取头部的1个 art。
 * @returns 包含一个画作 ID 的数组 [art_id1]
 */
async function getTodaysArtworksForFCM(): Promise<string[]> {
  try {
    const artworks = await Art.find({ tags: "fcm" }).sort({ publishTime: -1 }).limit(1).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 API发送。
 * @param uid 用户ID
 * @param cc 用户国家代码
 * @param fcmToken 用户的FCM Token
 * @param template 消息模板
 * @param messageData 消息数据
 * @param strategyId 消息策略ID
 * @param strategyName 消息策略名称
 */
const sendImmediateMessageAndRecord = 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 {
    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,
    });

    const finalMessageData = {
      ...messageData,
      msgid: messageRecord._id.toString(),
    };

    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) {
      await User.findOneAndUpdate({ uid: uid }, { fmToken: null });
      console.warn(`[FCM] 检测到无效令牌，自动清除 UID ${uid} 的 fmToken。`);
      errorMessage += " (Token cleared)";
    } else {
      console.error(`发送消息给用户 ${uid} 失败:`, error);
    }
  } finally {
    if (messageRecord) {
      await MessageRecord.findByIdAndUpdate(messageRecord._id, {
        status: messageStatus,
        fcmReceipt: fcmReceipt as any,
        actualSendAt: new Date(),
        errno: errorMessage,
      });
    }
  }
};

/**
 * 创建定时消息记录。
 * 此函数只负责创建数据库记录，不进行FCM发送。
 * @param user 用户对象
 * @param template 消息模板
 * @param messageData 消息数据
 * @param strategyId 消息策略ID
 * @param strategyName 消息策略名称
 * @param plannedSendAt 计划发送时间
 */
const createScheduledMessageRecord = async (
  user: IUser,
  template: IMessageTemplate,
  messageData: { [key: string]: string },
  strategyId: mongoose.Types.ObjectId,
  strategyName: string,
  plannedSendAt: Date
) => {
  try {
    await MessageRecord.create({
      uid: user.uid,
      cc: user.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: plannedSendAt,
      // status 保持默认的 0（未发送）
      status: 0,
    });
    console.log(`成功为用户 ${user.uid} 创建定时推送记录，计划发送时间: ${plannedSendAt.toISOString()}`);
  } catch (error) {
    console.error(`为用户 ${user.uid} 创建定时推送记录失败:`, error);
  }
};

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

  try {
    // 1. 获取所有需要的策略和模板
    const immediateStrategy = await MessageStrategy.findOne({ name: immediateStrategyName }).populate("templates");
    const scheduledStrategy = await MessageStrategy.findOne({ name: scheduledStrategyName }).populate("templates");

    if (!immediateStrategy || !immediateStrategy.templates || immediateStrategy.templates.length < 1) {
      console.error(`未找到即时策略 '${immediateStrategyName}' 或其绑定的消息模板。`);
      return;
    }
    if (!scheduledStrategy || !scheduledStrategy.templates || scheduledStrategy.templates.length < 1) {
      console.error(`未找到定时策略 '${scheduledStrategyName}' 或其绑定的消息模板。`);
      return;
    }

    const immediateTemplates = immediateStrategy.templates as IMessageTemplate[];
    const scheduledTemplates = scheduledStrategy.templates as IMessageTemplate[];

    // 2. 获取今日画作
    const todaysArtworks = await getTodaysArtworksForFCM();
    if (todaysArtworks.length === 0) {
      console.warn("今日用于FCM消息推送的画作数量为0。脚本结束。");
      return;
    }
    const artworkId = todaysArtworks[0];
    console.log(`今日画作ID：${artworkId}`);

    // 3. 筛选并分发用户
    const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
    const activeUsers = await User.find({
      lastActiveAt: { $gte: sevenDaysAgo },
      fmToken: { $nin: [null, ""] },
      $or: [{ versionCode: { $gte: 347 } }, { versionCode: -1 }],
    })
      .select("_id uid fmToken lang cc lastActiveAt")
      .lean<IUser[]>();

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

    // 随机打乱用户列表以进行 A/B 测试
    const shuffledUsers = shuffle(activeUsers);
    const splitIndex = Math.floor(shuffledUsers.length / 2);
    const immediateUsers = shuffledUsers.slice(0, splitIndex);
    const scheduledUsers = shuffledUsers.slice(splitIndex);

    console.log(`分为两组：即时发送组 ${immediateUsers.length} 人，定时发送组 ${scheduledUsers.length} 人。`);

    // 4. 发送第一批消息 (即时发送组)
    console.log("\n开始处理即时发送组...");
    for (const user of immediateUsers) {
      const userLang = getUserLanguage(user);
      const fcmToken = user.fmToken as string;
      const template = immediateTemplates[Math.floor(Math.random() * immediateTemplates.length)];
      const data = getMessageDataFromTemplate(template, userLang);

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

      data.bigger = "true";
      data.action = "go/art";
      data.param = artworkId;

      await sendImmediateMessageAndRecord(user.uid, user.cc, fcmToken, template, data, immediateStrategy._id, immediateStrategy.name);
    }
    console.log("即时发送组处理完成。");

    // 5. 创建第二批消息记录 (定时发送组)
    console.log("\n开始为定时发送组创建任务记录...");
    for (const user of scheduledUsers) {
      const userLang = getUserLanguage(user);
      const template = scheduledTemplates[Math.floor(Math.random() * scheduledTemplates.length)];
      const data = getMessageDataFromTemplate(template, userLang);

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

      data.bigger = "true";
      data.action = "go/art";
      data.param = artworkId;

      // 根据今天的日期和用户的 lastActiveAt 时间计算 plannedSendAt
      const now = new Date();
      const lastActiveAt = new Date(user.lastActiveAt!);

      const plannedSendAt = new Date(now.getFullYear(), now.getMonth(), now.getDate());
      plannedSendAt.setHours(lastActiveAt.getHours() - 1);
      plannedSendAt.setMinutes(lastActiveAt.getMinutes());
      plannedSendAt.setSeconds(lastActiveAt.getSeconds());
      plannedSendAt.setMilliseconds(lastActiveAt.getMilliseconds());

      // 如果计算出的时间已在过去，则推迟到第二天
      if (plannedSendAt.getTime() < now.getTime()) {
        plannedSendAt.setDate(plannedSendAt.getDate() + 1);
      }

      await createScheduledMessageRecord(user, template, data, scheduledStrategy._id, scheduledStrategy.name, plannedSendAt);
    }
    console.log("定时发送组任务记录创建完成。");
  } catch (error) {
    console.error("脚本执行过程中发生致命错误:", error);
    throw error;
  }
}

if (require.main === module) {
  run()
    .then(() => {
      console.log("脚本执行完毕，退出进程。");
      process.exit(0);
    })
    .catch((err) => {
      console.error("脚本执行失败:", err);
      process.exit(1);
    });
}
