import assert from 'assert';
import path from 'path';
import { fileURLToPath, pathToFileURL } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const logic = await import(pathToFileURL(path.join(__dirname, '..', '..', 'web-ui', 'logic.mjs')));
const { buildUsageChartGroups, buildUsageHeatmap, buildUsageHourlyHeatmap } = logic;

test('buildUsageChartGroups aggregates codex and claude sessions into day buckets', () => {
    const now = Date.UTC(2026, 3, 6, 12, 0, 0);
    const result = buildUsageChartGroups([
        { source: 'codex', model: 'gpt-5.3-codex', createdAt: '2026-04-06T07:30:00.000Z', updatedAt: '2026-04-06T08:00:00.000Z', messageCount: 5, totalTokens: 120, contextWindow: 32000, cwd: '/a' },
        { source: 'claude', model: 'claude-sonnet-4', createdAt: '2026-04-06T08:15:00.000Z', updatedAt: '2026-04-06T09:00:00.000Z', messageCount: 7, totalTokens: 230, contextWindow: 64000, cwd: '/a' },
        { source: 'codex', model: 'gpt-5.2-codex', createdAt: '2026-04-05T08:00:00.000Z', updatedAt: '2026-04-05T09:00:00.000Z', messageCount: 3, totalTokens: 90, contextWindow: 16000, cwd: '/b' }
    ], { range: '7d', now });

    assert.strictEqual(result.summary.totalSessions, 3);
    assert.strictEqual(result.summary.totalMessages, 15);
    assert.strictEqual(result.summary.totalTokens, 440);
    assert.strictEqual(result.summary.totalContextWindow, 112000);
    assert.strictEqual(result.summary.activeDurationMs, 8100000);
    assert.strictEqual(result.summary.totalDurationMs, 90000000);
    assert.strictEqual(result.summary.codexTotal, 2);
    assert.strictEqual(result.summary.claudeTotal, 1);
    assert.strictEqual(result.summary.avgMessagesPerSession, 5);
    assert.strictEqual(result.summary.busiestDay.label, '04-06');
    assert.strictEqual(result.summary.busiestDay.totalSessions, 2);
    assert.strictEqual(result.sourceShare.find(item => item.key === 'codex').percent, 67);
    assert.strictEqual(result.sourceShare.find(item => item.key === 'codex').messageTotal, 8);
    assert.strictEqual(result.sourceShare.find(item => item.key === 'claude').avgMessages, 7);
    assert.strictEqual(result.topPaths[0].path, '/a');
    assert.strictEqual(result.topPaths[0].count, 2);
    assert.strictEqual(result.topPaths[0].messageTotal, 12);
    assert.strictEqual(result.recentSessions[0].title, '未命名会话');
    assert.strictEqual(result.recentSessions[0].sourceLabel, 'Claude Code');
    assert.strictEqual(result.topSessionsByMessages[0].messageCount, 7);
    assert.strictEqual(result.hourActivity.find(item => item.key === '09').count, 2);
    assert.strictEqual(result.weekdayActivity.find(item => item.label === '周一').count, 2);
    const lastBucket = result.buckets[result.buckets.length - 1];
    assert.strictEqual(lastBucket.codex, 1);
    assert.strictEqual(lastBucket.claude, 1);
    assert.strictEqual(lastBucket.totalMessages, 12);
});

test('buildUsageChartGroups ignores invalid sessions and keeps empty buckets stable', () => {
    const now = Date.UTC(2026, 3, 6, 12, 0, 0);
    const result = buildUsageChartGroups([
        null,
        { source: 'other', updatedAt: '2026-04-06T08:00:00.000Z', messageCount: 9 },
        { source: 'codex', updatedAt: 'bad-date', messageCount: 2, totalTokens: 50, contextWindow: 1000 }
    ], { range: '7d', now });

    assert.strictEqual(result.summary.totalSessions, 0);
    assert.strictEqual(result.summary.totalMessages, 0);
    assert.strictEqual(result.summary.totalTokens, 0);
    assert.strictEqual(result.summary.totalContextWindow, 0);
    assert.strictEqual(result.buckets.length, 7);
    assert.ok(result.buckets.every((item) => item.totalSessions === 0));
    assert.ok(result.hourActivity.every((item) => item.count === 0));
    assert.ok(result.weekdayActivity.every((item) => item.count === 0));
    assert.deepStrictEqual(result.recentSessions, []);
});

test('buildUsageChartGroups produces stable unique keys for sessions without ids', () => {
    const now = Date.UTC(2026, 3, 6, 12, 0, 0);
    const result = buildUsageChartGroups([
        { source: 'codex', model: 'gpt-5.3-codex', updatedAt: '2026-04-06T09:00:00.000Z', messageCount: 4 },
        { source: 'codex', model: 'gpt-5.2-codex', updatedAt: '2026-04-06T09:00:00.000Z', messageCount: 4 },
        { source: 'claude', model: 'claude-sonnet-4', updatedAt: '2026-04-06T09:00:00.000Z', messageCount: 4 }
    ], { range: '7d', now });

    const recentKeys = result.recentSessions.map((item) => item.key);
    const topKeys = result.topSessionsByMessages.map((item) => item.key);

    assert.strictEqual(new Set(recentKeys).size, recentKeys.length);
    assert.strictEqual(new Set(topKeys).size, topKeys.length);
});

