// engine/product-intelligence/index.ts — SPEC-328: Product Intelligence Layer engine.
// Queries Supabase telemetry_events to generate product insights reports.
// Uses the same auth pattern as telemetry-client.ts (fire-and-forget-friendly).
import { PLANU_VERSION } from '../../config/version.js';
const SUPABASE_URL = process.env.PLANU_SUPABASE_URL ?? 'https://canusqigtzldstnjddio.supabase.co';
const SUPABASE_KEY = process.env.PLANU_SUPABASE_KEY ??
    'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNhbnVzcWlndHpsZHN0bmpkZGlvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzI3NTk2MjgsImV4cCI6MjA4ODMzNTYyOH0.Thw1Xgf9_iabOD75J00u35d2k5NsIpUBU9PrqOShxMU';
const TELEMETRY_TABLE = `${SUPABASE_URL}/rest/v1/telemetry_events`;
/** Core tools expected to be active — used for dead-tool detection. */
const KNOWN_CORE_TOOLS = [
    'create_spec',
    'list_specs',
    'update_status',
    'validate',
    'estimate',
    'init_project',
    'suggest_stack',
    'generate_tests',
    'generate_docs',
    'challenge_spec',
    'check_readiness',
    'decompose_spec',
    'scan_project',
    'manage_git',
    'product_insights',
];
function strProp(props, key) {
    const val = props[key];
    return typeof val === 'string' ? val : '';
}
async function fetchEvents(sinceIso) {
    const url = new URL(TELEMETRY_TABLE);
    url.searchParams.set('created_at', `gte.${sinceIso}`);
    url.searchParams.set('select', 'event_name,properties,installation_id,created_at');
    url.searchParams.set('limit', '50000');
    const res = await fetch(url.toString(), {
        headers: {
            apikey: SUPABASE_KEY,
            Authorization: `Bearer ${SUPABASE_KEY}`,
            Accept: 'application/json',
        },
        signal: AbortSignal.timeout(15_000),
    });
    if (!res.ok) {
        throw new Error(`Supabase query failed: ${res.status} ${res.statusText}`);
    }
    return (await res.json());
}
function buildAdoption(rows, sinceIso) {
    const map = new Map();
    for (const row of rows) {
        const tool = strProp(row.properties, 'tool');
        if (!tool) {
            continue;
        }
        const event = row.event_name;
        if (!['tool_used', 'tool_error', 'tool_blocked'].includes(event)) {
            continue;
        }
        let stats = map.get(tool);
        if (stats === undefined) {
            stats = { total: 0, installs: new Set(), errors: 0, blocked: 0, lastSeen: sinceIso };
            map.set(tool, stats);
        }
        stats.total += 1;
        stats.installs.add(row.installation_id);
        if (event === 'tool_error') {
            stats.errors += 1;
        }
        if (event === 'tool_blocked') {
            stats.blocked += 1;
        }
        if (row.created_at > stats.lastSeen) {
            stats.lastSeen = row.created_at;
        }
    }
    return Array.from(map.entries()).map(([tool, s]) => ({
        tool,
        totalCalls: s.total,
        uniqueInstalls: s.installs.size,
        errorRate: s.total > 0 ? s.errors / s.total : 0,
        blockedRate: s.total > 0 ? s.blocked / s.total : 0,
        lastSeenAt: s.lastSeen,
        trend: s.total === 1 ? 'new' : 'stable',
    }));
}
function buildFriction(rows) {
    const map = new Map();
    for (const row of rows) {
        const tool = strProp(row.properties, 'tool');
        if (!tool) {
            continue;
        }
        if (!['tool_used', 'tool_blocked'].includes(row.event_name)) {
            continue;
        }
        let s = map.get(tool);
        if (s === undefined) {
            s = { blocked: 0, total: 0, messages: [] };
            map.set(tool, s);
        }
        s.total += 1;
        if (row.event_name === 'tool_blocked') {
            s.blocked += 1;
            const msg = strProp(row.properties, 'message');
            if (msg) {
                s.messages.push(msg);
            }
        }
    }
    return Array.from(map.entries())
        .filter(([, s]) => s.blocked > 0)
        .map(([tool, s]) => {
        const msgCounts = new Map();
        for (const m of s.messages) {
            msgCounts.set(m, (msgCounts.get(m) ?? 0) + 1);
        }
        let topMessage = null;
        let maxCount = 0;
        for (const [m, c] of msgCounts.entries()) {
            if (c > maxCount) {
                maxCount = c;
                topMessage = m;
            }
        }
        return {
            tool,
            blockedCount: s.blocked,
            totalCalls: s.total,
            blockedRate: s.total > 0 ? s.blocked / s.total : 0,
            topMessage,
        };
    })
        .sort((a, b) => b.blockedRate - a.blockedRate);
}
function buildVersionAdoption(rows) {
    const map = new Map();
    for (const row of rows) {
        const ver = strProp(row.properties, 'planVersion');
        if (!ver) {
            continue;
        }
        let installs = map.get(ver);
        if (installs === undefined) {
            installs = new Set();
            map.set(ver, installs);
        }
        installs.add(row.installation_id);
    }
    const total = Array.from(map.values()).reduce((sum, s) => sum + s.size, 0);
    return Array.from(map.entries())
        .map(([planVersion, installs]) => ({
        planVersion,
        userCount: installs.size,
        pct: total > 0 ? installs.size / total : 0,
        isLatest: planVersion === PLANU_VERSION,
    }))
        .sort((a, b) => b.userCount - a.userCount);
}
function buildDeadTools(adoption, sinceIso) {
    const recentlyActive = new Set(adoption.filter((r) => r.lastSeenAt >= sinceIso).map((r) => r.tool));
    return KNOWN_CORE_TOOLS.filter((t) => !recentlyActive.has(t));
}
function buildSummary(report) {
    const topTool = report.adoption[0];
    const topFriction = report.frictionPoints[0];
    const lines = [
        `Period: ${report.periodDays}d | Events: ${report.totalEvents} | Installs: ${report.uniqueInstalls}`,
    ];
    if (topTool !== undefined) {
        lines.push(`Most used: ${topTool.tool} (${topTool.totalCalls} calls)`);
    }
    if (topFriction !== undefined) {
        lines.push(`Top friction: ${topFriction.tool} (${(topFriction.blockedRate * 100).toFixed(1)}% blocked)`);
    }
    if (report.deadTools.length > 0) {
        lines.push(`Dead tools: ${report.deadTools.join(', ')}`);
    }
    return lines.join(' | ');
}
/**
 * Generates a product insights report by querying Supabase telemetry_events.
 * Throws if the HTTP request fails — caller should handle and return isError.
 */
export async function generateProductInsights(report, periodDays = 30) {
    const since = new Date(Date.now() - periodDays * 24 * 60 * 60 * 1000);
    const sinceIso = since.toISOString();
    const rows = await fetchEvents(sinceIso);
    const uniqueInstalls = new Set(rows.map((r) => r.installation_id)).size;
    const adoption = report === 'friction' ? [] : buildAdoption(rows, sinceIso);
    const frictionPoints = report === 'versions' || report === 'dead_tools' ? [] : buildFriction(rows);
    const versionAdoption = report === 'adoption' || report === 'friction' || report === 'dead_tools'
        ? []
        : buildVersionAdoption(rows);
    const baseAdoption = adoption.length > 0 ? adoption : buildAdoption(rows, sinceIso);
    const deadTools = report === 'adoption' || report === 'friction' || report === 'versions'
        ? []
        : buildDeadTools(baseAdoption, sinceIso);
    const partial = {
        generatedAt: new Date().toISOString(),
        periodDays,
        totalEvents: rows.length,
        uniqueInstalls,
        adoption,
        frictionPoints,
        versionAdoption,
        deadTools,
    };
    return { ...partial, summary: buildSummary(partial) };
}
//# sourceMappingURL=index.js.map