"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnalyticsEngine = void 0;
const path_1 = require("path");
const node_cron_1 = __importDefault(require("node-cron"));
const index_js_1 = require("../../utils/index.js");
const file_ops_js_1 = require("../data-store/file-ops.js");
const aggregator_js_1 = require("./aggregator.js");
const log = (0, index_js_1.createModuleLogger)('analytics');
class AnalyticsEngine {
    dataStore;
    aggregator;
    cronTask = null;
    constructor(dataStore) {
        this.dataStore = dataStore;
        this.aggregator = new aggregator_js_1.AnalyticsAggregator();
    }
    /**
     * Get analytics for a character, loading from stored file or calculating fresh.
     */
    async getCharacterAnalytics(characterId, period, date) {
        const dateStr = date || this.getDateString(period);
        const filePath = this.getAnalyticsPath(characterId, period, dateStr);
        const existing = await (0, file_ops_js_1.readJSON)(filePath);
        if (existing)
            return existing;
        // Calculate on-demand if not stored
        if (period === 'daily')
            return this.calculateDailyAnalytics(characterId, dateStr);
        if (period === 'weekly')
            return this.calculateWeeklyAnalytics(characterId);
        return this.calculateMonthlyAnalytics(characterId);
    }
    /**
     * Calculate and store daily analytics for a character.
     */
    async calculateDailyAnalytics(characterId, date) {
        const dateStr = date || this.getDateString('daily');
        const conversations = await this.dataStore.listConversations(characterId);
        const transactions = await this.dataStore.getStarsTransactions(characterId);
        // Filter conversations and transactions for the given date
        const dayStart = new Date(`${dateStr}T00:00:00Z`);
        const dayEnd = new Date(`${dateStr}T23:59:59.999Z`);
        const filteredConversations = conversations.map(conv => ({
            ...conv,
            messages: conv.messages.filter(m => {
                const ts = new Date(m.timestamp).getTime();
                return ts >= dayStart.getTime() && ts <= dayEnd.getTime();
            }),
        })).filter(conv => conv.messages.length > 0);
        const filteredTransactions = transactions.filter(tx => {
            const ts = new Date(tx.createdAt).getTime();
            return ts >= dayStart.getTime() && ts <= dayEnd.getTime();
        });
        const analytics = {
            characterId,
            period: 'daily',
            date: dateStr,
            conversations: this.aggregator.aggregateConversations(filteredConversations),
            sales: this.aggregator.aggregateSales(filteredTransactions, filteredConversations),
            activity: this.aggregator.aggregateActivity(filteredConversations, filteredTransactions),
        };
        await this.storeAnalytics(analytics);
        log.info({ characterId, date: dateStr }, 'Daily analytics calculated');
        return analytics;
    }
    /**
     * Calculate and store weekly analytics for a character.
     */
    async calculateWeeklyAnalytics(characterId) {
        const now = new Date();
        const weekStart = new Date(now);
        weekStart.setDate(now.getDate() - now.getDay());
        weekStart.setHours(0, 0, 0, 0);
        const weekEnd = new Date(weekStart);
        weekEnd.setDate(weekStart.getDate() + 6);
        weekEnd.setHours(23, 59, 59, 999);
        const year = weekStart.getFullYear();
        const weekNum = this.getWeekNumber(weekStart);
        const dateStr = `${year}-W${String(weekNum).padStart(2, '0')}`;
        const conversations = await this.dataStore.listConversations(characterId);
        const transactions = await this.dataStore.getStarsTransactions(characterId);
        const filteredConversations = conversations.map(conv => ({
            ...conv,
            messages: conv.messages.filter(m => {
                const ts = new Date(m.timestamp).getTime();
                return ts >= weekStart.getTime() && ts <= weekEnd.getTime();
            }),
        })).filter(conv => conv.messages.length > 0);
        const filteredTransactions = transactions.filter(tx => {
            const ts = new Date(tx.createdAt).getTime();
            return ts >= weekStart.getTime() && ts <= weekEnd.getTime();
        });
        const analytics = {
            characterId,
            period: 'weekly',
            date: dateStr,
            conversations: this.aggregator.aggregateConversations(filteredConversations),
            sales: this.aggregator.aggregateSales(filteredTransactions, filteredConversations),
            activity: this.aggregator.aggregateActivity(filteredConversations, filteredTransactions),
        };
        await this.storeAnalytics(analytics);
        log.info({ characterId, date: dateStr }, 'Weekly analytics calculated');
        return analytics;
    }
    /**
     * Calculate and store monthly analytics for a character.
     */
    async calculateMonthlyAnalytics(characterId) {
        const now = new Date();
        const year = now.getFullYear();
        const month = now.getMonth();
        const monthStart = new Date(year, month, 1, 0, 0, 0, 0);
        const monthEnd = new Date(year, month + 1, 0, 23, 59, 59, 999);
        const dateStr = `${year}-${String(month + 1).padStart(2, '0')}`;
        const conversations = await this.dataStore.listConversations(characterId);
        const transactions = await this.dataStore.getStarsTransactions(characterId);
        const filteredConversations = conversations.map(conv => ({
            ...conv,
            messages: conv.messages.filter(m => {
                const ts = new Date(m.timestamp).getTime();
                return ts >= monthStart.getTime() && ts <= monthEnd.getTime();
            }),
        })).filter(conv => conv.messages.length > 0);
        const filteredTransactions = transactions.filter(tx => {
            const ts = new Date(tx.createdAt).getTime();
            return ts >= monthStart.getTime() && ts <= monthEnd.getTime();
        });
        const analytics = {
            characterId,
            period: 'monthly',
            date: dateStr,
            conversations: this.aggregator.aggregateConversations(filteredConversations),
            sales: this.aggregator.aggregateSales(filteredTransactions, filteredConversations),
            activity: this.aggregator.aggregateActivity(filteredConversations, filteredTransactions),
        };
        await this.storeAnalytics(analytics);
        log.info({ characterId, date: dateStr }, 'Monthly analytics calculated');
        return analytics;
    }
    /**
     * Compare analytics across multiple characters for a given period.
     */
    async compareCharacters(characterIds, period) {
        const results = [];
        for (const id of characterIds) {
            const analytics = await this.getCharacterAnalytics(id, period);
            results.push(analytics);
        }
        return results;
    }
    /**
     * Get a system-wide overview of all characters.
     */
    async getSystemOverview() {
        const characters = await this.dataStore.listCharacters();
        let totalMessages = 0;
        let totalStars = 0;
        let topCharacter;
        let topStars = 0;
        for (const char of characters) {
            const conversations = await this.dataStore.listConversations(char.id);
            const transactions = await this.dataStore.getStarsTransactions(char.id);
            const charMessages = conversations.reduce((sum, c) => sum + c.messages.length, 0);
            const charStars = transactions
                .filter(tx => tx.status === 'completed')
                .reduce((sum, tx) => sum + tx.starCount, 0);
            totalMessages += charMessages;
            totalStars += charStars;
            if (charStars > topStars) {
                topStars = charStars;
                topCharacter = char.id;
            }
        }
        return {
            totalCharacters: characters.length,
            totalMessages,
            totalStars,
            topCharacter: topStars > 0 ? topCharacter : undefined,
        };
    }
    /**
     * Start periodic analytics calculation using node-cron.
     * Default: daily at midnight.
     */
    startPeriodicCalculation(cronExpression) {
        if (this.cronTask) {
            this.cronTask.stop();
        }
        const expression = cronExpression || '0 0 * * *'; // default: daily at midnight
        this.cronTask = node_cron_1.default.schedule(expression, async () => {
            log.info('Running periodic analytics calculation');
            try {
                const characters = await this.dataStore.listCharacters();
                for (const char of characters) {
                    await this.calculateDailyAnalytics(char.id);
                }
                log.info('Periodic analytics calculation completed');
            }
            catch (err) {
                log.error({ err }, 'Periodic analytics calculation failed');
            }
        });
        log.info({ cronExpression: expression }, 'Periodic analytics calculation started');
    }
    /**
     * Stop periodic analytics calculation.
     */
    stopPeriodicCalculation() {
        if (this.cronTask) {
            this.cronTask.stop();
            this.cronTask = null;
            log.info('Periodic analytics calculation stopped');
        }
    }
    // --- Private helpers ---
    async storeAnalytics(analytics) {
        const filePath = this.getAnalyticsPath(analytics.characterId, analytics.period, analytics.date);
        await (0, file_ops_js_1.ensureDir)((0, path_1.resolve)(filePath, '..'));
        await (0, file_ops_js_1.atomicWriteJSON)(filePath, analytics);
    }
    getAnalyticsPath(characterId, period, dateStr) {
        const baseDir = this.dataStore.dataDir || 'data';
        return (0, path_1.resolve)(baseDir, 'analytics', characterId, period, `${dateStr}.json`);
    }
    getDateString(period) {
        const now = new Date();
        if (period === 'daily') {
            return now.toISOString().split('T')[0];
        }
        if (period === 'weekly') {
            const weekNum = this.getWeekNumber(now);
            return `${now.getFullYear()}-W${String(weekNum).padStart(2, '0')}`;
        }
        return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
    }
    getWeekNumber(date) {
        const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
        const dayNum = d.getUTCDay() || 7;
        d.setUTCDate(d.getUTCDate() + 4 - dayNum);
        const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
        return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
    }
}
exports.AnalyticsEngine = AnalyticsEngine;
//# sourceMappingURL=index.js.map