"use strict";
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;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendChallengeDayReminders = exports.unlockDueMemoryCapsules = void 0;
const functions = __importStar(require("firebase-functions"));
const admin = __importStar(require("firebase-admin"));
const DAY_MS = 24 * 60 * 60 * 1000;
const CHALLENGE_TITLES = {
    gratitude_week: { title: 'Gratitude Week', durationDays: 7 },
    appreciation_notes: { title: 'Appreciation Notes', durationDays: 7 },
    quality_time: { title: 'Quality Time', durationDays: 7 },
    deep_conversations: { title: 'Deep Conversations', durationDays: 7 },
};
exports.unlockDueMemoryCapsules = functions.pubsub
    .schedule('every 1 hours')
    .onRun(async () => {
    const db = admin.firestore();
    const messaging = admin.messaging();
    const now = Date.now();
    const snapshot = await db
        .collectionGroup('capsules')
        .where('status', '==', 'sealed')
        .where('unlockAt', '<=', now)
        .limit(100)
        .get();
    const notifications = [];
    for (const capsuleDoc of snapshot.docs) {
        const capsuleRef = capsuleDoc.ref;
        const coupleRef = capsuleRef.parent.parent;
        if (!coupleRef)
            continue;
        const capsuleNotifications = await db.runTransaction(async (tx) => {
            var _a, _b, _c, _d;
            const freshCapsule = await tx.get(capsuleRef);
            const capsule = (_a = freshCapsule.data()) !== null && _a !== void 0 ? _a : {};
            if (capsule.status !== 'sealed' || Number((_b = capsule.unlockAt) !== null && _b !== void 0 ? _b : 0) > now) {
                return [];
            }
            const coupleDoc = await tx.get(coupleRef);
            const userIds = ((_d = (_c = coupleDoc.data()) === null || _c === void 0 ? void 0 : _c.userIds) !== null && _d !== void 0 ? _d : []);
            if (userIds.length === 0)
                return [];
            tx.update(capsuleRef, {
                status: 'unlocked',
                unlockedAt: now,
                unlockNotifiedAt: admin.firestore.FieldValue.serverTimestamp(),
            });
            const capsuleId = capsuleRef.id;
            const coupleId = coupleRef.id;
            const title = typeof capsule.title === 'string' && capsule.title.trim().length > 0
                ? capsule.title.trim()
                : 'A memory capsule';
            return userIds.map((userId) => ({
                userId,
                type: 'memory_capsule_unlocked',
                title: 'Your memory capsule opened',
                body: `${title} is ready to read together.`,
                data: { couple_id: coupleId, capsule_id: capsuleId },
            }));
        });
        notifications.push(...capsuleNotifications);
    }
    await Promise.all(notifications.map((notification) => sendNotification(db, messaging, notification)));
    console.log(`[unlockDueMemoryCapsules] unlocked ${snapshot.size}; notified ${notifications.length}`);
});
exports.sendChallengeDayReminders = functions.pubsub
    .schedule('every 24 hours')
    .onRun(async () => {
    const db = admin.firestore();
    const messaging = admin.messaging();
    const now = Date.now();
    const snapshot = await db
        .collectionGroup('challenges')
        .where('status', '==', 'active')
        .limit(100)
        .get();
    const notifications = [];
    for (const challengeDoc of snapshot.docs) {
        const challengeRef = challengeDoc.ref;
        const coupleRef = challengeRef.parent.parent;
        if (!coupleRef)
            continue;
        const challengeNotifications = await db.runTransaction(async (tx) => {
            var _a, _b, _c, _d, _e, _f, _g;
            const freshChallenge = await tx.get(challengeRef);
            const challenge = (_a = freshChallenge.data()) !== null && _a !== void 0 ? _a : {};
            if (challenge.status !== 'active')
                return [];
            const startedAt = Number((_b = challenge.startedAt) !== null && _b !== void 0 ? _b : 0);
            if (startedAt <= 0 || startedAt > now)
                return [];
            const challengeId = typeof challenge.challengeId === 'string'
                ? challenge.challengeId
                : challengeRef.id;
            const catalogEntry = (_c = CHALLENGE_TITLES[challengeId]) !== null && _c !== void 0 ? _c : {
                title: 'Connection Challenge',
                durationDays: 7,
            };
            const day = Math.floor((now - startedAt) / DAY_MS) + 1;
            if (day < 1 || day > catalogEntry.durationDays)
                return [];
            const coupleDoc = await tx.get(coupleRef);
            const userIds = ((_e = (_d = coupleDoc.data()) === null || _d === void 0 ? void 0 : _d.userIds) !== null && _e !== void 0 ? _e : []);
            if (userIds.length === 0)
                return [];
            const completions = ((_f = challenge.completions) !== null && _f !== void 0 ? _f : {});
            const reminderSent = ((_g = challenge.challengeReminderSent) !== null && _g !== void 0 ? _g : {});
            const dueUserIds = userIds.filter((userId) => {
                var _a;
                const completedDays = (_a = completions[userId]) !== null && _a !== void 0 ? _a : [];
                const alreadyCompleted = completedDays.map(Number).includes(day);
                const alreadySent = reminderSent[reminderKey(userId, day)] === true;
                return !alreadyCompleted && !alreadySent;
            });
            if (dueUserIds.length === 0)
                return [];
            const updates = {
                lastChallengeReminderAt: admin.firestore.FieldValue.serverTimestamp(),
            };
            dueUserIds.forEach((userId) => {
                updates[`challengeReminderSent.${reminderKey(userId, day)}`] = true;
            });
            tx.update(challengeRef, updates);
            const coupleId = coupleRef.id;
            return dueUserIds.map((userId) => ({
                userId,
                type: 'challenge_day_ready',
                title: `Day ${day} is ready`,
                body: `${catalogEntry.title}: today's connection prompt is waiting.`,
                data: { couple_id: coupleId, challenge_id: challengeId, day: String(day) },
            }));
        });
        notifications.push(...challengeNotifications);
    }
    await Promise.all(notifications.map((notification) => sendNotification(db, messaging, notification)));
    console.log(`[sendChallengeDayReminders] scanned ${snapshot.size}; notified ${notifications.length}`);
});
function reminderKey(userId, day) {
    return `${userId.replace(/[^\w-]/g, '_')}_${day}`;
}
async function sendNotification(db, messaging, notification) {
    await db
        .collection('users')
        .doc(notification.userId)
        .collection('notification_queue')
        .add({
        type: notification.type,
        title: notification.title,
        body: notification.body,
        read: false,
        createdAt: admin.firestore.FieldValue.serverTimestamp(),
    });
    const tokens = await getUserTokens(db, notification.userId);
    if (tokens.length === 0) {
        console.log(`[sendNotification] no FCM tokens for ${notification.userId}`);
        return;
    }
    const message = {
        token: tokens[0],
        notification: {
            title: notification.title,
            body: notification.body,
        },
        data: Object.assign({ type: notification.type }, notification.data),
    };
    const sendResults = await Promise.allSettled(tokens.map((token) => messaging.send(Object.assign(Object.assign({}, message), { token }))));
    const failures = [];
    sendResults.forEach((result, index) => {
        if (result.status === 'rejected') {
            failures.push(`${tokens[index]}: ${String(result.reason)}`);
        }
    });
    if (failures.length > 0) {
        console.error(`[sendNotification] some notifications failed:`, failures);
    }
}
async function getUserTokens(db, userId) {
    var _a;
    const tokens = [];
    const userDoc = await db.collection('users').doc(userId).get();
    const legacyToken = (_a = userDoc.data()) === null || _a === void 0 ? void 0 : _a.fcmToken;
    if (typeof legacyToken === 'string' && legacyToken.length > 0) {
        tokens.push(legacyToken);
    }
    const tokenSnapshot = await db
        .collection('users')
        .doc(userId)
        .collection('fcmTokens')
        .get();
    tokenSnapshot.docs.forEach((doc) => {
        var _a;
        const token = (_a = doc.data()) === null || _a === void 0 ? void 0 : _a.token;
        if (typeof token === 'string' && token.length > 0 && !tokens.includes(token)) {
            tokens.push(token);
        }
    });
    return tokens;
}
//# sourceMappingURL=gameRetention.js.map