/**
 * src/memory/graphiti.ts
 * Centralized Knowledge Graph — The "Hive Mind"
 * Ensures all channels (Web, WhatsApp, Telegram) share the same memory
 * using Graphiti/Graphify for real-time graph-based RAG
 */
import { createChildLogger } from '../utils/logger.js';
const log = createChildLogger('memory:graphiti');
// ─── Graphiti Client (Mock/Placeholder) ──────────────────────────────
class GraphitiClient {
    baseUrl;
    apiKey;
    constructor() {
        this.baseUrl = process.env.GRAPHITI_URL || 'http://localhost:8080';
        this.apiKey = process.env.GRAPHITI_API_KEY || 'dev-key';
    }
    /**
     * Create a new knowledge graph for a tenant
     */
    async createGraph(tenantId, config) {
        log.info({ tenantId, config }, 'Creating knowledge graph');
        // Simulate API call
        // const response = await fetch(`${this.baseUrl}/graphs`, {
        //   method: 'POST',
        //   headers: { 'Authorization': `Bearer ${this.apiKey}` },
        //   body: JSON.stringify({ name: config.name, schema: config.schema }),
        // });
        // const data = await response.json();
        const graphId = `graph-${tenantId}-${Date.now()}`;
        log.info({ graphId }, 'Graph created successfully');
        return graphId;
    }
    /**
     * Add a node to the graph
     */
    async addNode(graphId, node) {
        log.debug({ graphId, node }, 'Adding node to graph');
        // await fetch(`${this.baseUrl}/graphs/${graphId}/nodes`, {
        //   method: 'POST',
        //   headers: { 'Authorization': `Bearer ${this.apiKey}` },
        //   body: JSON.stringify(node),
        // });
    }
    /**
     * Add an edge to the graph
     */
    async addEdge(graphId, edge) {
        log.debug({ graphId, edge }, 'Adding edge to graph');
        // await fetch(`${this.baseUrl}/graphs/${graphId}/edges`, {
        //   method: 'POST',
        //   headers: { 'Authorization': `Bearer ${this.apiKey}` },
        //   body: JSON.stringify(edge),
        // });
    }
    /**
     * Query the graph for relevant context
     */
    async queryGraph(graphId, query, options) {
        log.debug({ graphId, query }, 'Querying graph');
        // Simulate RAG retrieval
        // const response = await fetch(`${this.baseUrl}/graphs/${graphId}/query`, {
        //   method: 'POST',
        //   headers: { 'Authorization': `Bearer ${this.apiKey}` },
        //   body: JSON.stringify({ query, topK: options?.topK || 5 }),
        // });
        // const data = await response.json();
        // Mock response for now
        return {
            nodes: [
                { id: 'node-1', type: 'business', properties: { name: 'Al-Binaa' } },
                { id: 'node-2', type: 'product', properties: { name: 'Villa Construction' } },
            ],
            edges: [
                { source: 'node-1', target: 'node-2', relation: 'OFFERS' },
            ],
        };
    }
    /**
     * Sync memory across channels
     */
    async syncMemory(tenantId, channel, memory) {
        log.info({ tenantId, channel, memoryId: memory.id }, 'Syncing memory to graph');
        // 1. Create node for this memory entry
        const node = {
            id: `mem-${memory.id}`,
            type: 'conversation',
            properties: {
                channel,
                content: memory.content,
                timestamp: memory.timestamp.toISOString(),
                ...memory.metadata,
            },
        };
        // 2. Add node to graph
        const graphId = `graph-${tenantId}-main`;
        await this.addNode(graphId, node);
        // 3. Link to relevant business/product nodes (if any)
        if (memory.metadata.product_id) {
            await this.addEdge(graphId, {
                source: `mem-${memory.id}`,
                target: `prod-${memory.metadata.product_id}`,
                relation: 'MENTIONS',
            });
        }
    }
    /**
     * Get unified context for a tenant (across all channels)
     */
    async getUnifiedContext(tenantId, query) {
        log.info({ tenantId, query }, 'Fetching unified context');
        const graphId = `graph-${tenantId}-main`;
        const context = await this.queryGraph(graphId, query, { topK: 10 });
        // Build a coherent context string from nodes
        const contextText = context.nodes
            .map(n => {
            if (n.type === 'business')
                return `Business: ${n.properties.name}`;
            if (n.type === 'product')
                return `Product: ${n.properties.name} - ${n.properties.description || ''}`;
            if (n.type === 'conversation')
                return `Recent: ${n.properties.content.substring(0, 100)}...`;
            return '';
        })
            .join('\n');
        return contextText;
    }
}
// ─── Singleton Instance ──────────────────────────────────────────────
export const graphiti = new GraphitiClient();
// ─── Helper Functions ────────────────────────────────────────────────
/**
 * Initialize the knowledge graph for a new tenant
 */
export async function initTenantGraph(tenantId) {
    const graphId = await graphiti.createGraph(tenantId, {
        name: `tenant-${tenantId}-graph`,
        schema: 'business-knowledge',
        syncChannels: ['whatsapp', 'web', 'telegram', 'email'],
    });
    log.info({ tenantId, graphId }, 'Tenant graph initialized');
    return graphId;
}
/**
 * Add a memory entry to the graph (called from any channel)
 */
export async function addMemoryToGraph(tenantId, channel, content, metadata = {}) {
    const memory = {
        id: `mem-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        tenantId,
        channel,
        content,
        timestamp: new Date(),
        metadata,
    };
    await graphiti.syncMemory(tenantId, channel, memory);
}
/**
 * Get context for an AI response (unified across all channels)
 */
export async function getContextForResponse(tenantId, query) {
    return graphiti.getUnifiedContext(tenantId, query);
}
/**
 * Index initial documents (SOUL, PRODUCTS, PROCESSES) into the graph
 */
export async function indexInitialDocuments(tenantId, documents) {
    const graphId = `graph-${tenantId}-main`;
    // Index SOUL.md
    await graphiti.addNode(graphId, {
        id: `doc-soul-${tenantId}`,
        type: 'document',
        properties: { name: 'SOUL.md', content: documents.soul },
    });
    // Index PRODUCTS.md
    await graphiti.addNode(graphId, {
        id: `doc-products-${tenantId}`,
        type: 'document',
        properties: { name: 'PRODUCTS.md', content: documents.products },
    });
    // Index PROCESSES.md
    await graphiti.addNode(graphId, {
        id: `doc-processes-${tenantId}`,
        type: 'document',
        properties: { name: 'PROCESSES.md', content: documents.processes },
    });
    log.info({ tenantId }, 'Initial documents indexed into graph');
}
//# sourceMappingURL=graphiti.js.map