/**
 * LSP Manager
 *
 * Manages multiple LSP clients and provides unified interface for semantic analysis.
 * Automatically detects and initializes appropriate language servers.
 *
 * For TypeScript projects, uses TsServerClient (direct tsserver protocol) instead
 * of typescript-language-server, which has issues with large projects (>500 files).
 *
 * @module core/lsp/lsp-manager
 */
import { LSPClient, detectLSPServers, } from './lsp-client.js';
import { TsServerClient } from './tsserver-client.js';
import { TimeoutResolver } from './config/timeout-resolver.js';
import { parseLspConfig } from './config/lsp-config.js';
import { consoleLogger as logger } from '../../utils/logger.js';
import * as path from 'path';
import * as fs from 'fs';
/**
 * Manages LSP clients for multiple languages
 */
export class LSPManager {
    constructor(options) {
        this.clients = new Map();
        this.initialized = false;
        this.projectRoot = options.projectRoot;
        const config = options.lspConfig ?? parseLspConfig({});
        this.timeoutResolver = new TimeoutResolver(config);
    }
    /**
     * Initialize all available LSP servers
     * For TypeScript, uses TsServerClient (direct protocol) instead of
     * typescript-language-server which has issues with large projects.
     */
    async initialize() {
        if (this.initialized)
            return;
        try {
            // Check for TypeScript project first - use TsServerClient directly
            const hasTypeScript = this.hasTypeScriptProject();
            if (hasTypeScript) {
                logger.debug('TypeScript project detected, using direct tsserver client');
                // Get timeout in ms (TimeoutResolver returns seconds)
                const timeoutMs = this.timeoutResolver.getTimeout('typescript') * 1000;
                const tsClient = new TsServerClient(this.projectRoot, { timeoutMs });
                const success = await tsClient.initialize();
                if (success) {
                    this.clients.set('typescript', tsClient);
                    logger.info('TsServer client for typescript initialized');
                }
                else {
                    logger.warn('Failed to initialize TsServer client');
                }
            }
            // Detect and initialize other LSP servers (Python, Go, etc.)
            const servers = await detectLSPServers(this.projectRoot);
            const nonTsServers = servers.filter((s) => !s.command.includes('typescript'));
            if (nonTsServers.length > 0) {
                logger.info(`Found ${nonTsServers.length} additional LSP server(s)`);
                for (const serverConfig of nonTsServers) {
                    const lang = this.getLanguageFromCommand(serverConfig.command);
                    // Get timeout in ms (TimeoutResolver returns seconds)
                    const timeoutMs = this.timeoutResolver.getTimeout(lang) * 1000;
                    const client = new LSPClient({ ...serverConfig, timeoutMs });
                    const success = await client.initialize();
                    if (success) {
                        this.clients.set(lang, client);
                        logger.info(`LSP client for ${lang} initialized`);
                    }
                    else {
                        logger.warn(`Failed to initialize LSP for ${serverConfig.command}`);
                    }
                }
            }
            if (this.clients.size === 0) {
                logger.info('No code intelligence clients available. Semantic analysis will use grep fallback.');
            }
            this.initialized = true;
        }
        catch (error) {
            logger.error(`LSP initialization failed: ${error}`);
            this.initialized = true; // Continue without LSP
        }
    }
    /**
     * Check if project has TypeScript configuration.
     * Requires tsconfig.json (or jsconfig.json) - not just package.json,
     * since package.json alone doesn't indicate a TypeScript project
     * (Python, Go, etc. projects may have package.json for tooling).
     */
    hasTypeScriptProject() {
        return (fs.existsSync(path.join(this.projectRoot, 'tsconfig.json')) ||
            fs.existsSync(path.join(this.projectRoot, 'jsconfig.json')));
    }
    /**
     * Get language from LSP command
     * Supports 10 languages: TypeScript, Python, Go, Rust, Java, C#, Kotlin, Swift, PHP, Ruby
     */
    getLanguageFromCommand(command) {
        // TypeScript
        if (command.includes('typescript'))
            return 'typescript';
        // Python
        if (command.includes('pylsp') || command.includes('pyright'))
            return 'python';
        // Go
        if (command.includes('gopls'))
            return 'go';
        // Rust
        if (command.includes('rust-analyzer'))
            return 'rust';
        // Java
        if (command.includes('jdtls'))
            return 'java';
        // C#
        if (command.includes('csharp-ls') || command.includes('OmniSharp'))
            return 'csharp';
        // Kotlin
        if (command.includes('kotlin-language-server'))
            return 'kotlin';
        // Swift
        if (command.includes('sourcekit-lsp'))
            return 'swift';
        // PHP
        if (command.includes('intelephense') || command.includes('phpactor'))
            return 'php';
        // Ruby
        if (command.includes('solargraph'))
            return 'ruby';
        return 'unknown';
    }
    /**
     * Get code intelligence client for a file
     * Maps file extensions to language clients
     */
    getClientForFile(filePath) {
        const ext = path.extname(filePath);
        const langMap = {
            // TypeScript/JavaScript
            '.ts': 'typescript',
            '.tsx': 'typescript',
            '.js': 'typescript',
            '.jsx': 'typescript',
            '.mjs': 'typescript',
            '.cjs': 'typescript',
            // Python
            '.py': 'python',
            '.pyw': 'python',
            '.pyi': 'python',
            // Go
            '.go': 'go',
            // Rust
            '.rs': 'rust',
            // Java
            '.java': 'java',
            // C#
            '.cs': 'csharp',
            // Kotlin
            '.kt': 'kotlin',
            '.kts': 'kotlin',
            // Swift
            '.swift': 'swift',
            // PHP
            '.php': 'php',
            // Ruby
            '.rb': 'ruby',
            '.rake': 'ruby',
        };
        const lang = langMap[ext];
        return lang ? this.clients.get(lang) || null : null;
    }
    /**
     * Check if LSP is available for a file
     */
    isAvailableForFile(filePath) {
        return this.getClientForFile(filePath) !== null;
    }
    /**
     * Go to definition using LSP
     */
    async goToDefinition(filePath, line, character) {
        if (!this.initialized) {
            await this.initialize();
        }
        const client = this.getClientForFile(filePath);
        if (!client) {
            logger.debug(`No LSP client available for ${filePath}`);
            return null;
        }
        return await client.goToDefinition(filePath, line, character);
    }
    /**
     * Find references using LSP
     */
    async findReferences(filePath, line, character) {
        if (!this.initialized) {
            await this.initialize();
        }
        const client = this.getClientForFile(filePath);
        if (!client) {
            logger.debug(`No LSP client available for ${filePath}`);
            return null;
        }
        return await client.findReferences(filePath, line, character);
    }
    /**
     * Get hover information using LSP
     */
    async hover(filePath, line, character) {
        if (!this.initialized) {
            await this.initialize();
        }
        const client = this.getClientForFile(filePath);
        if (!client) {
            logger.debug(`No LSP client available for ${filePath}`);
            return null;
        }
        return await client.hover(filePath, line, character);
    }
    /**
     * Get document symbols using LSP
     */
    async documentSymbols(filePath) {
        if (!this.initialized) {
            await this.initialize();
        }
        const client = this.getClientForFile(filePath);
        if (!client) {
            logger.debug(`No LSP client available for ${filePath}`);
            return null;
        }
        return await client.documentSymbols(filePath);
    }
    /**
     * Search workspace symbols using LSP
     */
    async workspaceSymbols(query) {
        if (!this.initialized) {
            await this.initialize();
        }
        // Use first available client for workspace symbol search
        const client = this.clients.values().next().value;
        if (!client) {
            logger.debug('No LSP clients available for workspace symbol search');
            return null;
        }
        return await client.workspaceSymbols(query);
    }
    /**
     * Find all symbols in a file (legacy method for living docs extraction)
     */
    async findSymbols(filePath) {
        const result = await this.documentSymbols(filePath);
        if (!result || !result.success) {
            return [];
        }
        return result.symbols.map((s) => s.name);
    }
    /**
     * Shutdown all LSP clients
     */
    async shutdown() {
        for (const [lang, client] of this.clients) {
            logger.info(`Shutting down LSP client for ${lang}`);
            await client.shutdown();
        }
        this.clients.clear();
        this.initialized = false;
    }
    /**
     * Get statistics about LSP usage
     */
    getStatistics() {
        return {
            availableLanguages: Array.from(this.clients.keys()),
            clientCount: this.clients.size
        };
    }
    /**
     * Warm up all LSP clients by triggering workspace indexing
     * This should be called on session start to prevent timeout on first query
     * v1.0.197: Added for session-start warm-up integration
     *
     * @param entryFiles Optional list of key project files to pre-open
     */
    async warmup(entryFiles) {
        if (!this.initialized) {
            await this.initialize();
        }
        const warmedUp = [];
        const failed = [];
        for (const [lang, client] of this.clients) {
            try {
                const success = await client.warmup(entryFiles);
                if (success) {
                    warmedUp.push(lang);
                }
                else {
                    failed.push(lang);
                }
            }
            catch (error) {
                logger.warn(`Warmup failed for ${lang}: ${error}`);
                failed.push(lang);
            }
        }
        return {
            success: failed.length === 0,
            warmedUp,
            failed
        };
    }
    /**
     * Check if all clients are warmed up
     */
    isWarmedUp() {
        if (this.clients.size === 0)
            return true;
        for (const client of this.clients.values()) {
            if (!client.isWarmedUp())
                return false;
        }
        return true;
    }
}
/**
 * Global LSP manager instance
 */
let globalLSPManager = null;
/**
 * Get or create global LSP manager
 */
export function getGlobalLSPManager(projectRoot) {
    if (!globalLSPManager && projectRoot) {
        globalLSPManager = new LSPManager({ projectRoot });
    }
    if (!globalLSPManager) {
        throw new Error('LSP manager not initialized. Provide projectRoot on first call.');
    }
    return globalLSPManager;
}
/**
 * Shutdown global LSP manager
 */
export async function shutdownGlobalLSPManager() {
    if (globalLSPManager) {
        await globalLSPManager.shutdown();
        globalLSPManager = null;
    }
}
//# sourceMappingURL=lsp-manager.js.map