// tools/observatory-insights.ts — SPEC-572: Observatory cross-project aggregated telemetry insights
import { join } from 'node:path';
import { readJson, writeJson, globalDataDir, hashProjectPath } from '../storage/base-store.js';
import { getRegistry } from '../storage/global-projects-store.js';
import { listActuals } from '../storage/actuals-store.js';
import { readTelemetryConfig } from '../engine/telemetry/index.js';
import { computeLocalInsights, computeVelocityInsight, isCacheValid, } from '../engine/observatory-engine.js';
const OBSERVATORY_API_URL = 'https://planu.dev/api/observatory';
const FETCH_TIMEOUT_MS = 5000;
// ---------------------------------------------------------------------------
// Cache path
// ---------------------------------------------------------------------------
function observatoryCachePath() {
    return join(globalDataDir(), 'observatory-cache.json');
}
// ---------------------------------------------------------------------------
// Remote fetch (opt-in only)
// ---------------------------------------------------------------------------
/* v8 ignore start */
async function fetchRemoteInsights() {
    const controller = new AbortController();
    const timer = setTimeout(() => {
        controller.abort();
    }, FETCH_TIMEOUT_MS);
    try {
        const res = await fetch(OBSERVATORY_API_URL, {
            method: 'GET',
            headers: { Accept: 'application/json', 'User-Agent': 'planu-observatory/1.0' },
            signal: controller.signal,
        });
        clearTimeout(timer);
        if (!res.ok) {
            return null;
        }
        const data = (await res.json());
        return Array.isArray(data.insights) ? data.insights : null;
    }
    catch {
        clearTimeout(timer);
        return null;
    }
}
/* v8 ignore stop */
async function getOrFetchRemoteInsights() {
    const cachePath = observatoryCachePath();
    const cached = await readJson(cachePath, null);
    if (cached !== null && isCacheValid(cached.fetchedAt)) {
        return { insights: cached.insights, source: 'cache' };
    }
    const remote = await fetchRemoteInsights();
    /* v8 ignore start */
    if (remote !== null) {
        const newCache = {
            fetchedAt: new Date().toISOString(),
            insights: remote,
        };
        await writeJson(cachePath, newCache);
        return { insights: remote, source: 'remote' };
    }
    // Fallback: use stale cache if available
    if (cached !== null) {
        return { insights: cached.insights, source: 'cache' };
    }
    /* v8 ignore stop */
    return { insights: [], source: 'unavailable' };
}
// ---------------------------------------------------------------------------
// Main handler
// ---------------------------------------------------------------------------
export async function handleObservatoryInsights(args) {
    try {
        // Collect project IDs to analyze
        const projectIds = await resolveProjectIds(args.projectPath);
        // Load actuals from storage (tool layer responsibility)
        const allActuals = await gatherActuals(projectIds);
        // Compute local insights
        const localInsights = computeLocalInsights(allActuals);
        // Velocity insight (multi-project)
        const velocityInsight = computeVelocityInsight(allActuals, projectIds.length);
        if (velocityInsight !== null) {
            localInsights.push(velocityInsight);
        }
        // Remote insights (opt-in gate)
        let remoteInsights = [];
        let remoteSource = 'disabled';
        if (args.includeGlobal === true) {
            const telemetryConfig = await readTelemetryConfig();
            const observatoryEnabled = telemetryConfig?.enableObservatory === true;
            if (observatoryEnabled) {
                const remote = await getOrFetchRemoteInsights();
                remoteInsights = remote.insights;
                remoteSource = remote.source;
            }
            else {
                remoteSource = 'opt-out';
            }
        }
        return formatReport(localInsights, remoteInsights, remoteSource, projectIds.length);
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return {
            content: [
                {
                    type: 'text',
                    text: `Observatory error: ${message}. Ensure at least one project is registered.`,
                },
            ],
            isError: true,
        };
    }
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
async function resolveProjectIds(projectPath) {
    if (projectPath !== undefined && projectPath.length > 0) {
        return [hashProjectPath(projectPath)];
    }
    const registry = await getRegistry();
    return registry.projects.map((p) => p.hash);
}
async function gatherActuals(projectIds) {
    const results = [];
    for (const projectId of projectIds) {
        try {
            const actuals = await listActuals(projectId);
            for (const record of actuals) {
                if (record.estimatedHours > 0 && record.actualHours > 0) {
                    results.push({
                        estimatedHours: record.estimatedHours,
                        actualHours: record.actualHours,
                        scope: record.specScope,
                    });
                }
            }
        }
        catch {
            // Skip projects that cannot be read — they may not have actuals yet
        }
    }
    return results;
}
function formatReport(localInsights, remoteInsights, remoteSource, projectCount) {
    const lines = ['# Planu Observatory Report', ''];
    lines.push(`Projects analyzed: ${projectCount}`);
    lines.push('');
    if (localInsights.length === 0) {
        lines.push('## Local Insights', '', 'No patterns detected yet. Record actuals with `record_actual` to build insights.', '');
    }
    else {
        lines.push('## Local Insights', '');
        for (const insight of localInsights) {
            const conf = `[${insight.confidence}]`;
            lines.push(`- ${conf} ${insight.pattern} (n=${insight.sampleSize}, category=${insight.category})`);
        }
        lines.push('');
    }
    if (remoteInsights.length > 0) {
        lines.push('## Community Insights (planu.dev)', '');
        for (const insight of remoteInsights) {
            const conf = `[${insight.confidence}]`;
            lines.push(`- ${conf} ${insight.pattern} (n=${insight.sampleSize})`);
        }
        lines.push('');
    }
    else if (remoteSource === 'opt-out') {
        lines.push('## Community Insights', '', 'Disabled. Enable with `configure_telemetry` (enableObservatory=true).', '');
    }
    else if (remoteSource === 'unavailable') {
        lines.push('## Community Insights', '', 'Remote data unavailable (timeout or network error). Local insights only.', '');
    }
    return { content: [{ type: 'text', text: lines.join('\n') }] };
}
//# sourceMappingURL=observatory-insights.js.map