/**
 * LiteLLM Client - Bridge between CCW and ccw-litellm Python package
 * Provides LLM chat and embedding capabilities via spawned Python process
 *
 * Features:
 * - Chat completions with multiple models
 * - Text embeddings generation
 * - Configuration management
 * - JSON protocol communication
 */
import { spawn } from 'child_process';
import { existsSync } from 'fs';
import { join } from 'path';
import { getCodexLensPython, getCodexLensVenvDir } from '../utils/codexlens-path.js';
// Platform-specific constants for CodexLens venv
const IS_WINDOWS = process.platform === 'win32';
const CODEXLENS_VENV = getCodexLensVenvDir();
const VENV_BIN_DIR = IS_WINDOWS ? 'Scripts' : 'bin';
const PYTHON_EXECUTABLE = IS_WINDOWS ? 'python.exe' : 'python';
/**
 * Get the Python path from CodexLens venv
 * Falls back to system 'python' if venv doesn't exist
 * @returns Path to Python executable
 */
export function getCodexLensVenvPython() {
    const venvPython = join(CODEXLENS_VENV, VENV_BIN_DIR, PYTHON_EXECUTABLE);
    if (existsSync(venvPython)) {
        return venvPython;
    }
    // Fallback to system Python if venv not available
    return 'python';
}
/**
 * Get the Python path from CodexLens venv using centralized path utility
 * Falls back to system 'python' if venv doesn't exist
 * @returns Path to Python executable
 */
export function getCodexLensPythonPath() {
    const codexLensPython = getCodexLensPython();
    if (existsSync(codexLensPython)) {
        return codexLensPython;
    }
    // Fallback to system Python if venv not available
    return 'python';
}
export class LiteLLMClient {
    pythonPath;
    configPath;
    timeout;
    constructor(config = {}) {
        this.pythonPath = config.pythonPath || getCodexLensVenvPython();
        this.configPath = config.configPath;
        this.timeout = config.timeout || 60000;
    }
    /**
     * Execute Python ccw-litellm command
     */
    async executePython(args, options = {}) {
        const timeout = options.timeout || this.timeout;
        return new Promise((resolve, reject) => {
            const proc = spawn(this.pythonPath, ['-m', 'ccw_litellm.cli', ...args], {
                stdio: ['pipe', 'pipe', 'pipe'],
                env: { ...process.env }
            });
            let stdout = '';
            let stderr = '';
            let timedOut = false;
            // Set up timeout
            const timeoutId = setTimeout(() => {
                timedOut = true;
                proc.kill('SIGTERM');
                reject(new Error(`Command timed out after ${timeout}ms`));
            }, timeout);
            proc.stdout.on('data', (data) => {
                stdout += data.toString();
            });
            proc.stderr.on('data', (data) => {
                stderr += data.toString();
            });
            proc.on('error', (error) => {
                clearTimeout(timeoutId);
                reject(new Error(`Failed to spawn Python process: ${error.message}`));
            });
            proc.on('close', (code) => {
                clearTimeout(timeoutId);
                if (timedOut) {
                    return; // Already rejected
                }
                if (code === 0) {
                    resolve(stdout.trim());
                }
                else {
                    const errorMsg = stderr.trim() || `Process exited with code ${code}`;
                    reject(new Error(errorMsg));
                }
            });
        });
    }
    /**
     * Check if ccw-litellm is available
     */
    async isAvailable() {
        try {
            // Increased timeout to 15s for Python cold start
            await this.executePython(['version'], { timeout: 15000 });
            return true;
        }
        catch {
            return false;
        }
    }
    /**
     * Get status information
     */
    async getStatus() {
        try {
            // Increased timeout to 15s for Python cold start
            const output = await this.executePython(['version'], { timeout: 15000 });
            // Parse "ccw-litellm 0.1.0" format
            const versionMatch = output.trim().match(/ccw-litellm\s+([\d.]+)/);
            const version = versionMatch ? versionMatch[1] : output.trim();
            return {
                available: true,
                version
            };
        }
        catch (error) {
            return {
                available: false,
                error: error.message
            };
        }
    }
    /**
     * Get current configuration
     */
    async getConfig() {
        // config command outputs JSON by default, no --json flag needed
        const output = await this.executePython(['config']);
        return JSON.parse(output);
    }
    /**
     * Generate embeddings for texts
     */
    async embed(texts, model = 'default') {
        if (!texts || texts.length === 0) {
            throw new Error('texts array cannot be empty');
        }
        const args = ['embed', '--model', model, '--output', 'json'];
        // Add texts as arguments
        for (const text of texts) {
            args.push(text);
        }
        const output = await this.executePython(args, { timeout: this.timeout * 2 });
        const vectors = JSON.parse(output);
        return {
            vectors,
            dimensions: vectors[0]?.length || 0,
            model
        };
    }
    /**
     * Chat with LLM
     */
    async chat(message, model = 'default') {
        if (!message) {
            throw new Error('message cannot be empty');
        }
        const args = ['chat', '--model', model, message];
        return this.executePython(args, { timeout: this.timeout * 2 });
    }
    /**
     * Multi-turn chat with messages array
     */
    async chatMessages(messages, model = 'default') {
        if (!messages || messages.length === 0) {
            throw new Error('messages array cannot be empty');
        }
        // For now, just use the last user message
        // TODO: Implement full message history support in ccw-litellm
        const lastMessage = messages[messages.length - 1];
        const content = await this.chat(lastMessage.content, model);
        return {
            content,
            model,
            usage: undefined // TODO: Add usage tracking
        };
    }
}
// Singleton instance
let _client = null;
/**
 * Get or create singleton LiteLLM client
 */
export function getLiteLLMClient(config) {
    if (!_client) {
        _client = new LiteLLMClient(config);
    }
    return _client;
}
/**
 * Check if LiteLLM is available
 */
export async function checkLiteLLMAvailable() {
    try {
        const client = getLiteLLMClient();
        return await client.isAvailable();
    }
    catch {
        return false;
    }
}
/**
 * Get LiteLLM status
 */
export async function getLiteLLMStatus() {
    try {
        const client = getLiteLLMClient();
        return await client.getStatus();
    }
    catch (error) {
        return {
            available: false,
            error: error.message
        };
    }
}
//# sourceMappingURL=litellm-client.js.map