/**
 * analytics-retention-split smoke — asserts the retention-spanning analytics
 * surfaces aggregate across BOTH storage tiers (raw Heartbeat + HeartbeatHourly),
 * not raw alone.
 *
 * Why (2026-05-31): raw Heartbeat is retained only RAW_RETENTION_DAYS; older
 * data lives only in HeartbeatHourly (RollupService / /api/cron/cleanup). The
 * heatmap, the analytics KPI/trend/insights services and the downtime report all
 * queried raw directly over windows up to 90 days, so every day past the boundary
 * went blank / 0% once cleanup runs. The fix routes them through the boundary-
 * split helpers in analytics-aggregation.ts. This smoke statically guards that
 * wiring so a future edit can't silently regress to raw-only and re-blank the
 * 90-day views.
 */
import './_lib';
import { ok, fail } from './_lib';
import { readFileSync } from 'fs';
import path from 'path';

const ROOT = path.resolve(__dirname, '../..');
const read = (rel: string): string => readFileSync(path.join(ROOT, rel), 'utf8');

function assert(label: string, cond: boolean, detail: string): void {
    if (!cond) fail(label, detail);
    ok(label);
}

function main(): void {
    // 1. Single source of truth for the boundary, and the cleanup cron must use
    //    the SAME value — drift would split the tiers at the wrong instant.
    const retention = read('src/lib/services/retention.ts');
    const retMatch = retention.match(/RAW_RETENTION_DAYS\s*=\s*(\d+)/);
    assert('retention.ts defines RAW_RETENTION_DAYS', !!retMatch,
        'src/lib/services/retention.ts no longer exports a numeric RAW_RETENTION_DAYS.');
    const retDays = Number(retMatch![1]);

    const cleanup = read('src/app/api/cron/cleanup/route.ts');
    const cleanupMatch = cleanup.match(/RAW_RETENTION_DAYS\s*=\s*(\d+)/);
    assert('cleanup cron retention matches retention.ts', !!cleanupMatch && Number(cleanupMatch![1]) === retDays,
        `cleanup route RAW_RETENTION_DAYS (${cleanupMatch?.[1]}) != retention.ts (${retDays}) — tiers would split at the wrong boundary.`);

    // 2. The aggregation module must read the hourly rollup on the older side.
    const aggMod = read('src/lib/services/analytics-aggregation.ts');
    assert('analytics-aggregation queries HeartbeatHourly', /FROM HeartbeatHourly/.test(aggMod),
        'analytics-aggregation.ts no longer reads HeartbeatHourly — older windows will be empty.');
    assert('analytics-aggregation splits at rawBoundary', /rawBoundary\(\)/.test(aggMod),
        'analytics-aggregation.ts no longer splits at rawBoundary() — raw + hourly would overlap or truncate.');
    for (const fn of ['dailyAgg', 'rangeAggScoped', 'trendAgg', 'failureCountByMonitor']) {
        assert(`analytics-aggregation exports ${fn}`, new RegExp(`export\\s+(async\\s+)?function\\s+${fn}\\b`).test(aggMod),
            `analytics-aggregation.ts no longer exports ${fn}.`);
    }

    // 3. Heatmap route goes through the daily combiner, not a raw findMany.
    const heatmap = read('src/app/api/analytics/heatmap/route.ts');
    assert('heatmap uses dailyAgg + fillDailySeries', /dailyAgg\(/.test(heatmap) && /fillDailySeries\(/.test(heatmap),
        'heatmap route no longer uses dailyAgg/fillDailySeries — likely reverted to raw-only and re-blanks days 8-90.');
    assert('heatmap no longer raw-findMany over the window', !/prisma\.heartbeat\.findMany/.test(heatmap),
        'heatmap route loads raw Heartbeat rows again — that both blanks old days and re-pegs CPU.');

    // 4. Analytics KPI/trend/insights services route through the combiner.
    const analytics = read('src/lib/server/analytics.ts');
    for (const fn of ['rangeAggScoped', 'trendAgg', 'failureCountByMonitor']) {
        assert(`server/analytics uses ${fn}`, new RegExp(`${fn}\\(`).test(analytics),
            `src/lib/server/analytics.ts no longer calls ${fn} — that surface reverts to a raw-only ~7-day window.`);
    }

    // 5. Downtime report clamps the raw scan to the boundary and reports the
    //    older window honestly rather than fabricating outage rows.
    const downtime = read('src/app/api/reports/downtime/route.ts');
    assert('downtime clamps raw scan to rawBoundary', /rawBoundary\(\)/.test(downtime) && /detailedStart/.test(downtime),
        'downtime route no longer clamps the transition scan to the retention boundary.');
    assert('downtime surfaces retentionLimited meta', /retentionLimited/.test(downtime),
        'downtime route no longer returns retentionLimited meta — the UI can\'t flag the 7-day detail limit.');

    ok('analytics-retention-split', 'heatmap / analytics KPI-trend-insights / downtime all combine raw + HeartbeatHourly at the shared boundary');
}

main();
