// @crash-shield-ignore-file — config/cache reader for Planu-controlled JSON; writer is this codebase, shape guaranteed by build/seed.
// engine/telemetry/health-checker.ts — SPEC-346: Diagnose why telemetry data is not reaching the dashboard.
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { readTelemetryConfig } from './telemetry-store.js';
import { sendTelemetryEvent } from './telemetry-client.js';
const NETWORK_TIMEOUT_MS = 5_000;
const RECENT_EVENT_THRESHOLD_DAYS = 7;
// ---------------------------------------------------------------------------
// Individual checks
// ---------------------------------------------------------------------------
async function checkConfigEnabled() {
    const config = await readTelemetryConfig();
    if (!config?.enabled) {
        return {
            check: {
                name: 'config-enabled',
                status: 'fail',
                detail: 'Telemetry is disabled in ~/.planu/telemetry.json',
            },
            enabled: false,
        };
    }
    return {
        check: {
            name: 'config-enabled',
            status: 'ok',
            detail: 'Telemetry is enabled',
        },
        enabled: true,
    };
}
function checkEndpointConfigured(endpoint) {
    if (!endpoint || endpoint.trim() === '') {
        return {
            name: 'endpoint-configured',
            status: 'fail',
            detail: 'No telemetry endpoint configured. Set PLANU_TELEMETRY_ENDPOINT env var.',
        };
    }
    return {
        name: 'endpoint-configured',
        status: 'ok',
        detail: `Endpoint: ${endpoint}`,
    };
}
async function checkNetworkReachable(endpoint) {
    if (!endpoint) {
        return {
            name: 'network-reachable',
            status: 'skip',
            detail: 'Skipped — no endpoint to test',
        };
    }
    try {
        const response = await fetch(endpoint, {
            method: 'GET',
            signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
        });
        if (response.ok || response.status < 500) {
            return {
                name: 'network-reachable',
                status: 'ok',
                detail: `Endpoint responded with HTTP ${response.status}`,
            };
        }
        return {
            name: 'network-reachable',
            status: 'warn',
            detail: `Endpoint returned HTTP ${response.status} — server may be experiencing issues`,
        };
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return {
            name: 'network-reachable',
            status: 'warn',
            detail: `Could not reach endpoint: ${message}`,
        };
    }
}
async function checkLastEvent(projectPath) {
    const candidates = [
        join(projectPath, 'data', 'telemetry-events.json'),
        join(projectPath, '.planu', 'telemetry-events.json'),
        join(projectPath, 'data', 'telemetry.json'),
    ];
    for (const candidate of candidates) {
        try {
            const raw = await readFile(candidate, 'utf-8');
            const parsed = JSON.parse(raw);
            if (!Array.isArray(parsed) || parsed.length === 0) {
                continue;
            }
            const last = parsed[parsed.length - 1];
            const ts = typeof last.timestamp === 'string' ? last.timestamp : undefined;
            if (!ts) {
                return {
                    name: 'last-event',
                    status: 'warn',
                    detail: `Found event log at ${candidate} but last event has no timestamp`,
                };
            }
            const daysAgo = (Date.now() - new Date(ts).getTime()) / (1000 * 60 * 60 * 24);
            if (daysAgo > RECENT_EVENT_THRESHOLD_DAYS) {
                return {
                    name: 'last-event',
                    status: 'warn',
                    detail: `Last event was ${Math.floor(daysAgo)} days ago (${ts})`,
                };
            }
            return {
                name: 'last-event',
                status: 'ok',
                detail: `Last event: ${ts} (${Math.floor(daysAgo)} days ago)`,
            };
        }
        catch {
            // try next candidate
        }
    }
    return {
        name: 'last-event',
        status: 'warn',
        detail: 'No event log found — no events have been sent yet',
    };
}
async function checkErrorLog(projectPath) {
    const candidates = [
        join(projectPath, 'data', 'telemetry-errors.json'),
        join(projectPath, '.planu', 'telemetry-errors.json'),
    ];
    for (const candidate of candidates) {
        try {
            const raw = await readFile(candidate, 'utf-8');
            const parsed = JSON.parse(raw);
            if (!Array.isArray(parsed)) {
                continue;
            }
            const recent = parsed.slice(-5);
            if (recent.length === 0) {
                return {
                    name: 'error-log',
                    status: 'ok',
                    detail: 'No telemetry errors recorded',
                };
            }
            const summary = recent
                .map((e) => {
                if (typeof e.message === 'string') {
                    return e.message;
                }
                if (typeof e.error === 'string') {
                    return e.error;
                }
                return '[unknown error]';
            })
                .join('; ');
            return {
                name: 'error-log',
                status: 'warn',
                detail: `${recent.length} recent error(s): ${summary}`,
            };
        }
        catch {
            // try next candidate
        }
    }
    return {
        name: 'error-log',
        status: 'ok',
        detail: 'No error log found',
    };
}
async function checkClientVersion(projectPath) {
    const pkgPath = join(projectPath, 'package.json');
    try {
        const raw = await readFile(pkgPath, 'utf-8');
        const pkg = JSON.parse(raw);
        const version = typeof pkg.version === 'string' ? pkg.version : 'unknown';
        const name = typeof pkg.name === 'string' ? pkg.name : 'unknown';
        return {
            name: 'version',
            status: 'ok',
            detail: `Client: ${name}@${version}`,
        };
    }
    catch {
        return {
            name: 'version',
            status: 'warn',
            detail: `Could not read package.json at ${pkgPath}`,
        };
    }
}
// ---------------------------------------------------------------------------
// Test event
// ---------------------------------------------------------------------------
function sendTestDiagnosticEvent(installationId) {
    sendTelemetryEvent({
        event: 'tool_used',
        properties: {
            tool: 'telemetry_health',
            diagnostic: true,
            timestamp: new Date().toISOString(),
        },
        installationId,
    });
}
// ---------------------------------------------------------------------------
// Status helpers
// ---------------------------------------------------------------------------
function buildRecommendation(status) {
    switch (status) {
        case 'disabled':
            return 'Run configure_telemetry to enable usage tracking';
        case 'misconfigured':
            return 'Set PLANU_TELEMETRY_ENDPOINT or run configure_telemetry';
        case 'degraded':
            return 'Check network connectivity or firewall rules blocking outbound requests';
        case 'healthy':
            return 'Telemetry is working. If no data shows in dashboard, check the dashboard filters.';
    }
}
function buildHumanSummary(status, checks) {
    const warnCount = checks.filter((c) => c.status === 'warn').length;
    switch (status) {
        case 'disabled':
            return 'Telemetry is turned off — no usage data is being collected.';
        case 'misconfigured':
            return 'Telemetry cannot send data: configuration issue needs fixing.';
        case 'degraded':
            return `Telemetry is configured but ${warnCount} issue(s) may be preventing data from reaching the dashboard.`;
        case 'healthy':
            return 'Telemetry appears healthy. Data should be reaching the dashboard.';
    }
}
function computeStatus(isEnabled, checks) {
    if (!isEnabled) {
        return 'disabled';
    }
    const endpointCheck = checks.find((c) => c.name === 'endpoint-configured');
    if (endpointCheck?.status === 'fail') {
        return 'misconfigured';
    }
    const hasWarn = checks.some((c) => c.status === 'warn');
    if (hasWarn) {
        return 'degraded';
    }
    return 'healthy';
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
export async function checkTelemetryHealth(projectPath, options) {
    const { check: enabledCheck, enabled } = await checkConfigEnabled();
    if (!enabled) {
        const checks = [enabledCheck];
        const status = 'disabled';
        return {
            status,
            checks,
            recommendation: buildRecommendation(status),
            humanSummary: buildHumanSummary(status, checks),
        };
    }
    // Default endpoint from telemetry-client
    const defaultEndpoint = 'https://canusqigtzldstnjddio.supabase.co/rest/v1/telemetry_events';
    const endpoint = process.env.PLANU_TELEMETRY_ENDPOINT?.trim() ?? defaultEndpoint;
    const [networkCheck, lastEventCheck, errorLogCheck, versionCheck] = await Promise.all([
        checkNetworkReachable(endpoint),
        checkLastEvent(projectPath),
        checkErrorLog(projectPath),
        checkClientVersion(projectPath),
    ]);
    const endpointCheck = checkEndpointConfigured(endpoint);
    const checks = [
        enabledCheck,
        endpointCheck,
        networkCheck,
        lastEventCheck,
        errorLogCheck,
        versionCheck,
    ];
    const status = computeStatus(enabled, checks);
    if (options?.sendTestEvent === true) {
        const config = await readTelemetryConfig();
        const installationId = config?.installationId ?? 'anonymous';
        sendTestDiagnosticEvent(installationId);
    }
    return {
        status,
        checks,
        recommendation: buildRecommendation(status),
        humanSummary: buildHumanSummary(status, checks),
    };
}
//# sourceMappingURL=health-checker.js.map