test('buildUsageChartGroups supports all range and keeps every valid session in filteredSessions', () => {
    const now = Date.UTC(2026, 3, 6, 12, 0, 0);
    const sessions = [
        { source: 'codex', model: 'gpt-5.3-codex', createdAt: '2026-04-06T07:30:00.000Z', updatedAt: '2026-04-06T08:00:00.000Z', messageCount: 5, totalTokens: 120, contextWindow: 32000, cwd: '/a' },
        { source: 'claude', model: 'claude-sonnet-4', createdAt: '2026-03-01T08:15:00.000Z', updatedAt: '2026-03-01T09:00:00.000Z', messageCount: 7, totalTokens: 230, contextWindow: 64000, cwd: '/b' }
    ];
    const result = buildUsageChartGroups(sessions, { range: 'all', now });

    assert.strictEqual(result.range, 'all');
    assert.strictEqual(result.summary.totalSessions, 2);
    assert.strictEqual(result.filteredSessions.length, 2);
    assert.strictEqual(result.buckets[0].key, '2026-03-01');
    assert.strictEqual(result.buckets[result.buckets.length - 1].key, '2026-04-06');
});

test('buildUsageHeatmap aligns to monday and aggregates sessions per day', () => {
    const now = Date.UTC(2026, 3, 6, 12, 0, 0);
    const result = buildUsageHeatmap([
        { source: 'codex', updatedAt: '2026-04-06T08:00:00.000Z', messageCount: 5, totalTokens: 120 },
        { source: 'claude', updatedAt: '2026-04-06T09:00:00.000Z', messageCount: 7, totalTokens: 230 },
        { source: 'codex', updatedAt: '2026-04-05T09:00:00.000Z', messageCount: 3, totalTokens: 90 }
    ], { range: '7d', now });

    assert.strictEqual(result.range, '7d');
    assert.ok(Array.isArray(result.weeks));
    assert.ok(result.weeks.length >= 1);
    assert.ok(result.maxSessionCount >= 2);
    const hasDay = result.weeks.some((week) => Array.isArray(week.days) && week.days.some((cell) => cell && cell.dateKey === '2026-04-06' && cell.sessionCount === 2));
    assert.ok(hasDay);
});

test('buildUsageHourlyHeatmap produces 7x24 grid with correct aggregation', () => {
    const now = Date.UTC(2026, 3, 6, 12, 0, 0);
    const result = buildUsageHourlyHeatmap([
        { source: 'codex', updatedAt: '2026-04-06T08:00:00.000Z', messageCount: 5, totalTokens: 120 },
        { source: 'claude', updatedAt: '2026-04-06T08:30:00.000Z', messageCount: 7, totalTokens: 230 },
        { source: 'codex', updatedAt: '2026-04-05T14:00:00.000Z', messageCount: 3, totalTokens: 90 }
    ], { range: '7d', now });

    assert.strictEqual(result.range, '7d');
    assert.strictEqual(result.grid.length, 7);
    assert.strictEqual(result.grid[0].length, 24);
    assert.strictEqual(result.weekdayKeys.length, 7);
    assert.strictEqual(result.hourLabels.length, 24);

    const april6Dow = (new Date(Date.UTC(2026, 3, 6)).getUTCDay() + 6) % 7;
    assert.strictEqual(result.grid[april6Dow][8].sessionCount, 2);
    assert.strictEqual(result.grid[april6Dow][8].messageCount, 12);
    assert.strictEqual(result.grid[april6Dow][8].tokenTotal, 350);
    assert.ok(result.maxSessionCount >= 2);
});

test('buildUsageHourlyHeatmap ignores sessions outside range', () => {
    const now = Date.UTC(2026, 3, 6, 12, 0, 0);
    const result = buildUsageHourlyHeatmap([
        { source: 'codex', updatedAt: '2026-04-06T08:00:00.000Z', messageCount: 5, totalTokens: 120 },
        { source: 'codex', updatedAt: '2026-03-01T08:00:00.000Z', messageCount: 99, totalTokens: 999 }
    ], { range: '7d', now });

    let totalSessions = 0;
    for (const row of result.grid) {
        for (const cell of row) {
            totalSessions += cell.sessionCount;
        }
    }
    assert.strictEqual(totalSessions, 1);
});

test('buildUsageHourlyHeatmap returns empty grid for no sessions', () => {
    const result = buildUsageHourlyHeatmap([], { range: '7d' });
    assert.strictEqual(result.grid.length, 7);
    assert.strictEqual(result.grid[0].length, 24);
    assert.strictEqual(result.maxSessionCount, 1);
});
