/**
 * Tool Registry - Centralized Tool Search and Management
 * @module core/tools/tool-registry
 * @since v0.35.0
 */
import * as path from 'path';
import * as fs from '../../utils/fs-native.js';
import { consoleLogger } from '../../utils/logger.js';
import { ToolEventBus } from './tool-event-bus.js';
import { ToolIndexBuilder } from './tool-index-builder.js';
import { ToolSearchEngine } from './tool-search-engine.js';
const REGISTRY_VERSION = '1.0.0';
const REGISTRY_FILENAME = 'tool-registry.json';
export class ToolRegistry {
    constructor(projectRoot, options = {}) {
        this.projectRoot = projectRoot;
        this.registryPath = path.join(projectRoot, '.specweave', 'state', REGISTRY_FILENAME);
        this.logger = options.logger ?? consoleLogger;
        this.eventBus = options.eventBus ?? new ToolEventBus({ logger: this.logger });
        this.tools = new Map();
        this.loadedTools = new Map();
        this.keywordIndex = new Map();
        this.searchEngine = new ToolSearchEngine();
        this.indexBuilder = new ToolIndexBuilder(projectRoot, { logger: this.logger });
        this.initialized = false;
        this.metadata = null;
    }
    async ensureInitialized() {
        if (this.initialized)
            return;
        await this.load();
        this.initialized = true;
    }
    async load() {
        try {
            if (fs.existsSync(this.registryPath)) {
                const content = fs.readFileSync(this.registryPath, 'utf-8');
                const data = JSON.parse(content);
                this.tools.clear();
                this.keywordIndex.clear();
                for (const [id, tool] of Object.entries(data.tools)) {
                    this.tools.set(id, tool);
                }
                if (data.keywordIndex) {
                    for (const [keyword, toolIds] of Object.entries(data.keywordIndex)) {
                        this.keywordIndex.set(keyword, new Set(toolIds));
                    }
                }
                this.metadata = data.metadata;
                this.searchEngine.buildIndex(Array.from(this.tools.values()));
                this.logger.debug(`Loaded ${this.tools.size} tools from registry`);
            }
        }
        catch (error) {
            this.logger.error(`Failed to load tool registry: ${error}`);
            throw error;
        }
    }
    async save() {
        try {
            const stateDir = path.dirname(this.registryPath);
            if (!fs.existsSync(stateDir))
                fs.mkdirSync(stateDir, { recursive: true });
            const keywordIndex = {};
            for (const [keyword, toolIds] of this.keywordIndex) {
                keywordIndex[keyword] = Array.from(toolIds);
            }
            const data = {
                version: REGISTRY_VERSION,
                tools: Object.fromEntries(this.tools),
                metadata: {
                    created: this.metadata?.created ?? new Date().toISOString(),
                    lastUpdated: new Date().toISOString(),
                    toolCount: this.tools.size,
                    buildDuration: this.metadata?.buildDuration,
                },
                keywordIndex,
            };
            fs.writeFileSync(this.registryPath, JSON.stringify(data, null, 2));
            this.logger.debug(`Saved ${this.tools.size} tools to registry`);
        }
        catch (error) {
            this.logger.error(`Failed to save tool registry: ${error}`);
            throw error;
        }
    }
    async rebuild(options = {}) {
        const startTime = Date.now();
        try {
            const result = await this.indexBuilder.buildIndex(options);
            if (result.success) {
                this.tools.clear();
                this.keywordIndex.clear();
                this.loadedTools.clear();
                const tools = this.indexBuilder.getIndexedTools();
                for (const tool of tools) {
                    this.tools.set(tool.id, tool);
                    for (const keyword of tool.keywords) {
                        if (!this.keywordIndex.has(keyword))
                            this.keywordIndex.set(keyword, new Set());
                        this.keywordIndex.get(keyword).add(tool.id);
                    }
                    if (options.emitEvents) {
                        this.eventBus.emit({ type: 'ToolIndexed', tool, timestamp: new Date().toISOString() });
                    }
                }
                this.searchEngine.buildIndex(tools);
                this.metadata = {
                    created: this.metadata?.created ?? new Date().toISOString(),
                    lastUpdated: new Date().toISOString(),
                    toolCount: this.tools.size,
                    buildDuration: Date.now() - startTime,
                };
                await this.save();
                if (options.emitEvents) {
                    this.eventBus.emit({ type: 'RegistryRebuilt', result, timestamp: new Date().toISOString() });
                }
                this.logger.info(`Tool registry rebuilt: ${result.toolCount} tools in ${result.duration}ms`);
            }
            return result;
        }
        catch (error) {
            this.logger.error(`Failed to rebuild tool registry: ${error}`);
            return {
                success: false, toolCount: 0, skillCount: 0, agentCount: 0, commandCount: 0,
                duration: Date.now() - startTime, errors: [String(error)], warnings: [],
            };
        }
    }
    async search(query, options = {}) {
        await this.ensureInitialized();
        return this.searchEngine.search(query, options);
    }
    async findBestMatch(intent) {
        await this.ensureInitialized();
        const results = await this.search(intent, { limit: 1 });
        return results.length > 0 ? results[0] : null;
    }
    async suggest(partialQuery, limit = 5) {
        await this.ensureInitialized();
        return this.searchEngine.suggest(partialQuery, limit);
    }
    async getTool(id) {
        await this.ensureInitialized();
        return this.tools.get(id) ?? null;
    }
    async getLoadedTool(id) {
        await this.ensureInitialized();
        if (this.loadedTools.has(id))
            return this.loadedTools.get(id);
        return this.loadToolContent(id);
    }
    async listTools(filter) {
        await this.ensureInitialized();
        let tools = Array.from(this.tools.values());
        if (filter?.type)
            tools = tools.filter((t) => t.type === filter.type);
        if (filter?.plugin)
            tools = tools.filter((t) => t.pluginName === filter.plugin);
        return tools;
    }
    async hasTool(id) {
        await this.ensureInitialized();
        return this.tools.has(id);
    }
    async count(filter) {
        return (await this.listTools(filter)).length;
    }
    async loadToolContent(id) {
        await this.ensureInitialized();
        const tool = this.tools.get(id);
        if (!tool)
            return null;
        const fullPath = path.isAbsolute(tool.relativePath)
            ? tool.relativePath
            : path.join(this.projectRoot, tool.relativePath);
        if (!fs.existsSync(fullPath)) {
            this.logger.warn(`Tool file not found: ${fullPath}`);
            return null;
        }
        try {
            const content = fs.readFileSync(fullPath, 'utf-8');
            const loadedTool = {
                ...tool,
                content,
                systemPrompt: tool.type === 'agent' ? content : undefined,
                loaded: true,
            };
            this.loadedTools.set(id, loadedTool);
            this.eventBus.emit({ type: 'ToolLoaded', toolId: id, tool: loadedTool, timestamp: new Date().toISOString() });
            return loadedTool;
        }
        catch (error) {
            this.logger.error(`Failed to load tool content for ${id}: ${error}`);
            return null;
        }
    }
    async preloadTools(ids) {
        await this.ensureInitialized();
        for (const id of ids) {
            if (!this.loadedTools.has(id))
                await this.loadToolContent(id);
        }
    }
    unloadTool(id) { this.loadedTools.delete(id); }
    unloadAll() { this.loadedTools.clear(); }
    getEventBus() { return this.eventBus; }
    recordToolSelection(toolId, query, score, context) {
        this.eventBus.emit({ type: 'ToolSelected', toolId, query, score, context, timestamp: new Date().toISOString() });
    }
    async getStats() {
        await this.ensureInitialized();
        const plugins = new Set();
        let skillCount = 0;
        let agentCount = 0;
        let commandCount = 0;
        for (const tool of this.tools.values()) {
            plugins.add(tool.pluginName);
            if (tool.type === 'skill')
                skillCount++;
            else if (tool.type === 'agent')
                agentCount++;
            else if (tool.type === 'command')
                commandCount++;
        }
        return {
            totalTools: this.tools.size,
            skillCount,
            agentCount,
            commandCount,
            loadedCount: this.loadedTools.size,
            pluginCount: plugins.size,
            keywordCount: this.keywordIndex.size,
            lastUpdated: this.metadata?.lastUpdated ?? 'never',
        };
    }
}
//# sourceMappingURL=tool-registry.js.map