"use strict";
/**
 * Intent Validator Service
 *
 * Validates natural language UI descriptions against the intent dictionary,
 * extracting UI intents and matching them to shadcn/ui component patterns.
 *
 * @module services/IntentValidator
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IntentValidator = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const chalk_1 = __importDefault(require("chalk"));
const fuse_js_1 = __importDefault(require("fuse.js"));
/**
 * Main service for validating UI intents from natural language
 */
class IntentValidator {
    /**
     * @param dictionaryPath - Optional path to custom dictionary (defaults to built-in)
     */
    constructor(dictionaryPath) {
        this.dictionaryPath =
            dictionaryPath ||
                path.join(__dirname, "..", "config", "intent-dictionary.json");
        this.dictionary = this.loadDictionary();
        this.fuse = this.initializeFuse();
    }
    /**
     * Load intent dictionary from JSON file
     */
    loadDictionary() {
        try {
            if (!fs.existsSync(this.dictionaryPath)) {
                console.warn(chalk_1.default.yellow(`⚠️  Intent dictionary not found at ${this.dictionaryPath}, using default`));
                return this.getDefaultDictionary();
            }
            const raw = fs.readFileSync(this.dictionaryPath, "utf-8");
            const dictionary = JSON.parse(raw);
            // Validate dictionary structure
            if (!dictionary.version || !dictionary.mappings) {
                throw new Error("Invalid dictionary structure");
            }
            return dictionary;
        }
        catch (error) {
            console.error(chalk_1.default.red("❌ Failed to load intent dictionary:"), error);
            return this.getDefaultDictionary();
        }
    }
    /**
     * Initialize Fuse.js for fuzzy matching
     */
    initializeFuse() {
        const searchableItems = [];
        // Build searchable items from dictionary mappings
        for (const [canonicalName, mapping] of Object.entries(this.dictionary.mappings)) {
            // Add canonical name
            searchableItems.push({
                canonical_name: canonicalName,
                search_text: canonicalName,
                type: "canonical",
                mapping: mapping,
            });
            // Add intent phrases
            for (const phrase of mapping.intent_phrases) {
                searchableItems.push({
                    canonical_name: canonicalName,
                    search_text: phrase.phrase,
                    aliases: phrase.aliases || [],
                    context_keywords: phrase.context_keywords || [],
                    type: "phrase",
                    mapping: mapping,
                });
            }
        }
        // Add synonyms
        for (const [synonym, canonicalName] of Object.entries(this.dictionary.synonyms)) {
            searchableItems.push({
                canonical_name: canonicalName,
                search_text: synonym,
                type: "synonym",
                mapping: this.dictionary.mappings[canonicalName],
            });
        }
        return new fuse_js_1.default(searchableItems, {
            keys: [
                { name: "search_text", weight: 0.7 },
                { name: "aliases", weight: 0.3 },
                { name: "context_keywords", weight: 0.2 },
            ],
            threshold: 0.4, // Lower threshold = more strict matching
            includeScore: true,
            includeMatches: true,
        });
    }
    /**
     * Get minimal default dictionary as fallback
     */
    getDefaultDictionary() {
        return {
            version: "1.0.0",
            schema_version: "1.0.0",
            last_updated: new Date().toISOString(),
            mappings: {},
            synonyms: {},
            categories: [],
            validation_config: {
                min_confidence_threshold: 0.7,
                ambiguity_threshold: 0.3,
                require_design_tokens: false,
                strict_mode: false,
            },
            usage_analytics: {
                total_validations: 0,
                intent_usage_counts: {},
                validation_failures: [],
                average_confidence: 0,
            },
        };
    }
    /**
     * Main validation method - analyzes context files and extracts UI intents
     */
    async validateContextFiles(prd, types, brand) {
        console.log(chalk_1.default.blue("🔍 Validating UI intents from context..."));
        try {
            // 1. Extract intent phrases from text
            const extractedIntents = this.extractIntentsFromText(prd, types, brand);
            console.log(chalk_1.default.gray(`   Extracted ${extractedIntents.length} potential intents`));
            // 2. Match against dictionary
            const matches = await this.matchIntents(extractedIntents);
            console.log(chalk_1.default.gray(`   Matched ${matches.length} intents`));
            // 3. Detect ambiguities
            const ambiguities = this.detectAmbiguities(matches, extractedIntents);
            // 4. Find unknown intents
            const unknowns = this.findUnknownIntents(extractedIntents, matches);
            // 5. Calculate overall confidence
            const confidence = this.calculateConfidence(matches);
            // 6. Generate clarifications if needed
            const clarifications = this.generateClarifications(ambiguities);
            // 7. Generate warnings and suggestions
            const warnings = this.generateWarnings(matches, ambiguities, unknowns);
            const suggestions = this.generateSuggestions(unknowns);
            // 8. Update analytics
            this.updateAnalytics(matches.length, confidence);
            const report = {
                validated_intents: matches,
                ambiguous_intents: ambiguities,
                unknown_intents: unknowns,
                confidence_score: confidence,
                clarifications_needed: clarifications,
                warnings,
                suggestions,
            };
            console.log(chalk_1.default.green(`✅ Validation complete: ${matches.length} validated, ${ambiguities.length} ambiguous, ${unknowns.length} unknown`));
            return report;
        }
        catch (error) {
            console.error(chalk_1.default.red("❌ Intent validation failed:"), error);
            throw error;
        }
    }
    /**
     * Extract UI intent phrases from text using pattern matching
     */
    extractIntentsFromText(prd, types, brand) {
        const extracted = [];
        const allText = `${prd}\n${types || ""}\n${brand || ""}`;
        // Define extraction patterns for common UI descriptions
        const patterns = [
            // Buttons
            {
                regex: /\b(button|btn)\s+(to|for|that)\s+([^.!?\n]+)/gi,
                confidence: 0.8,
            },
            {
                regex: /\b(submit|save|cancel|delete|edit|create)\s+(button|btn)/gi,
                confidence: 0.85,
            },
            { regex: /\b(cta|call to action)\b/gi, confidence: 0.9 },
            // Forms
            {
                regex: /\b(login|signin|sign in|authentication|auth)\s+(form|page)/gi,
                confidence: 0.95,
            },
            {
                regex: /\b(registration|signup|sign up|register)\s+(form|page)/gi,
                confidence: 0.9,
            },
            { regex: /\b(form)\s+(to|for|with)\s+([^.!?\n]+)/gi, confidence: 0.75 },
            // Inputs
            {
                regex: /\b(input|field|text box)\s+(for|to enter)\s+([^.!?\n]+)/gi,
                confidence: 0.8,
            },
            {
                regex: /\b(email|password|text|number)\s+(input|field)/gi,
                confidence: 0.85,
            },
            // Dialogs & Modals
            {
                regex: /\b(confirmation|confirm)\s+(dialog|modal|popup)/gi,
                confidence: 0.9,
            },
            {
                regex: /\b(alert|dialog|modal|popup)\s+(for|to|that)\s+([^.!?\n]+)/gi,
                confidence: 0.75,
            },
            { regex: /\b(are you sure|confirm|confirmation)\b/gi, confidence: 0.8 },
            // Navigation
            { regex: /\b(navigation|nav)\s+(menu|bar)/gi, confidence: 0.9 },
            {
                regex: /\b(menu|navbar|sidebar)\s+(with|showing|for)\s+([^.!?\n]+)/gi,
                confidence: 0.8,
            },
            // Data Display
            {
                regex: /\b(table|data table|grid)\s+(of|showing|displaying|with)\s+([^.!?\n]+)/gi,
                confidence: 0.85,
            },
            {
                regex: /\b(list|grid)\s+(of|showing)\s+([^.!?\n]+)/gi,
                confidence: 0.7,
            },
            {
                regex: /\b(card|panel)\s+(for|showing|displaying)\s+([^.!?\n]+)/gi,
                confidence: 0.75,
            },
            // Feedback
            {
                regex: /\b(error|warning|success)\s+(message|alert|notification)/gi,
                confidence: 0.85,
            },
            { regex: /\b(loading|spinner|loader)\b/gi, confidence: 0.9 },
            {
                regex: /\b(toast|notification|alert)\s+(for|to|showing)\s+([^.!?\n]+)/gi,
                confidence: 0.8,
            },
        ];
        for (const { regex, confidence } of patterns) {
            let match;
            const regexCopy = new RegExp(regex.source, regex.flags);
            while ((match = regexCopy.exec(allText)) !== null) {
                const matchedText = match[0];
                const matchIndex = match.index;
                // Extract context around the match
                const context = this.extractContext(allText, matchIndex);
                extracted.push({
                    original_text: matchedText.trim(),
                    pattern_matched: regex.source,
                    context,
                    confidence,
                });
            }
        }
        // Remove duplicates (same text)
        const unique = extracted.filter((item, index, self) => index === self.findIndex((t) => t.original_text === item.original_text));
        return unique;
    }
    /**
     * Extract surrounding context for an intent match
     */
    extractContext(text, matchIndex) {
        const contextRadius = 100;
        const before = text
            .slice(Math.max(0, matchIndex - contextRadius), matchIndex)
            .trim();
        const after = text
            .slice(matchIndex, Math.min(text.length, matchIndex + contextRadius))
            .trim();
        // Extract keywords from context
        const contextText = `${before} ${after}`.toLowerCase();
        const keywords = [];
        // Look for category keywords
        const categoryKeywords = {
            forms: ["form", "input", "field", "submit", "validate", "entry"],
            navigation: ["navigation", "menu", "nav", "link", "route", "page"],
            feedback: ["alert", "error", "success", "warning", "notify", "message"],
            "data-display": [
                "table",
                "list",
                "grid",
                "data",
                "row",
                "column",
                "display",
            ],
            layout: ["card", "panel", "container", "layout", "section"],
        };
        for (const [category, words] of Object.entries(categoryKeywords)) {
            for (const word of words) {
                if (contextText.includes(word)) {
                    keywords.push(word);
                }
            }
        }
        // Determine section (rough estimate)
        const lines = text.slice(0, matchIndex).split("\n");
        const lineNumber = lines.length;
        const section = lineNumber < 50
            ? "Introduction"
            : lineNumber < 100
                ? "Features"
                : lineNumber < 200
                    ? "Requirements"
                    : "Implementation";
        return {
            before,
            after,
            keywords: [...new Set(keywords)],
            section,
        };
    }
    /**
     * Match extracted intents against dictionary
     */
    async matchIntents(extracted) {
        const matches = [];
        for (const intent of extracted) {
            // 1. Try exact phrase matching first
            const exactMatch = this.findExactMatch(intent.original_text);
            if (exactMatch) {
                matches.push({
                    original_text: intent.original_text,
                    canonical_name: exactMatch.canonical_name,
                    intent_mapping: exactMatch,
                    confidence: 1.0,
                    extracted_context: intent.context,
                    resolved_placeholders: this.resolvePlaceholders(exactMatch, intent),
                });
                continue;
            }
            // 2. Try synonym matching
            const synonymMatch = this.findSynonymMatch(intent.original_text);
            if (synonymMatch) {
                matches.push({
                    original_text: intent.original_text,
                    canonical_name: synonymMatch.canonical_name,
                    intent_mapping: synonymMatch,
                    confidence: 0.95,
                    extracted_context: intent.context,
                    resolved_placeholders: this.resolvePlaceholders(synonymMatch, intent),
                });
                continue;
            }
            // 3. Try fuzzy matching
            const fuzzyMatches = this.fuzzyMatch(intent.original_text, intent);
            if (fuzzyMatches.length > 0) {
                const bestMatch = fuzzyMatches[0];
                if (bestMatch &&
                    bestMatch.confidence >=
                        this.dictionary.validation_config.min_confidence_threshold) {
                    matches.push({
                        original_text: intent.original_text,
                        canonical_name: bestMatch.canonical_name,
                        intent_mapping: bestMatch.mapping,
                        confidence: bestMatch.confidence,
                        extracted_context: intent.context,
                        resolved_placeholders: this.resolvePlaceholders(bestMatch.mapping, intent),
                    });
                }
            }
        }
        return matches;
    }
    /**
     * Find exact phrase match in dictionary
     */
    findExactMatch(text) {
        const lowerText = text.toLowerCase().trim();
        for (const [canonicalName, mapping] of Object.entries(this.dictionary.mappings)) {
            for (const intentPhrase of mapping.intent_phrases) {
                if (lowerText.includes(intentPhrase.phrase.toLowerCase())) {
                    return mapping;
                }
                for (const alias of intentPhrase.aliases) {
                    if (lowerText.includes(alias.toLowerCase())) {
                        return mapping;
                    }
                }
            }
        }
        return null;
    }
    /**
     * Find match via synonym lookup
     */
    findSynonymMatch(text) {
        const lowerText = text.toLowerCase().trim();
        for (const [synonym, canonicalName] of Object.entries(this.dictionary.synonyms)) {
            if (lowerText.includes(synonym.toLowerCase())) {
                return this.dictionary.mappings[canonicalName] || null;
            }
        }
        return null;
    }
    /**
     * Fuzzy match using Fuse.js for better accuracy
     */
    fuzzyMatch(text, extracted) {
        const results = [];
        // Use Fuse.js to search
        const fuseResults = this.fuse.search(text);
        for (const result of fuseResults) {
            const item = result.item;
            const score = result.score || 0;
            // Convert Fuse.js score (0-1, lower is better) to confidence (0-1, higher is better)
            const confidence = 1 - score;
            // Apply context keyword bonus
            let contextBonus = 0;
            if (item.context_keywords && extracted.context.keywords) {
                const contextMatches = item.context_keywords.filter((kw) => extracted.context.keywords.includes(kw.toLowerCase()));
                contextBonus =
                    (contextMatches.length / item.context_keywords.length) * 0.2;
            }
            const finalConfidence = Math.min(confidence + contextBonus, 1.0);
            // Only include if above threshold
            if (finalConfidence >= this.dictionary.validation_config.ambiguity_threshold) {
                results.push({
                    canonical_name: item.canonical_name,
                    confidence: finalConfidence,
                    mapping: item.mapping,
                    matched_phrase: item.search_text,
                });
            }
        }
        // Sort by confidence (highest first) and remove duplicates
        const uniqueResults = results.reduce((acc, current) => {
            const existing = acc.find((item) => item.canonical_name === current.canonical_name);
            if (!existing || current.confidence > existing.confidence) {
                return acc
                    .filter((item) => item.canonical_name !== current.canonical_name)
                    .concat(current);
            }
            return acc;
        }, []);
        return uniqueResults.sort((a, b) => b.confidence - a.confidence);
    }
    /**
     * Resolve placeholder values from context
     */
    resolvePlaceholders(mapping, intent) {
        const placeholders = {};
        // Extract common placeholders from text
        const text = intent.original_text;
        // label: try to extract from "button to X" or "button for X"
        const labelMatch = text.match(/(?:to|for|that)\s+(.+)$/i);
        if (labelMatch && labelMatch[1]) {
            placeholders.label = this.capitalize(labelMatch[1].trim());
        }
        else {
            placeholders.label = "Submit";
        }
        // size: default to medium
        placeholders.size = "default";
        // handler: generate name
        placeholders.handler =
            "handle" + this.capitalize(mapping.canonical_name.replace(/-/g, ""));
        // aria_label: use label or description
        placeholders.aria_label = placeholders.label;
        // disabled: default to false
        placeholders.disabled = "false";
        // loading: default to loading state variable
        placeholders.loading = "loading";
        return placeholders;
    }
    /**
     * Detect ambiguous intents (multiple high-confidence matches)
     */
    detectAmbiguities(matches, extracted) {
        const ambiguities = [];
        const threshold = this.dictionary.validation_config.ambiguity_threshold;
        // Check each extracted intent
        for (const intent of extracted) {
            // Find all fuzzy matches for this intent
            const fuzzyMatches = this.fuzzyMatch(intent.original_text, intent);
            // If multiple matches with similar confidence
            const highConfidenceMatches = fuzzyMatches.filter((m) => m.confidence >= threshold);
            if (highConfidenceMatches.length > 1) {
                // Check if top 2 are close in confidence (within 0.15)
                const top = highConfidenceMatches[0];
                const second = highConfidenceMatches[1];
                if (top &&
                    second &&
                    Math.abs(top.confidence - second.confidence) < 0.15) {
                    ambiguities.push({
                        original_text: intent.original_text,
                        possible_intents: highConfidenceMatches
                            .slice(0, 3)
                            .map((match) => ({
                            canonical_name: match.canonical_name,
                            confidence: match.confidence,
                            reasoning: this.generateReasoning(match.mapping, intent),
                        })),
                        context_needed: [
                            "Component type",
                            "User interaction",
                            "Visual style",
                        ],
                    });
                }
            }
        }
        return ambiguities;
    }
    /**
     * Generate reasoning for why an intent matched
     */
    generateReasoning(mapping, intent) {
        const keywords = mapping.intent_phrases[0]?.context_keywords || [];
        const matchedKeywords = keywords.filter((kw) => intent.original_text.toLowerCase().includes(kw.toLowerCase()));
        if (matchedKeywords.length > 0) {
            return `Matched keywords: ${matchedKeywords.join(", ")}`;
        }
        return `Pattern match in ${intent.context.section}`;
    }
    /**
     * Find unknown intents that couldn't be matched
     */
    findUnknownIntents(extracted, matches) {
        const unknowns = [];
        const matchedTexts = new Set(matches.map((m) => m.original_text));
        for (const intent of extracted) {
            if (!matchedTexts.has(intent.original_text)) {
                // Find similar intents
                const fuzzyMatches = this.fuzzyMatch(intent.original_text, intent);
                const similar = fuzzyMatches.slice(0, 3).map((m) => ({
                    canonical_name: m.canonical_name,
                    similarity_score: m.confidence,
                }));
                unknowns.push({
                    original_text: intent.original_text,
                    similar_intents: similar,
                    suggested_fallback: similar.length > 0 ? similar[0]?.canonical_name || "" : "",
                });
            }
        }
        return unknowns;
    }
    /**
     * Calculate overall confidence score
     */
    calculateConfidence(matches) {
        if (matches.length === 0)
            return 0;
        const sum = matches.reduce((acc, m) => acc + m.confidence, 0);
        return sum / matches.length;
    }
    /**
     * Generate clarification requests for ambiguous intents
     */
    generateClarifications(ambiguities) {
        return ambiguities.map((ambig) => ({
            question: `What type of component did you mean?`,
            original_text: ambig.original_text,
            suggested_intents: ambig.possible_intents.map((pi) => {
                const mapping = this.dictionary.mappings[pi.canonical_name];
                return {
                    canonical_name: pi.canonical_name,
                    description: mapping?.component_pattern.shadcn_components
                        ?.map((c) => c.name)
                        .join(", ") || "",
                    components: mapping?.component_pattern.shadcn_components?.map((c) => c.name) ||
                        [],
                };
            }),
        }));
    }
    /**
     * Generate warnings about validation results
     */
    generateWarnings(matches, ambiguities, unknowns) {
        const warnings = [];
        if (matches.length === 0) {
            warnings.push("No UI intents detected in context files");
        }
        if (ambiguities.length > 0) {
            warnings.push(`${ambiguities.length} ambiguous intent(s) require clarification`);
        }
        if (unknowns.length > 0) {
            warnings.push(`${unknowns.length} unknown intent(s) could not be matched`);
        }
        const lowConfidence = matches.filter((m) => m.confidence < 0.7);
        if (lowConfidence.length > 0) {
            warnings.push(`${lowConfidence.length} intent(s) have low confidence`);
        }
        return warnings;
    }
    /**
     * Generate suggestions for improving intent detection
     */
    generateSuggestions(unknowns) {
        const suggestions = [];
        if (unknowns.length > 0) {
            suggestions.push("Consider adding more specific UI component descriptions to your PRD");
            const uniqueUnknowns = unknowns.slice(0, 3);
            for (const unknown of uniqueUnknowns) {
                if (unknown.similar_intents.length > 0 && unknown.similar_intents[0]) {
                    suggestions.push(`"${unknown.original_text}" might be: ${unknown.similar_intents[0].canonical_name}`);
                }
            }
        }
        return suggestions;
    }
    /**
     * Update usage analytics
     */
    updateAnalytics(validatedCount, confidence) {
        this.dictionary.usage_analytics.total_validations++;
        const current = this.dictionary.usage_analytics.average_confidence;
        const total = this.dictionary.usage_analytics.total_validations;
        // Running average
        this.dictionary.usage_analytics.average_confidence =
            (current * (total - 1) + confidence) / total;
    }
    /**
     * Utility: capitalize first letter
     */
    capitalize(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    }
    /**
     * Get current dictionary for inspection
     */
    getDictionary() {
        return this.dictionary;
    }
    /**
     * Get list of available intents
     */
    getAvailableIntents() {
        return Object.keys(this.dictionary.mappings);
    }
    /**
     * Get intent mapping by canonical name
     */
    getIntentMapping(canonicalName) {
        return this.dictionary.mappings[canonicalName] || null;
    }
}
exports.IntentValidator = IntentValidator;
//# sourceMappingURL=IntentValidator.js.map