import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
// Helper to get __dirname in ESM and CJS (bundled)
const _filename = typeof __filename !== 'undefined'
    ? __filename
    : (typeof import.meta !== 'undefined' && import.meta.url ? fileURLToPath(import.meta.url) : '');
const _dirname = typeof __dirname !== 'undefined'
    ? __dirname
    : (_filename ? path.dirname(_filename) : '');
export class NodeSchemaProvider {
    index = null;
    enrichedIndex = null;
    enrichedIndexPath;
    constructor(customIndexPath) {
        if (customIndexPath) {
            this.enrichedIndexPath = customIndexPath;
        }
        else {
            // Resolve path to assets/n8n-nodes-technical.json
            // In dist structure: dist/services/node-schema-provider.js -> dist/assets/n8n-nodes-technical.json
            this.enrichedIndexPath = path.resolve(_dirname, '../assets/n8n-nodes-technical.json');
        }
    }
    loadIndex() {
        if (this.index)
            return;
        // Load technical index (required)
        if (!fs.existsSync(this.enrichedIndexPath)) {
            throw new Error(`Technical node index not found at: ${this.enrichedIndexPath}\n` +
                `Please run the build process: npm run build in packages/agent-cli`);
        }
        try {
            const content = fs.readFileSync(this.enrichedIndexPath, 'utf-8');
            this.enrichedIndex = JSON.parse(content);
            this.index = this.enrichedIndex;
        }
        catch (error) {
            throw new Error(`Failed to load technical node index: ${error.message}\n` +
                `The index file may be corrupted. Try rebuilding: npm run build in packages/agent-cli`);
        }
    }
    /**
     * Get the full JSON schema for a specific node by name.
     * Returns null if not found.
     */
    getNodeSchema(nodeName) {
        this.loadIndex();
        // Direct match
        if (this.index.nodes[nodeName]) {
            return this.index.nodes[nodeName];
        }
        // Case insensitive fallback
        const lowerName = nodeName.toLowerCase();
        const found = Object.keys(this.index.nodes).find(k => k.toLowerCase() === lowerName);
        return found ? this.index.nodes[found] : null;
    }
    /**
     * Calculate relevance score for a node based on query
     */
    calculateRelevance(query, node, key) {
        const lowerQuery = query.toLowerCase();
        let score = 0;
        // Exact name match (highest priority)
        if (key.toLowerCase() === lowerQuery) {
            score += 1000;
        }
        else if (key.toLowerCase().includes(lowerQuery)) {
            score += 500;
        }
        // Display name match
        const displayName = (node.displayName || '').toLowerCase();
        if (displayName === lowerQuery) {
            score += 800;
        }
        else if (displayName.includes(lowerQuery)) {
            score += 400;
        }
        // Keyword match (from enriched metadata)
        if (node.metadata?.keywords) {
            const keywords = node.metadata.keywords;
            if (keywords.includes(lowerQuery)) {
                score += 300;
            }
            // Partial keyword match
            const matchingKeywords = keywords.filter((k) => k.includes(lowerQuery) || lowerQuery.includes(k));
            score += matchingKeywords.length * 50;
        }
        // Operations match
        if (node.metadata?.operations) {
            const matchingOps = node.metadata.operations.filter((op) => op.toLowerCase().includes(lowerQuery));
            score += matchingOps.length * 100;
        }
        // Use cases match
        if (node.metadata?.useCases) {
            const matchingUseCases = node.metadata.useCases.filter((uc) => uc.toLowerCase().includes(lowerQuery));
            score += matchingUseCases.length * 80;
        }
        // Description match (lower priority)
        const description = (node.description || '').toLowerCase();
        if (description.includes(lowerQuery)) {
            score += 100;
        }
        // Bonus for nodes with high keyword scores (AI/popular nodes)
        if (node.metadata?.keywordScore) {
            score += node.metadata.keywordScore * 0.5;
        }
        // Multi-word query: check if all words match
        const queryWords = lowerQuery.split(/\s+/).filter(w => w.length > 2);
        if (queryWords.length > 1) {
            const allFields = [
                key.toLowerCase(),
                displayName,
                description,
                ...(node.metadata?.keywords || []),
                ...(node.metadata?.operations || []),
                ...(node.metadata?.useCases || [])
            ].join(' ');
            const matchedWords = queryWords.filter(word => allFields.includes(word));
            if (matchedWords.length === queryWords.length) {
                score += 200 * queryWords.length;
            }
        }
        return score;
    }
    /**
     * Fuzzy search for nodes with improved relevance scoring.
     * Returns a list of matches (stub only, not full schema).
     */
    searchNodes(query, limit = 20) {
        this.loadIndex();
        const lowerQuery = query.toLowerCase();
        const scoredResults = [];
        for (const [key, node] of Object.entries(this.index.nodes)) {
            const score = this.calculateRelevance(query, node, key);
            if (score > 0) {
                scoredResults.push({
                    name: node.name || key,
                    displayName: node.displayName || key,
                    description: node.description || '',
                    version: node.version,
                    keywords: node.metadata?.keywords || [],
                    operations: node.metadata?.operations || [],
                    useCases: node.metadata?.useCases || [],
                    relevanceScore: score,
                    score
                });
            }
        }
        // Sort by score (highest first) and take top results
        scoredResults.sort((a, b) => b.score - a.score);
        return scoredResults.slice(0, limit).map(({ score, ...rest }) => rest);
    }
    /**
     * List all available nodes (compact format).
     */
    listAllNodes() {
        this.loadIndex();
        return Object.values(this.index.nodes).map(node => ({
            name: node.name,
            displayName: node.displayName,
            description: node.description || '',
            version: node.version,
            keywords: node.metadata?.keywords || [],
            operations: node.metadata?.operations || [],
            useCases: node.metadata?.useCases || []
        }));
    }
}
//# sourceMappingURL=node-schema-provider.js.map