/**
 * src/knowledge/routes.ts
 *
 * GET /api/knowledge/graph?tenant_id=...
 *   → { nodes: [...], links: [...], stats: {...} }
 *
 * Powers the dashboard's force-directed graph view. Returns up to 200 entities
 * + 400 relationships per request — enough to visualize a Phase 1 customer's
 * full memory at a glance, capped so a saturated database doesn't ship a
 * 50MB JSON to the browser.
 */
import { createChildLogger } from '../utils/logger.js';
import { supabase } from '../utils/supabase.js';
import { authMiddleware } from '../auth/middleware.js';
const log = createChildLogger('knowledge:routes');
export async function registerKnowledgeRoutes(app) {
    app.get('/api/knowledge/graph', { preHandler: [authMiddleware] }, async (request, reply) => {
        try {
            const tenantId = request.query.tenant_id;
            if (!tenantId) {
                return reply.code(400).send({ success: false, error: 'tenant_id query param is required' });
            }
            const limit = Math.min(parseInt(request.query.limit ?? '300', 10) || 300, 600);
            // 1. Pull relationships FIRST so we can guarantee their endpoints make
            //    it into the node set. (The old code picked top-200 entities by
            //    episode_count — which excluded tools/skills/goals (episode_count=0)
            //    and so silently dropped every tool↔tool / goal→tool edge, leaving
            //    the graph as disconnected dots.)
            const relResult = await supabase
                .from('kg_relationships')
                .select('source_id, target_id, type')
                .eq('tenant_id', tenantId)
                .limit(800);
            const rels = (relResult.data ?? []);
            const connectedIds = new Set();
            for (const r of rels) {
                connectedIds.add(r.source_id);
                connectedIds.add(r.target_id);
            }
            // 2. Fetch entities (most/all), ranked by episode_count.
            const entitiesResult = await supabase
                .from('kg_entities')
                .select('id, type, name, kind, customer_id, episode_count, source_files')
                .eq('tenant_id', tenantId)
                .order('episode_count', { ascending: false })
                .limit(1000);
            const allEntities = (entitiesResult.data ?? []);
            // 3. Build the node set: every CONNECTED entity first (so no edge is
            //    orphaned), then fill remaining slots with the highest-activity
            //    entities, up to `limit`.
            const chosen = [];
            const chosenIds = new Set();
            for (const e of allEntities) {
                if (connectedIds.has(e.id) && !chosenIds.has(e.id)) {
                    chosen.push(e);
                    chosenIds.add(e.id);
                }
            }
            for (const e of allEntities) {
                if (chosen.length >= limit)
                    break;
                if (!chosenIds.has(e.id)) {
                    chosen.push(e);
                    chosenIds.add(e.id);
                }
            }
            const entities = chosen;
            // 4. Keep links whose BOTH endpoints are in the node set.
            const links = rels
                .filter((r) => chosenIds.has(r.source_id) && chosenIds.has(r.target_id))
                .map((r) => ({ source: r.source_id, target: r.target_id, type: r.type }));
            const stats = {
                entities: entities.length,
                relationships: links.length,
                customers: entities.filter((e) => e.type === 'Person' && e.customer_id).length,
                products: entities.filter((e) => e.type === 'Product').length,
            };
            const nodes = entities.map((e) => ({
                id: e.id,
                name: e.name,
                type: e.type,
                kind: e.kind,
                episode_count: e.episode_count,
                customer_id: e.customer_id,
                source_files: e.source_files ?? [],
            }));
            return reply.send({ success: true, data: { nodes, links, stats } });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'knowledge graph fetch failed');
            return reply.code(500).send({ success: false, error: 'Internal server error' });
        }
    });
    // ─── POST /api/knowledge/vault/rebuild ─────────────────────────────
    // Bulk-exports every kg_entity for the tenant as a markdown note with
    // wiki-links to its kg_relationships targets. The vault directory can
    // then be opened in Obsidian — its graph view will draw the full KG.
    app.post('/api/knowledge/vault/rebuild', { preHandler: [authMiddleware] }, async (req, reply) => {
        const tenantId = (req.body?.tenant_id || req.user?.tenant_id);
        if (!tenantId)
            return reply.code(400).send({ success: false, error: 'tenant_id required' });
        try {
            const { rebuildVaultForTenant } = await import('./vault.js');
            const result = await rebuildVaultForTenant(tenantId);
            reply.send({ success: true, data: result });
        }
        catch (err) {
            log.error({ err: err.message }, 'vault rebuild failed');
            reply.code(500).send({ success: false, error: err.message });
        }
    });
    // ─── GET /api/knowledge/vault/path ────────────────────────────────
    // Returns the local filesystem path of the vault — owner clicks
    // "Open vault folder" in Memory tab → Tauri opens it in Finder/Explorer.
    app.get('/api/knowledge/vault/path', { preHandler: [authMiddleware] }, async (req, reply) => {
        const tenantId = req.query.tenant_id;
        if (!tenantId)
            return reply.code(400).send({ success: false, error: 'tenant_id required' });
        const { getVaultRoot } = await import('./vault.js');
        reply.send({ success: true, data: { path: getVaultRoot(tenantId) } });
    });
    log.info('Knowledge graph routes registered ✅');
}
//# sourceMappingURL=routes.js.map