/**
 * IDE Integration Protocol
 *
 * Provides a standardized protocol for IDE/editor integration:
 * - JSON-RPC based communication
 * - WebSocket and stdio transports
 * - LSP-compatible message format
 * - Bidirectional async communication
 */
import { EventEmitter } from 'events';
import * as net from 'net';
import * as readline from 'readline';
// ============================================================================
// Error Codes
// ============================================================================
export const ErrorCodes = {
    ParseError: -32700,
    InvalidRequest: -32600,
    MethodNotFound: -32601,
    InvalidParams: -32602,
    InternalError: -32603,
    ServerNotInitialized: -32002,
    UnknownErrorCode: -32001,
    RequestCancelled: -32800,
    ContentModified: -32801,
};
// ============================================================================
// IDE Protocol Server
// ============================================================================
export class IDEProtocolServer extends EventEmitter {
    handlers = new Map();
    transport = null;
    nextId = 1;
    pendingRequests = new Map();
    initialized = false;
    capabilities = {
        fileOperations: true,
        diagnostics: true,
        codeActions: true,
        completion: true,
        hover: true,
        formatting: true,
        semanticTokens: false,
        inlineValues: false,
    };
    constructor() {
        super();
        this.registerBuiltinMethods();
    }
    /**
     * Register built-in methods
     */
    registerBuiltinMethods() {
        // Initialize
        this.registerMethod('initialize', async (params) => {
            const initParams = params;
            if (initParams.capabilities) {
                this.capabilities = { ...this.capabilities, ...initParams.capabilities };
            }
            this.initialized = true;
            this.emit('initialized');
            return { capabilities: this.capabilities };
        });
        // Shutdown
        this.registerMethod('shutdown', async () => {
            this.emit('shutdown');
            return null;
        });
        // Exit
        this.registerMethod('exit', async () => {
            this.emit('exit');
            this.stop();
            return null;
        });
        // Ping
        this.registerMethod('ping', async () => {
            return { pong: Date.now() };
        });
        // Get capabilities
        this.registerMethod('getCapabilities', async () => {
            return this.capabilities;
        });
    }
    /**
     * Register a method handler
     */
    registerMethod(method, handler) {
        this.handlers.set(method, handler);
    }
    /**
     * Start with stdio transport
     */
    startStdio() {
        this.transport = new StdioTransport();
        this.setupTransport();
    }
    /**
     * Start with TCP transport
     */
    startTCP(port) {
        return new Promise((resolve, reject) => {
            const transport = new TCPTransport(port);
            transport.on('listening', () => {
                this.transport = transport;
                this.setupTransport();
                resolve();
            });
            transport.on('error', reject);
            transport.start();
        });
    }
    /**
     * Setup transport handlers
     */
    setupTransport() {
        if (!this.transport)
            return;
        this.transport.on('message', async (msg) => {
            try {
                await this.handleMessage(msg);
            }
            catch (error) {
                this.emit('error', error);
            }
        });
        this.transport.on('error', (error) => {
            this.emit('error', error);
        });
        this.transport.on('close', () => {
            this.emit('close');
        });
    }
    /**
     * Handle incoming message
     */
    async handleMessage(msg) {
        // Handle response
        if (msg.id !== undefined && (msg.result !== undefined || msg.error !== undefined)) {
            const pending = this.pendingRequests.get(msg.id);
            if (pending) {
                this.pendingRequests.delete(msg.id);
                if (msg.error) {
                    pending.reject(new Error(msg.error.message));
                }
                else {
                    pending.resolve(msg.result);
                }
            }
            return;
        }
        // Handle request/notification
        if (msg.method) {
            this.emit('request', { method: msg.method, params: msg.params });
            const handler = this.handlers.get(msg.method);
            if (!handler) {
                if (msg.id !== undefined) {
                    this.sendError(msg.id, ErrorCodes.MethodNotFound, `Method not found: ${msg.method}`);
                }
                return;
            }
            try {
                const result = await handler(msg.params);
                if (msg.id !== undefined) {
                    this.sendResult(msg.id, result);
                }
            }
            catch (error) {
                if (msg.id !== undefined) {
                    this.sendError(msg.id, ErrorCodes.InternalError, error instanceof Error ? error.message : String(error));
                }
            }
        }
    }
    /**
     * Send a request
     */
    async request(method, params) {
        if (!this.transport) {
            throw new Error('Transport not initialized');
        }
        const id = this.nextId++;
        const msg = {
            jsonrpc: '2.0',
            id,
            method,
            params,
        };
        return new Promise((resolve, reject) => {
            this.pendingRequests.set(id, {
                resolve: resolve,
                reject,
            });
            this.transport.send(msg);
        });
    }
    /**
     * Send a notification
     */
    notify(method, params) {
        if (!this.transport)
            return;
        const msg = {
            jsonrpc: '2.0',
            method,
            params,
        };
        this.transport.send(msg);
    }
    /**
     * Send result
     */
    sendResult(id, result) {
        if (!this.transport)
            return;
        const msg = {
            jsonrpc: '2.0',
            id,
            result,
        };
        this.transport.send(msg);
    }
    /**
     * Send error
     */
    sendError(id, code, message, data) {
        if (!this.transport)
            return;
        const msg = {
            jsonrpc: '2.0',
            id,
            error: { code, message, data },
        };
        this.transport.send(msg);
    }
    /**
     * Stop the server
     */
    stop() {
        if (this.transport) {
            this.transport.close();
            this.transport = null;
        }
        this.pendingRequests.clear();
        this.removeAllListeners();
    }
    // =========================================================================
    // IDE-specific methods
    // =========================================================================
    /**
     * Send diagnostics to IDE
     */
    publishDiagnostics(file, diagnostics) {
        this.notify('grok/publishDiagnostics', { file, diagnostics });
    }
    /**
     * Request code action from agent
     */
    async requestCodeAction(file, range, context) {
        return this.request('grok/codeAction', { file, range, context });
    }
    /**
     * Apply workspace edit
     */
    async applyEdit(edit) {
        return this.request('grok/applyEdit', { edit });
    }
    /**
     * Show message in IDE
     */
    showMessage(type, message) {
        this.notify('grok/showMessage', { type, message });
    }
    /**
     * Request input from user
     */
    async requestInput(prompt, placeholder) {
        return this.request('grok/requestInput', { prompt, placeholder });
    }
    /**
     * Open file in IDE
     */
    async openFile(file, line, column) {
        return this.request('grok/openFile', { file, line, column });
    }
    /**
     * Get current IDE state
     */
    async getIDEState() {
        return this.request('grok/getState');
    }
    /**
     * Get file content from IDE
     */
    async getFileContent(file) {
        return this.request('grok/getFileContent', { file });
    }
}
// ============================================================================
// Stdio Transport
// ============================================================================
class StdioTransport extends EventEmitter {
    reader;
    contentLength = 0;
    buffer = '';
    constructor() {
        super();
        this.reader = readline.createInterface({
            input: process.stdin,
            terminal: false,
        });
        this.reader.on('line', (line) => {
            this.handleLine(line);
        });
        this.reader.on('close', () => {
            this.emit('close');
        });
        process.stdin.on('error', (error) => {
            this.emit('error', error);
        });
    }
    handleLine(line) {
        // Parse LSP-style headers
        if (line.startsWith('Content-Length:')) {
            this.contentLength = parseInt(line.slice(15).trim(), 10);
        }
        else if (line === '') {
            // Empty line means headers done, read body
            if (this.contentLength > 0) {
                // Read content from stdin
                process.stdin.once('data', (data) => {
                    try {
                        const msg = JSON.parse(data.toString());
                        this.emit('message', msg);
                    }
                    catch (error) {
                        this.emit('error', new Error(`Failed to parse message: ${error}`));
                    }
                    this.contentLength = 0;
                });
            }
        }
        else {
            // Try to parse as plain JSON
            try {
                const msg = JSON.parse(line);
                this.emit('message', msg);
            }
            catch {
                // Ignore non-JSON lines
            }
        }
    }
    send(message) {
        const content = JSON.stringify(message);
        const header = `Content-Length: ${Buffer.byteLength(content)}\r\n\r\n`;
        process.stdout.write(header + content);
    }
    close() {
        this.reader.close();
    }
}
// ============================================================================
// TCP Transport
// ============================================================================
class TCPTransport extends EventEmitter {
    server = null;
    socket = null;
    port;
    buffer = '';
    constructor(port) {
        super();
        this.port = port;
    }
    start() {
        this.server = net.createServer((socket) => {
            this.socket = socket;
            socket.on('data', (data) => {
                this.buffer += data.toString();
                this.processBuffer();
            });
            socket.on('close', () => {
                this.socket = null;
                this.emit('clientDisconnected');
            });
            socket.on('error', (error) => {
                this.emit('error', error);
            });
            this.emit('clientConnected');
        });
        this.server.on('error', (error) => {
            this.emit('error', error);
        });
        this.server.listen(this.port, () => {
            this.emit('listening', this.port);
        });
    }
    processBuffer() {
        while (true) {
            // Look for Content-Length header
            const headerEnd = this.buffer.indexOf('\r\n\r\n');
            if (headerEnd === -1)
                break;
            const header = this.buffer.slice(0, headerEnd);
            const match = header.match(/Content-Length:\s*(\d+)/i);
            if (!match) {
                // Try to parse as plain JSON
                const lineEnd = this.buffer.indexOf('\n');
                if (lineEnd !== -1) {
                    const line = this.buffer.slice(0, lineEnd);
                    this.buffer = this.buffer.slice(lineEnd + 1);
                    try {
                        const msg = JSON.parse(line);
                        this.emit('message', msg);
                    }
                    catch {
                        // Ignore
                    }
                }
                break;
            }
            const contentLength = parseInt(match[1], 10);
            const contentStart = headerEnd + 4;
            const contentEnd = contentStart + contentLength;
            if (this.buffer.length < contentEnd)
                break;
            const content = this.buffer.slice(contentStart, contentEnd);
            this.buffer = this.buffer.slice(contentEnd);
            try {
                const msg = JSON.parse(content);
                this.emit('message', msg);
            }
            catch (error) {
                this.emit('error', new Error(`Failed to parse message: ${error}`));
            }
        }
    }
    send(message) {
        if (!this.socket)
            return;
        const content = JSON.stringify(message);
        const header = `Content-Length: ${Buffer.byteLength(content)}\r\n\r\n`;
        this.socket.write(header + content);
    }
    close() {
        if (this.socket) {
            this.socket.destroy();
            this.socket = null;
        }
        if (this.server) {
            this.server.close();
            this.server = null;
        }
        this.emit('close');
    }
}
// ============================================================================
// IDE Client (for testing)
// ============================================================================
export class IDEProtocolClient extends EventEmitter {
    socket = null;
    buffer = '';
    nextId = 1;
    pendingRequests = new Map();
    /**
     * Connect to server
     */
    connect(port) {
        return new Promise((resolve, reject) => {
            this.socket = net.createConnection({ port }, () => {
                resolve();
            });
            this.socket.on('data', (data) => {
                this.buffer += data.toString();
                this.processBuffer();
            });
            this.socket.on('error', (error) => {
                reject(error);
                this.emit('error', error);
            });
            this.socket.on('close', () => {
                this.emit('close');
            });
        });
    }
    processBuffer() {
        const lines = this.buffer.split('\n');
        this.buffer = lines.pop() || '';
        for (const line of lines) {
            if (!line.trim())
                continue;
            try {
                const msg = JSON.parse(line);
                this.handleMessage(msg);
            }
            catch {
                // Ignore
            }
        }
    }
    handleMessage(msg) {
        if (msg.id !== undefined && (msg.result !== undefined || msg.error !== undefined)) {
            const pending = this.pendingRequests.get(msg.id);
            if (pending) {
                this.pendingRequests.delete(msg.id);
                if (msg.error) {
                    pending.reject(new Error(msg.error.message));
                }
                else {
                    pending.resolve(msg.result);
                }
            }
        }
        else if (msg.method) {
            this.emit('notification', { method: msg.method, params: msg.params });
        }
    }
    /**
     * Send request
     */
    async request(method, params) {
        if (!this.socket) {
            throw new Error('Not connected');
        }
        const id = this.nextId++;
        const msg = {
            jsonrpc: '2.0',
            id,
            method,
            params,
        };
        return new Promise((resolve, reject) => {
            this.pendingRequests.set(id, {
                resolve: resolve,
                reject,
            });
            this.socket.write(JSON.stringify(msg) + '\n');
        });
    }
    /**
     * Send notification
     */
    notify(method, params) {
        if (!this.socket)
            return;
        const msg = {
            jsonrpc: '2.0',
            method,
            params,
        };
        this.socket.write(JSON.stringify(msg) + '\n');
    }
    /**
     * Disconnect
     */
    disconnect() {
        if (this.socket) {
            this.socket.destroy();
            this.socket = null;
        }
    }
}
// ============================================================================
// Factory Functions
// ============================================================================
/**
 * Create IDE protocol server
 */
export function createIDEServer() {
    return new IDEProtocolServer();
}
/**
 * Create IDE protocol client
 */
export function createIDEClient() {
    return new IDEProtocolClient();
}
//# sourceMappingURL=ide-protocol.js.map