"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = run;
const mongoose_1 = __importDefault(require("mongoose"));
const userModel_1 = require("../../../src/models/userModel");
// 确保先导入并注册 MessageTemplate 模型
const messageTemplateModel_1 = require("../../../src/models/messageTemplateModel");
// 确保 MessageTemplate 模型已被注册
mongoose_1.default.model("MessageTemplate", messageTemplateModel_1.MessageTemplate.schema);
// 然后再导入和使用 MessageStrategy 模型
const messageStrategyModel_1 = require("../../../src/models/messageStrategyModel");
const messageRecordModel_1 = require("../../../src/models/messageRecordModel");
const fcmService_1 = require("../../../src/services/fcmService");
const artModel_1 = __importDefault(require("../../../src/models/artModel"));
const strategyName = "daily-notify-at-midday";
const fcmService = fcmService_1.FCMService.getInstance();
// 国家代码到语言的映射表
const countryCodeToLanguageMap = {
    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) {
    if (user.lang) {
        return user.lang;
    }
    if (user.cc && countryCodeToLanguageMap[user.cc]) {
        return countryCodeToLanguageMap[user.cc];
    }
    return "en"; // 最终默认语言为英语
}
/**
 * 将多语言模板转换为 FCM 消息数据格式。
 * @param template 消息模板
 * @param userLang 用户的语言代码
 * @param artworkId 画作ID
 */
function getMessageDataFromTemplate(template, userLang, artworkId) {
    // 优先使用用户的语言，如果没有，则回退到默认语言 'en'
    const lang = template.messageContent[userLang] ? userLang : "en";
    // 统一使用 page 路径
    const imageUrl = `https://d1e6q48ob2nxw1.cloudfront.net/thumbs/v2/page/640/${artworkId}.png`;
    return {
        title: template.messageTitle[lang] || template.messageTitle["en"] || "",
        content: template.messageContent[lang] || template.messageContent["en"] || "",
        image: imageUrl,
        bigger: "true",
        action: "go/art",
        param: artworkId,
        extend: template.extend || "",
    };
}
/**
 * 获取今日的用于消息推送的8幅最新画作。
 * 查询 art 表，获取最新的8个作品。
 * @returns 包含八个画作 ID 的数组
 */
async function getTodaysArtworksForFCM() {
    try {
        const artworks = await artModel_1.default.find({}).sort({ publishTime: -1 }).limit(8).lean();
        return artworks.map((art) => art._id.toString());
    }
    catch (error) {
        console.error("查询今日画作失败:", error);
        return [];
    }
}
/**
 * 检查错误是否为 FCM 令牌失效错误。
 * @param error 错误对象
 * @returns 如果是令牌失效错误则返回 true
 */
function isTokenInvalidationError(error) {
    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, cc, fcmToken, template, messageData, strategyId, strategyName) => {
    let messageRecord = null;
    let fcmReceipt = null;
    let messageStatus = 0;
    let errorMessage = null;
    try {
        // 1. 先创建 MessageRecord 记录，状态为 0 (未发送)
        messageRecord = await messageRecordModel_1.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.errorInfo;
        const isInvalidToken = isTokenInvalidationError(error);
        messageStatus = -1;
        errorMessage = errorInfo ? errorInfo.code : error.message;
        if (isInvalidToken) {
            // 如果是无效令牌错误，清空该用户的 fmToken
            await userModel_1.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 messageRecordModel_1.MessageRecord.findByIdAndUpdate(messageRecord._id, {
                status: messageStatus,
                fcmReceipt: fcmReceipt,
                actualSendAt: new Date(),
                errno: errorMessage,
            });
        }
    }
};
/**
 * 脚本的入口方法，用于筛选用户并发送每日FCM通知。
 * 此方法通过cron外部调用。
 *
 * @returns {Promise<void>} 返回一个 Promise，当所有任务（包括定时任务）完成后解决。
 */
async function run() {
    console.log("脚本开始：发送活跃用户每日通知...");
    try {
        const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
        // 筛选条件调整：用户最近12小时不活跃
        const twelveHoursAgo = new Date(Date.now() - 12 * 60 * 60 * 1000);
        // 性能优化：只查询需要的字段（_id, fmToken, lang, cc），避免加载整个文档
        const activeUsers = await userModel_1.User.find({
            lastActiveAt: { $gte: sevenDaysAgo, $lt: twelveHoursAgo }, // 调整筛选条件
            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();
        if (activeUsers.length === 0) {
            console.log("未找到符合条件的用户，脚本结束。");
            return;
        }
        console.log(`找到 ${activeUsers.length} 位活跃用户。`);
        const strategy = await messageStrategyModel_1.MessageStrategy.findOne({ name: strategyName }).populate("templates");
        if (!strategy || !strategy.templates || strategy.templates.length === 0) {
            console.error(`未找到策略 '${strategyName}' 或其绑定的消息模板。`);
            return;
        }
        const templates = strategy.templates;
        console.log(`找到 ${templates.length} 个消息模板。`);
        // 调整为获取最新的8个画作，用于随机选择
        const todaysArtworks = await getTodaysArtworksForFCM();
        if (todaysArtworks.length === 0) {
            console.warn("今日用于FCM消息推送的画作数量不足。脚本结束。");
            return;
        }
        console.log(`今日获取到 ${todaysArtworks.length} 幅最新画作。`);
        // 遍历用户，发送一轮消息
        console.log("\n开始发送第一轮消息...");
        for (const user of activeUsers) {
            const userLang = getUserLanguage(user);
            const fcmToken = user.fmToken;
            // 随机选择一个模板
            const randomIndex = Math.floor(Math.random() * templates.length);
            const template = templates[randomIndex];
            // 从获取的8幅画作中随机选择一个
            const randomArtworkIndex = Math.floor(Math.random() * todaysArtworks.length);
            const artworkId = todaysArtworks[randomArtworkIndex];
            const messageData = getMessageDataFromTemplate(template, userLang, artworkId);
            await sendAndRecordMessage(user.uid, user.cc, fcmToken, template, messageData, strategy._id, strategy.name);
        }
        console.log("消息发送完成。");
    }
    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);
    });
}
