"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
// --- Mock data ---
function makeConversation(overrides = {}) {
    return {
        characterId: 'char-1',
        telegramUserId: 100,
        userName: 'TestUser',
        userInfo: { name: 'Test', interests: [], notes: [] },
        messages: [],
        lastActivity: '2024-06-15T12:00:00Z',
        totalMessages: 0,
        totalStarsSpent: 0,
        ...overrides,
    };
}
function makeTransaction(overrides = {}) {
    return {
        id: 'tx-1',
        characterId: 'char-1',
        telegramUserId: 100,
        type: 'payment',
        starCount: 50,
        status: 'completed',
        createdAt: '2024-06-15T14:00:00Z',
        ...overrides,
    };
}
// --- Mocks ---
vitest_1.vi.mock('../../utils/index.js', () => ({
    createModuleLogger: () => ({ info: vitest_1.vi.fn(), warn: vitest_1.vi.fn(), error: vitest_1.vi.fn(), debug: vitest_1.vi.fn() }),
}));
const analyticsStore = new Map();
const mockAtomicWriteJSON = vitest_1.vi.fn(async (path, data) => {
    analyticsStore.set(path, data);
});
const mockReadJSON = vitest_1.vi.fn(async (path) => {
    return analyticsStore.get(path) || null;
});
const mockEnsureDir = vitest_1.vi.fn();
vitest_1.vi.mock('../data-store/file-ops.js', () => ({
    atomicWriteJSON: (path, data) => mockAtomicWriteJSON(path, data),
    readJSON: (path) => mockReadJSON(path),
    ensureDir: (path) => mockEnsureDir(path),
}));
const conversationStore = new Map();
const transactionStore = new Map();
const mockDataStore = {
    dataDir: '/tmp/test-data',
    listConversations: vitest_1.vi.fn(async (charId) => conversationStore.get(charId) || []),
    getStarsTransactions: vitest_1.vi.fn(async (charId) => transactionStore.get(charId) || []),
    listCharacters: vitest_1.vi.fn(async () => [
        { id: 'char-1', status: 'active' },
        { id: 'char-2', status: 'active' },
    ]),
};
vitest_1.vi.mock('node-cron', () => {
    const mockTask = { stop: vitest_1.vi.fn() };
    return {
        default: {
            schedule: vitest_1.vi.fn((_expr, cb) => {
                mockTask._callback = cb;
                return mockTask;
            }),
        },
    };
});
const aggregator_js_1 = require("./aggregator.js");
const index_js_1 = require("./index.js");
const node_cron_1 = __importDefault(require("node-cron"));
let aggregator;
let engine;
(0, vitest_1.beforeEach)(() => {
    aggregator = new aggregator_js_1.AnalyticsAggregator();
    engine = new index_js_1.AnalyticsEngine(mockDataStore);
    analyticsStore.clear();
    conversationStore.clear();
    transactionStore.clear();
    vitest_1.vi.clearAllMocks();
});
// =============================================
// AnalyticsAggregator Tests
// =============================================
(0, vitest_1.describe)('AnalyticsAggregator.aggregateConversations', () => {
    (0, vitest_1.it)('should return zeros for empty conversations', () => {
        const result = aggregator.aggregateConversations([]);
        (0, vitest_1.expect)(result).toEqual({
            totalMessages: 0,
            uniqueUsers: 0,
            averageConversationDuration: 0,
            averageMessagesPerConversation: 0,
        });
    });
    (0, vitest_1.it)('should count total messages across conversations', () => {
        const convs = [
            makeConversation({
                telegramUserId: 100,
                messages: [
                    { id: '1', role: 'user', content: 'hi', timestamp: '2024-06-15T10:00:00Z' },
                    { id: '2', role: 'assistant', content: 'hello', timestamp: '2024-06-15T10:01:00Z' },
                ],
            }),
            makeConversation({
                telegramUserId: 200,
                messages: [
                    { id: '3', role: 'user', content: 'hey', timestamp: '2024-06-15T11:00:00Z' },
                ],
            }),
        ];
        const result = aggregator.aggregateConversations(convs);
        (0, vitest_1.expect)(result.totalMessages).toBe(3);
        (0, vitest_1.expect)(result.uniqueUsers).toBe(2);
        (0, vitest_1.expect)(result.averageMessagesPerConversation).toBe(1.5);
    });
    (0, vitest_1.it)('should calculate average conversation duration in minutes', () => {
        const convs = [
            makeConversation({
                messages: [
                    { id: '1', role: 'user', content: 'hi', timestamp: '2024-06-15T10:00:00Z' },
                    { id: '2', role: 'assistant', content: 'hello', timestamp: '2024-06-15T10:30:00Z' },
                ],
            }),
        ];
        const result = aggregator.aggregateConversations(convs);
        (0, vitest_1.expect)(result.averageConversationDuration).toBe(30);
    });
    (0, vitest_1.it)('should count same user only once for uniqueUsers', () => {
        const convs = [
            makeConversation({ telegramUserId: 100, messages: [{ id: '1', role: 'user', content: 'a', timestamp: '2024-06-15T10:00:00Z' }] }),
            makeConversation({ telegramUserId: 100, messages: [{ id: '2', role: 'user', content: 'b', timestamp: '2024-06-15T11:00:00Z' }] }),
        ];
        const result = aggregator.aggregateConversations(convs);
        (0, vitest_1.expect)(result.uniqueUsers).toBe(1);
    });
});
(0, vitest_1.describe)('AnalyticsAggregator.aggregateSales', () => {
    (0, vitest_1.it)('should return zeros for empty data', () => {
        const result = aggregator.aggregateSales([], []);
        (0, vitest_1.expect)(result).toEqual({
            totalStarsEarned: 0,
            totalTransactions: 0,
            conversionRate: 0,
            averageStarsPerUser: 0,
            topSellingHours: [],
        });
    });
    (0, vitest_1.it)('should sum stars from completed transactions only', () => {
        const txs = [
            makeTransaction({ starCount: 50, status: 'completed' }),
            makeTransaction({ id: 'tx-2', starCount: 30, status: 'pending' }),
            makeTransaction({ id: 'tx-3', starCount: 20, status: 'completed' }),
        ];
        const result = aggregator.aggregateSales(txs, []);
        (0, vitest_1.expect)(result.totalStarsEarned).toBe(70);
        (0, vitest_1.expect)(result.totalTransactions).toBe(2);
    });
    (0, vitest_1.it)('should calculate conversion rate from pitch metadata', () => {
        const convs = [
            makeConversation({
                messages: [
                    { id: '1', role: 'assistant', content: 'pitch', timestamp: '2024-06-15T10:00:00Z', metadata: { starsPitchMade: true } },
                    { id: '2', role: 'user', content: 'ok', timestamp: '2024-06-15T10:01:00Z', metadata: { starsAccepted: true } },
                    { id: '3', role: 'assistant', content: 'pitch2', timestamp: '2024-06-15T11:00:00Z', metadata: { starsPitchMade: true } },
                ],
            }),
        ];
        const result = aggregator.aggregateSales([], convs);
        // 1 accept / 2 pitches = 0.5
        (0, vitest_1.expect)(result.conversionRate).toBe(0.5);
    });
    (0, vitest_1.it)('should calculate average stars per paying user', () => {
        const txs = [
            makeTransaction({ telegramUserId: 100, starCount: 50, status: 'completed' }),
            makeTransaction({ id: 'tx-2', telegramUserId: 100, starCount: 30, status: 'completed' }),
            makeTransaction({ id: 'tx-3', telegramUserId: 200, starCount: 20, status: 'completed' }),
        ];
        const result = aggregator.aggregateSales(txs, []);
        // Total 100 stars / 2 unique users = 50
        (0, vitest_1.expect)(result.averageStarsPerUser).toBe(50);
    });
    (0, vitest_1.it)('should identify top selling hours', () => {
        const txs = [
            makeTransaction({ starCount: 100, createdAt: '2024-06-15T14:00:00Z', status: 'completed' }),
            makeTransaction({ id: 'tx-2', starCount: 50, createdAt: '2024-06-15T14:30:00Z', status: 'completed' }),
            makeTransaction({ id: 'tx-3', starCount: 80, createdAt: '2024-06-15T20:00:00Z', status: 'completed' }),
        ];
        const result = aggregator.aggregateSales(txs, []);
        // Hour 14 = 150 stars, Hour 20 = 80 stars
        (0, vitest_1.expect)(result.topSellingHours[0]).toBe(14);
        (0, vitest_1.expect)(result.topSellingHours[1]).toBe(20);
    });
});
(0, vitest_1.describe)('AnalyticsAggregator.aggregateActivity', () => {
    (0, vitest_1.it)('should return all-zero hourly maps for empty data', () => {
        const result = aggregator.aggregateActivity([], []);
        (0, vitest_1.expect)(Object.keys(result.hourlyMessageCount).length).toBe(24);
        (0, vitest_1.expect)(Object.keys(result.hourlyStarsCount).length).toBe(24);
        for (let h = 0; h < 24; h++) {
            (0, vitest_1.expect)(result.hourlyMessageCount[h]).toBe(0);
            (0, vitest_1.expect)(result.hourlyStarsCount[h]).toBe(0);
        }
        (0, vitest_1.expect)(result.peakHour).toBe(0);
        (0, vitest_1.expect)(result.quietHour).toBe(0);
    });
    (0, vitest_1.it)('should count messages per hour', () => {
        const convs = [
            makeConversation({
                messages: [
                    { id: '1', role: 'user', content: 'a', timestamp: '2024-06-15T10:00:00Z' },
                    { id: '2', role: 'user', content: 'b', timestamp: '2024-06-15T10:30:00Z' },
                    { id: '3', role: 'user', content: 'c', timestamp: '2024-06-15T14:00:00Z' },
                ],
            }),
        ];
        const result = aggregator.aggregateActivity(convs, []);
        (0, vitest_1.expect)(result.hourlyMessageCount[10]).toBe(2);
        (0, vitest_1.expect)(result.hourlyMessageCount[14]).toBe(1);
        (0, vitest_1.expect)(result.peakHour).toBe(10);
    });
    (0, vitest_1.it)('should count stars per hour from completed transactions', () => {
        const txs = [
            makeTransaction({ starCount: 50, createdAt: '2024-06-15T14:00:00Z', status: 'completed' }),
            makeTransaction({ id: 'tx-2', starCount: 30, createdAt: '2024-06-15T14:30:00Z', status: 'completed' }),
            makeTransaction({ id: 'tx-3', starCount: 10, createdAt: '2024-06-15T20:00:00Z', status: 'pending' }),
        ];
        const result = aggregator.aggregateActivity([], txs);
        (0, vitest_1.expect)(result.hourlyStarsCount[14]).toBe(80);
        (0, vitest_1.expect)(result.hourlyStarsCount[20]).toBe(0); // pending excluded
    });
    (0, vitest_1.it)('should identify peak and quiet hours', () => {
        const convs = [
            makeConversation({
                messages: [
                    { id: '1', role: 'user', content: 'a', timestamp: '2024-06-15T08:00:00Z' },
                    { id: '2', role: 'user', content: 'b', timestamp: '2024-06-15T08:30:00Z' },
                    { id: '3', role: 'user', content: 'c', timestamp: '2024-06-15T08:45:00Z' },
                    { id: '4', role: 'user', content: 'd', timestamp: '2024-06-15T15:00:00Z' },
                ],
            }),
        ];
        const result = aggregator.aggregateActivity(convs, []);
        (0, vitest_1.expect)(result.peakHour).toBe(8);
        // quietHour should be one of the hours with 0 messages
        (0, vitest_1.expect)(result.hourlyMessageCount[result.quietHour]).toBe(0);
    });
});
// =============================================
// AnalyticsEngine Tests
// =============================================
(0, vitest_1.describe)('AnalyticsEngine.calculateDailyAnalytics', () => {
    (0, vitest_1.it)('should calculate and store daily analytics', async () => {
        const date = '2024-06-15';
        conversationStore.set('char-1', [
            makeConversation({
                messages: [
                    { id: '1', role: 'user', content: 'hi', timestamp: '2024-06-15T10:00:00Z' },
                    { id: '2', role: 'assistant', content: 'hello', timestamp: '2024-06-15T10:05:00Z' },
                ],
            }),
        ]);
        transactionStore.set('char-1', [
            makeTransaction({ starCount: 50, createdAt: '2024-06-15T14:00:00Z' }),
        ]);
        const result = await engine.calculateDailyAnalytics('char-1', date);
        (0, vitest_1.expect)(result.characterId).toBe('char-1');
        (0, vitest_1.expect)(result.period).toBe('daily');
        (0, vitest_1.expect)(result.date).toBe(date);
        (0, vitest_1.expect)(result.conversations.totalMessages).toBe(2);
        (0, vitest_1.expect)(result.sales.totalStarsEarned).toBe(50);
        (0, vitest_1.expect)(mockAtomicWriteJSON).toHaveBeenCalled();
    });
    (0, vitest_1.it)('should filter messages to the specified date only', async () => {
        conversationStore.set('char-1', [
            makeConversation({
                messages: [
                    { id: '1', role: 'user', content: 'yesterday', timestamp: '2024-06-14T23:00:00Z' },
                    { id: '2', role: 'user', content: 'today', timestamp: '2024-06-15T10:00:00Z' },
                    { id: '3', role: 'user', content: 'tomorrow', timestamp: '2024-06-16T01:00:00Z' },
                ],
            }),
        ]);
        const result = await engine.calculateDailyAnalytics('char-1', '2024-06-15');
        (0, vitest_1.expect)(result.conversations.totalMessages).toBe(1);
    });
    (0, vitest_1.it)('should handle empty data gracefully', async () => {
        const result = await engine.calculateDailyAnalytics('char-1', '2024-06-15');
        (0, vitest_1.expect)(result.conversations.totalMessages).toBe(0);
        (0, vitest_1.expect)(result.sales.totalStarsEarned).toBe(0);
        (0, vitest_1.expect)(result.activity.peakHour).toBe(0);
    });
});
(0, vitest_1.describe)('AnalyticsEngine.calculateWeeklyAnalytics', () => {
    (0, vitest_1.it)('should calculate weekly analytics', async () => {
        conversationStore.set('char-1', [
            makeConversation({
                messages: [
                    { id: '1', role: 'user', content: 'hi', timestamp: new Date().toISOString() },
                ],
            }),
        ]);
        const result = await engine.calculateWeeklyAnalytics('char-1');
        (0, vitest_1.expect)(result.period).toBe('weekly');
        (0, vitest_1.expect)(result.date).toMatch(/^\d{4}-W\d{2}$/);
        (0, vitest_1.expect)(mockAtomicWriteJSON).toHaveBeenCalled();
    });
});
(0, vitest_1.describe)('AnalyticsEngine.calculateMonthlyAnalytics', () => {
    (0, vitest_1.it)('should calculate monthly analytics', async () => {
        conversationStore.set('char-1', [
            makeConversation({
                messages: [
                    { id: '1', role: 'user', content: 'hi', timestamp: new Date().toISOString() },
                ],
            }),
        ]);
        const result = await engine.calculateMonthlyAnalytics('char-1');
        (0, vitest_1.expect)(result.period).toBe('monthly');
        (0, vitest_1.expect)(result.date).toMatch(/^\d{4}-\d{2}$/);
        (0, vitest_1.expect)(mockAtomicWriteJSON).toHaveBeenCalled();
    });
});
(0, vitest_1.describe)('AnalyticsEngine.getCharacterAnalytics', () => {
    (0, vitest_1.it)('should return stored analytics if available', async () => {
        const stored = {
            characterId: 'char-1',
            period: 'daily',
            date: '2024-06-15',
            conversations: { totalMessages: 42, uniqueUsers: 5, averageConversationDuration: 10, averageMessagesPerConversation: 8.4 },
            sales: { totalStarsEarned: 200, totalTransactions: 4, conversionRate: 0.5, averageStarsPerUser: 50, topSellingHours: [14] },
            activity: { hourlyMessageCount: {}, hourlyStarsCount: {}, peakHour: 14, quietHour: 3 },
        };
        // Pre-store analytics so readJSON returns it
        mockReadJSON.mockResolvedValueOnce(stored);
        const result = await engine.getCharacterAnalytics('char-1', 'daily', '2024-06-15');
        (0, vitest_1.expect)(result).toEqual(stored);
    });
    (0, vitest_1.it)('should calculate on-demand if not stored', async () => {
        conversationStore.set('char-1', []);
        const result = await engine.getCharacterAnalytics('char-1', 'daily', '2024-06-15');
        (0, vitest_1.expect)(result.characterId).toBe('char-1');
        (0, vitest_1.expect)(result.period).toBe('daily');
    });
});
(0, vitest_1.describe)('AnalyticsEngine.compareCharacters', () => {
    (0, vitest_1.it)('should return analytics for multiple characters', async () => {
        conversationStore.set('char-1', []);
        conversationStore.set('char-2', []);
        const results = await engine.compareCharacters(['char-1', 'char-2'], 'daily');
        (0, vitest_1.expect)(results).toHaveLength(2);
        (0, vitest_1.expect)(results[0].characterId).toBe('char-1');
        (0, vitest_1.expect)(results[1].characterId).toBe('char-2');
    });
});
(0, vitest_1.describe)('AnalyticsEngine.getSystemOverview', () => {
    (0, vitest_1.it)('should aggregate overview across all characters', async () => {
        conversationStore.set('char-1', [
            makeConversation({
                messages: [
                    { id: '1', role: 'user', content: 'hi', timestamp: '2024-06-15T10:00:00Z' },
                    { id: '2', role: 'assistant', content: 'hello', timestamp: '2024-06-15T10:01:00Z' },
                ],
            }),
        ]);
        conversationStore.set('char-2', [
            makeConversation({
                characterId: 'char-2',
                messages: [
                    { id: '3', role: 'user', content: 'hey', timestamp: '2024-06-15T11:00:00Z' },
                ],
            }),
        ]);
        transactionStore.set('char-1', [
            makeTransaction({ starCount: 100, status: 'completed' }),
        ]);
        transactionStore.set('char-2', [
            makeTransaction({ id: 'tx-2', characterId: 'char-2', starCount: 200, status: 'completed' }),
        ]);
        const overview = await engine.getSystemOverview();
        (0, vitest_1.expect)(overview.totalCharacters).toBe(2);
        (0, vitest_1.expect)(overview.totalMessages).toBe(3);
        (0, vitest_1.expect)(overview.totalStars).toBe(300);
        (0, vitest_1.expect)(overview.topCharacter).toBe('char-2');
    });
    (0, vitest_1.it)('should return undefined topCharacter when no stars earned', async () => {
        conversationStore.set('char-1', []);
        conversationStore.set('char-2', []);
        transactionStore.set('char-1', []);
        transactionStore.set('char-2', []);
        const overview = await engine.getSystemOverview();
        (0, vitest_1.expect)(overview.topCharacter).toBeUndefined();
    });
});
(0, vitest_1.describe)('AnalyticsEngine scheduling', () => {
    (0, vitest_1.it)('should start periodic calculation with cron', () => {
        engine.startPeriodicCalculation('0 0 * * *');
        (0, vitest_1.expect)(node_cron_1.default.schedule).toHaveBeenCalledWith('0 0 * * *', vitest_1.expect.any(Function));
    });
    (0, vitest_1.it)('should use default cron expression when none provided', () => {
        engine.startPeriodicCalculation();
        (0, vitest_1.expect)(node_cron_1.default.schedule).toHaveBeenCalledWith('0 0 * * *', vitest_1.expect.any(Function));
    });
    (0, vitest_1.it)('should stop previous cron task when starting new one', () => {
        engine.startPeriodicCalculation();
        const firstTask = vitest_1.vi.mocked(node_cron_1.default.schedule).mock.results[0].value;
        engine.startPeriodicCalculation('0 */6 * * *');
        (0, vitest_1.expect)(firstTask.stop).toHaveBeenCalled();
    });
    (0, vitest_1.it)('should stop periodic calculation', () => {
        engine.startPeriodicCalculation();
        engine.stopPeriodicCalculation();
        const task = vitest_1.vi.mocked(node_cron_1.default.schedule).mock.results[0].value;
        (0, vitest_1.expect)(task.stop).toHaveBeenCalled();
    });
    (0, vitest_1.it)('should handle stopPeriodicCalculation when not started', () => {
        // Should not throw
        engine.stopPeriodicCalculation();
    });
});
//# sourceMappingURL=index.test.js.map