#!/usr/bin/env node
/**
 * REST API Server for Memory Service
 * Provides HTTP interface for web applications
 */
import Fastify from 'fastify';
import { initDatabaseFromEnv } from '../database/index.js';
import { MemoryCore } from '../core/index.js';
import { MemoryType, ImportanceLevel } from '../types/enums.js';
class APIServer {
    fastify;
    memoryCore = null;
    port;
    host;
    constructor(port = 3000, host = '0.0.0.0') {
        this.port = port;
        this.host = host;
        this.fastify = Fastify({
            logger: {
                level: process.env['LOG_LEVEL'] || 'info',
            },
        });
        this.setupMiddleware();
        this.setupRoutes();
        this.setupErrorHandling();
    }
    setupMiddleware() {
        // CORS
        this.fastify.register(import('@fastify/cors'), {
            origin: true,
            credentials: true,
        });
        // Authentication middleware
        this.fastify.addHook('preHandler', async (request, reply) => {
            // Skip auth for health check and public endpoints
            if (request.url === '/health' || request.url === '/') {
                return;
            }
            const authHeader = request.headers.authorization;
            if (!authHeader || !authHeader.startsWith('Bearer ')) {
                reply.code(401).send({ success: false, error: 'Missing or invalid authorization header' });
                return;
            }
            const apiKey = authHeader.substring(7);
            if (!this.memoryCore) {
                reply.code(500).send({ success: false, error: 'Server not initialized' });
                return;
            }
            try {
                const user = await this.memoryCore.getUserByApiKey(apiKey);
                if (!user || !user.isActive) {
                    reply.code(401).send({ success: false, error: 'Invalid API key' });
                    return;
                }
                request.user = { id: user.id, email: user.email };
            }
            catch (error) {
                reply.code(500).send({ success: false, error: 'Authentication error' });
                return;
            }
        });
    }
    setupRoutes() {
        // Health check
        this.fastify.get('/health', async (request, reply) => {
            return { status: 'ok', timestamp: new Date().toISOString() };
        });
        // Root endpoint
        this.fastify.get('/', async (request, reply) => {
            return {
                name: 'MCP Memory Service API',
                version: '1.0.0',
                description: 'REST API for cloud-based vector memory service',
            };
        });
        // Memory endpoints
        this.fastify.post('/api/memories', {
            schema: {
                body: {
                    type: 'object',
                    required: ['title', 'content'],
                    properties: {
                        title: { type: 'string' },
                        content: { type: 'string' },
                        memory_type: { type: 'string' },
                        importance: { type: 'integer', minimum: 1, maximum: 4 },
                        tags: { type: 'array', items: { type: 'string' } },
                        entity_ids: { type: 'array', items: { type: 'integer' } },
                    },
                },
            },
        }, this.addMemory.bind(this));
        this.fastify.get('/api/memories/search', {
            schema: {
                querystring: {
                    type: 'object',
                    required: ['query'],
                    properties: {
                        query: { type: 'string' },
                        limit: { type: 'integer', minimum: 1, maximum: 100 },
                        threshold: { type: 'number', minimum: 0, maximum: 1 },
                        memory_types: { type: 'string' }, // Comma-separated
                    },
                },
            },
        }, this.searchMemories.bind(this));
        this.fastify.get('/api/memories', this.getMemories.bind(this));
        // Entity endpoints
        this.fastify.post('/api/entities', {
            schema: {
                body: {
                    type: 'object',
                    required: ['name', 'entity_type'],
                    properties: {
                        name: { type: 'string' },
                        entity_type: { type: 'string' },
                        description: { type: 'string' },
                        company: { type: 'string' },
                        title: { type: 'string' },
                        email: { type: 'string' },
                        phone: { type: 'string' },
                        website: { type: 'string' },
                        importance: { type: 'integer', minimum: 1, maximum: 4 },
                        tags: { type: 'array', items: { type: 'string' } },
                        notes: { type: 'string' },
                    },
                },
            },
        }, this.createEntity.bind(this));
        this.fastify.get('/api/entities/search', {
            schema: {
                querystring: {
                    type: 'object',
                    required: ['query'],
                    properties: {
                        query: { type: 'string' },
                        limit: { type: 'integer', minimum: 1, maximum: 100 },
                    },
                },
            },
        }, this.searchEntities.bind(this));
        this.fastify.get('/api/entities', this.getEntities.bind(this));
        // Search endpoints
        this.fastify.get('/api/search', {
            schema: {
                querystring: {
                    type: 'object',
                    required: ['query'],
                    properties: {
                        query: { type: 'string' },
                        limit: { type: 'integer', minimum: 1, maximum: 100 },
                        threshold: { type: 'number', minimum: 0, maximum: 1 },
                        memory_types: { type: 'string' }, // Comma-separated
                        entity_types: { type: 'string' }, // Comma-separated
                    },
                },
            },
        }, this.unifiedSearch.bind(this));
        // Statistics endpoint
        this.fastify.get('/api/statistics', this.getStatistics.bind(this));
        // User management endpoints
        this.fastify.post('/api/users', {
            schema: {
                body: {
                    type: 'object',
                    required: ['email'],
                    properties: {
                        email: { type: 'string' },
                        name: { type: 'string' },
                        organization: { type: 'string' },
                    },
                },
            },
        }, this.createUser.bind(this));
    }
    setupErrorHandling() {
        this.fastify.setErrorHandler((error, request, reply) => {
            this.fastify.log.error(error);
            const response = {
                success: false,
                error: error.message || 'Internal server error',
            };
            reply.code(error.statusCode || 500).send(response);
        });
        this.fastify.setNotFoundHandler((request, reply) => {
            const response = {
                success: false,
                error: 'Not found',
            };
            reply.code(404).send(response);
        });
    }
    // Route handlers
    async addMemory(request, reply) {
        const { title, content, memory_type, importance, tags, entity_ids } = request.body;
        const result = await this.memoryCore.addMemory(title, content, memory_type || MemoryType.MEMORY, {
            userId: request.user.id,
            importance: importance || ImportanceLevel.MEDIUM,
            tags,
            entityIds: entity_ids,
        });
        return {
            success: result.status === 'success',
            data: result.data,
            message: result.message,
            error: result.error,
        };
    }
    async searchMemories(request, reply) {
        const { query, limit, threshold, memory_types } = request.query;
        const memoryTypesArray = memory_types
            ? memory_types.split(',').map((t) => t.trim())
            : undefined;
        const result = await this.memoryCore.searchMemories(query, {
            query,
            userId: request.user.id,
            limit: limit ? parseInt(limit) : 10,
            threshold: threshold ? parseFloat(threshold) : 0.7,
            memoryTypes: memoryTypesArray,
        });
        return {
            success: result.status === 'success',
            data: result.data,
            message: result.message,
            error: result.error,
        };
    }
    async getMemories(request, reply) {
        // TODO: Implement get memories in MemoryCore
        return {
            success: false,
            error: 'Get memories not yet implemented',
        };
    }
    async createEntity(request, reply) {
        const { name, entity_type, ...options } = request.body;
        const result = await this.memoryCore.createEntity(name, entity_type, {
            userId: request.user.id,
            ...options,
            importance: options.importance || ImportanceLevel.MEDIUM,
        });
        return {
            success: result.status === 'success',
            data: result.data,
            message: result.message,
            error: result.error,
        };
    }
    async searchEntities(request, reply) {
        const { query, limit } = request.query;
        const result = await this.memoryCore.searchEntities(query, {
            userId: request.user.id,
            limit: limit ? parseInt(limit) : 10,
        });
        return {
            success: result.status === 'success',
            data: result.data,
            message: result.message,
            error: result.error,
        };
    }
    async getEntities(request, reply) {
        // TODO: Implement get entities in MemoryCore
        return {
            success: false,
            error: 'Get entities not yet implemented',
        };
    }
    async unifiedSearch(request, reply) {
        const { query, limit, threshold, memory_types, entity_types } = request.query;
        const memoryTypesArray = memory_types
            ? memory_types.split(',').map((t) => t.trim())
            : undefined;
        const entityTypesArray = entity_types
            ? entity_types.split(',').map((t) => t.trim())
            : undefined;
        const result = await this.memoryCore.unifiedSearch(query, {
            query,
            userId: request.user.id,
            limit: limit ? parseInt(limit) : 10,
            threshold: threshold ? parseFloat(threshold) : 0.7,
            memoryTypes: memoryTypesArray,
            entityTypes: entityTypesArray,
        });
        return {
            success: result.status === 'success',
            data: result.data,
            message: result.message,
            error: result.error,
        };
    }
    async getStatistics(request, reply) {
        const result = await this.memoryCore.getStatistics(request.user.id);
        return {
            success: result.status === 'success',
            data: result.data,
            message: result.message,
            error: result.error,
        };
    }
    async createUser(request, reply) {
        const userData = request.body;
        const result = await this.memoryCore.createUser(userData);
        return {
            success: result.status === 'success',
            data: result.data,
            message: result.message,
            error: result.error,
        };
    }
    async start() {
        try {
            // Initialize database and memory core
            const db = initDatabaseFromEnv();
            this.memoryCore = new MemoryCore(db, process.env.OPENAI_API_KEY);
            await this.memoryCore.initialize();
            // Start the server
            await this.fastify.listen({ port: this.port, host: this.host });
            this.fastify.log.info(`API server listening on ${this.host}:${this.port}`);
        }
        catch (error) {
            this.fastify.log.error('Failed to start API server: %s', String(error));
            process.exit(1);
        }
    }
    async stop() {
        if (this.memoryCore) {
            await this.memoryCore.close();
        }
        await this.fastify.close();
    }
}
// Start the server if this file is run directly
if (import.meta.url === `file://${process.argv[1]}`) {
    const port = process.env.PORT ? parseInt(process.env.PORT) : 3000;
    const host = process.env.HOST || '0.0.0.0';
    const server = new APIServer(port, host);
    // Graceful shutdown
    process.on('SIGINT', async () => {
        console.log('Received SIGINT, shutting down gracefully...');
        await server.stop();
        process.exit(0);
    });
    process.on('SIGTERM', async () => {
        console.log('Received SIGTERM, shutting down gracefully...');
        await server.stop();
        process.exit(0);
    });
    server.start().catch(error => {
        console.error('Failed to start API server:', error);
        process.exit(1);
    });
}
export { APIServer };
//# sourceMappingURL=server.js.map