"use strict";
// oms/scripts/ingestFCMTokens.ts
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// Load environment variables for database credentials
const dotenv = __importStar(require("dotenv"));
dotenv.config();
const mongoose_1 = __importDefault(require("mongoose")); // Mongoose for OMS MongoDB models
const dayjs_1 = __importDefault(require("dayjs")); // For date manipulation
const duration_1 = __importDefault(require("dayjs/plugin/duration")); // dayjs plugin for duration
const userModel_1 = require("../models/userModel"); // OMS User Model
// Node.js built-in modules for file processing
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const zlib = __importStar(require("zlib"));
const readline = __importStar(require("readline"));
// --- IMPORTANT FIX: Extend dayjs with the duration plugin ---
dayjs_1.default.extend(duration_1.default);
// --- Persistent Configuration (can still come from .env) ---
const OMS_MONGO_URI = process.env.MONGO_URI || "mongodb://oms:oms123.@localhost:27717/omsdb?authSource=admin";
// --- Batching Configuration ---
const MONGO_BATCH_SIZE = 1000; // MongoDB 批量写入大小
// --- Initialize Services ---
async function initializeServices() {
    try {
        // Connect to OMS MongoDB (using Mongoose)
        await mongoose_1.default.connect(OMS_MONGO_URI);
        console.log(`[Script] Connected to OMS MongoDB: ${OMS_MONGO_URI}`);
    }
    catch (error) {
        console.error("[Script] Failed to initialize services:", error);
        process.exit(1); // Exit if essential services cannot connect
    }
}
/**
 * Historical Data Ingestion Logic for FCM Tokens
 * @param logFilesDir The directory containing the .log.gz files.
 */
async function ingestFCMTokens(logFilesDir) {
    const overallStartTime = (0, dayjs_1.default)();
    console.log(`[Script] Starting FCM token ingestion from local files in: ${logFilesDir}`);
    let totalProcessedEvents = 0;
    let totalUpdatedUsers = 0;
    const mongoUserWriteOperations = []; // Array of Mongoose bulkWrite operations
    // Get all files from the directory and filter for .log.gz files
    let allFiles = [];
    try {
        allFiles = fs
            .readdirSync(logFilesDir)
            .filter((file) => file.endsWith(".log.gz"))
            .sort(); // Sort files to ensure chronological processing
    }
    catch (readDirError) {
        console.error(`[Script] Error reading directory '${logFilesDir}':`, readDirError);
        return;
    }
    const totalFiles = allFiles.length;
    if (totalFiles === 0) {
        console.log("[Script] No .log.gz files found in the specified directory. Exiting.");
        return;
    }
    console.log(`[Script] Found ${totalFiles} .log.gz files to process.`);
    for (let i = 0; i < totalFiles; i++) {
        const expectedFilename = allFiles[i];
        const filePath = path.join(logFilesDir, expectedFilename);
        const fileStartTime = (0, dayjs_1.default)();
        console.log(`\n--- Processing file ${i + 1}/${totalFiles}: ${expectedFilename} ---`);
        let processedEventsCount = 0;
        let updatedUsersCount = 0;
        const fileStream = fs.createReadStream(filePath);
        const gunzip = zlib.createGunzip();
        const rl = readline.createInterface({
            input: fileStream.pipe(gunzip),
            crlfDelay: Infinity, // Handle Windows/Unix line endings
        });
        try {
            for await (const line of rl) {
                if (!line.trim())
                    continue; // Skip empty lines
                let eventLog;
                try {
                    eventLog = JSON.parse(line);
                }
                catch (parseError) {
                    console.error(`[Script] Error parsing JSON from line in '${expectedFilename}': ${line}. Error: ${parseError}`);
                    continue; // Skip malformed JSON lines
                }
                const projectId = eventLog.project_id;
                const uid = projectId === 1 ? eventLog.uid : eventLog.user_id;
                const eventType = projectId === 1 ? eventLog.type : eventLog.name;
                const fmToken = eventLog.token;
                // --- Filter for the specific event type and ensure required fields exist ---
                if (eventType === "firebase_message_token" && uid && fmToken) {
                    // Add update operation to the batch
                    mongoUserWriteOperations.push({
                        updateOne: {
                            filter: { uid: uid },
                            update: {
                                $set: { fmToken: fmToken },
                            },
                            // We only want to update existing users, not create new ones.
                            // If we wanted to create new users, we would use upsert: true.
                            upsert: false,
                        },
                    });
                    processedEventsCount++;
                }
                // Execute batch write if batch size is reached
                if (mongoUserWriteOperations.length >= MONGO_BATCH_SIZE) {
                    try {
                        const bulkResult = await userModel_1.User.bulkWrite(mongoUserWriteOperations, { ordered: false });
                        updatedUsersCount += (bulkResult.upsertedCount || 0) + (bulkResult.modifiedCount || 0);
                        console.log(`[Script] MongoDB batch written. Matched/Modified: ${bulkResult.matchedCount}/${bulkResult.modifiedCount}.`);
                    }
                    catch (bulkError) {
                        console.error(`[Script] Error in MongoDB bulkWrite for file '${expectedFilename}':`, bulkError);
                    }
                    mongoUserWriteOperations.length = 0; // Clear buffer
                }
            } // End of for await (const line of rl) loop
            // --- Flush any remaining buffers after file processing ---
            if (mongoUserWriteOperations.length > 0) {
                try {
                    const bulkResult = await userModel_1.User.bulkWrite(mongoUserWriteOperations, { ordered: false });
                    updatedUsersCount += (bulkResult.upsertedCount || 0) + (bulkResult.modifiedCount || 0);
                    console.log(`[Script] MongoDB final batch written. Matched/Modified: ${bulkResult.matchedCount}/${bulkResult.modifiedCount}.`);
                }
                catch (bulkError) {
                    console.error(`[Script] Error in final MongoDB bulkWrite for file '${expectedFilename}':`, bulkError);
                }
                mongoUserWriteOperations.length = 0; // Clear buffer
            }
            totalProcessedEvents += processedEventsCount;
            totalUpdatedUsers += updatedUsersCount;
            const fileDuration = dayjs_1.default.duration((0, dayjs_1.default)().diff(fileStartTime)).asSeconds();
            console.log(`[Script] File '${expectedFilename}' processed: ${processedEventsCount} events, ${updatedUsersCount} users updated. Time taken: ${fileDuration.toFixed(2)} seconds.`);
        }
        catch (error) {
            console.error(`[Script] Error processing file '${expectedFilename}':`, error);
        }
    } // End of for...of allFiles loop
    const overallDuration = dayjs_1.default.duration((0, dayjs_1.default)().diff(overallStartTime)).asSeconds();
    console.log(`\n--- FCM token ingestion complete ---`);
    console.log(`Total processed events: ${totalProcessedEvents}`);
    console.log(`Total users with updated FCM tokens: ${totalUpdatedUsers}`);
    console.log(`Total time taken: ${overallDuration.toFixed(2)} seconds.`);
}
// --- Main execution ---
async function main() {
    await initializeServices(); // Initialize MongoDB connection
    // Use the second command-line argument for the log file directory
    const logFilesDirectory = process.argv[2] || path.join(__dirname, "../../../../../logs/");
    await ingestFCMTokens(logFilesDirectory);
    // Ensure mongoose connection is properly closed only if it was connected
    if (mongoose_1.default.connection.readyState === 1)
        await mongoose_1.default.disconnect();
    console.log("[Script] Database connections closed.");
    process.exit(0);
}
main().catch(console.error);
