"use strict";
/**
 * Vision Element Finder Service
 *
 * Combines vision AI with DOM understanding to identify and interact with UI elements.
 * This is the revolutionary approach: instead of brittle CSS selectors,
 * we "see" the page and find elements like a human would.
 */
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;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.VisionElementFinder = void 0;
const AICore_1 = require("../core/ai/AICore");
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const uuid_1 = require("uuid");
class VisionElementFinder {
    constructor(projectPath, confidenceThreshold = 70 // Minimum confidence to trust vision over DOM
    ) {
        this.aiCore = AICore_1.AICore.getInstance({
            fallbackEnabled: true,
            workingDirectory: projectPath
        });
        this.screenshotsDir = path.join(projectPath, ".mycontext", "vision-screenshots");
        this.confidenceThreshold = confidenceThreshold;
        fs.ensureDirSync(this.screenshotsDir);
    }
    /**
     * Analyze the current page using vision
     */
    async analyzePage(page) {
        // Take screenshot
        const screenshotPath = await this.takeScreenshot(page);
        // Get DOM context for hybrid approach
        const domContext = await this.getDOMContext(page);
        // Analyze with vision AI (fallback to DOM/text-only if vision fails)
        const prompt = this.buildAnalysisPrompt(domContext);
        let visionAnalysis = null;
        try {
            visionAnalysis = await this.analyzeScreenshotWithPrompt(screenshotPath, prompt);
        }
        catch (error) {
            const msg = error?.message || String(error);
            console.warn(`⚠️ Vision analysis failed, switching to DOM-only analysis: ${msg.substring(0, 120)}`);
        }
        if (!visionAnalysis || (Object.keys(visionAnalysis).length === 0 && !visionAnalysis.aiInterpretation)) {
            // DOM-only minimal analysis so the navigator can keep running
            const domOnlySummary = await this.getDomOnlySummary(page);
            return {
                screenshot: screenshotPath,
                timestamp: new Date().toISOString(),
                elements: [],
                interactiveElements: [],
                uiState: {
                    url: page.url(),
                    loadingState: "loaded",
                    modalOpen: false,
                    mainContent: domOnlySummary,
                },
                designSystem: { colors: [], typography: [], spacing: "" },
                layoutStructure: { type: "responsive", sections: [] },
                aiInterpretation: `DOM-only mode (vision unavailable). Summary:\n${domOnlySummary}`,
            };
        }
        return {
            screenshot: screenshotPath,
            timestamp: new Date().toISOString(),
            elements: [],
            interactiveElements: [],
            uiState: { url: page.url(), loadingState: "loaded", modalOpen: false, mainContent: "Analysis failed or incomplete" },
            designSystem: { colors: [], typography: [], spacing: "" },
            layoutStructure: { type: "unknown", sections: [] },
            aiInterpretation: "Vision analysis failed to return a valid structure",
            ...visionAnalysis
        };
    }
    async getDomOnlySummary(page) {
        try {
            return await page.evaluate(() => {
                const pick = (s) => Array.from(document.querySelectorAll(s)).slice(0, 20);
                const text = (el) => (el.textContent || "").replace(/\s+/g, " ").trim();
                const buttons = pick("button,[role='button'],a, input, select, textarea");
                const lines = [];
                const title = document.title ? `title: ${document.title}` : "";
                if (title)
                    lines.push(title);
                const headings = pick("h1,h2,h3").map(text).filter(Boolean).slice(0, 6);
                if (headings.length)
                    lines.push(`headings: ${headings.join(" | ")}`);
                const actions = buttons
                    .map((el) => {
                    const tag = el.tagName.toLowerCase();
                    const label = el.getAttribute("aria-label") ||
                        el.getAttribute("name") ||
                        el.getAttribute("placeholder") ||
                        text(el);
                    const href = el.href ? ` href=${el.href}` : "";
                    return `${tag}: ${label || "(no label)"}${href}`;
                })
                    .filter((s) => !s.includes("(no label)"))
                    .slice(0, 20);
                if (actions.length)
                    lines.push(`interactive: ${actions.join(" | ")}`);
                const bodyText = text(document.body).slice(0, 800);
                if (bodyText)
                    lines.push(`body: ${bodyText}`);
                return lines.join("\n");
            });
        }
        catch (e) {
            return `DOM-only summary failed: ${e?.message || String(e)}`;
        }
    }
    async decideNextActionDomOnly(page, mission, previousSteps, primeObjective, testData) {
        const domContext = await this.getDOMContext(page);
        const domOnlySummary = await this.getDomOnlySummary(page);
        const prompt = `You are an AI agent driving a browser WITHOUT screenshots (vision unavailable).
You must navigate by interpreting DOM context and page text.

PRIME OBJECTIVE: "${primeObjective}"
MISSION: ${mission}
CURRENT URL: ${page.url()}

DOM-ONLY SUMMARY:
${domOnlySummary}

DOM CONTEXT:
${domContext}

PREVIOUS STEPS:
${previousSteps.map((s, i) => `${i + 1}. ${s}`).join("\n")}

STRUCTURED TEST DATA (JSON):
${testData ? JSON.stringify(testData, null, 2) : "None"}

Return JSON ONLY with:
{
  "action": "click|fill|scroll|wait|navigate|complete|multi_action",
  "confidence": number,
  "reasoning": string,
  "visualContext": string,
  "targetElement": { "description": string, "domSelector": string, "confidence": number },
  "value": string,
  "url": string,
  "subActions": [],
  "intent": string,
  "alignsWithPrimeObjective": true,
  "gravityScore": number
}

Rules:
- Prefer DOM selectors in targetElement.domSelector (Playwright locator-compatible).
- Use "navigate" with url only for direct URL jumps.
- DO NOT return comments inside JSON.`;
        const text = await this.aiCore.generateText(prompt);
        return this.parseJsonFromModel(text);
    }
    parseJsonFromModel(text) {
        let jsonText = text.trim();
        const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
        if (codeBlockMatch && codeBlockMatch[1])
            jsonText = codeBlockMatch[1].trim();
        else {
            const firstBrace = text.indexOf("{");
            const lastBrace = text.lastIndexOf("}");
            if (firstBrace !== -1 && lastBrace !== -1)
                jsonText = text.substring(firstBrace, lastBrace + 1);
        }
        // reuse same sanitization as vision parsing
        const sanitize = (raw) => {
            let s = raw.trim();
            s = s.replace(/\/\*[\s\S]*?\*\//g, "");
            s = s.replace(/(^|\s)\/\/.*$/gm, "$1");
            s = s.replace(/,\s*([}\]])/g, "$1");
            return s.trim();
        };
        jsonText = sanitize(jsonText);
        try {
            return JSON.parse(jsonText);
        }
        catch (e) {
            // best-effort repair
            const repaired = sanitize(jsonText);
            return JSON.parse(repaired);
        }
    }
    /**
     * Find an element by natural language description
     */
    async findElement(page, description, fallbackSelector) {
        // Take screenshot
        const screenshotPath = await this.takeScreenshot(page);
        // Get DOM context
        const domContext = await this.getDOMContext(page);
        // Ask vision AI to locate the element
        const prompt = `You are analyzing a web page screenshot to locate a specific UI element.

**Target Element**: ${description}

**Available DOM Context** (use this to correlate with visual):
${domContext}

**Task**: Find the element described as "${description}" in the screenshot.

Return a JSON response with this structure:
\`\`\`json
{
  "found": true/false,
  "element": {
    "description": "Clear description of what you see",
    "type": "button|link|input|form|card|navigation|text|image|unknown",
    "visualCoordinates": {
      "x": number (center X coordinate),
      "y": number (center Y coordinate),
      "width": number,
      "height": number
    },
    "domSelector": "CSS selector if you can determine one from context",
    "confidence": number (0-100),
    "interactable": true/false,
    "textContent": "Any text visible in the element"
  }
}
\`\`\`

If you cannot find the element, set "found": false and provide your reasoning.`;
        try {
            const result = await this.askVisionAI(screenshotPath, prompt);
            if (!result.found) {
                // Try fallback selector if provided
                if (fallbackSelector) {
                    return await this.findByDOMSelector(page, fallbackSelector);
                }
                return null;
            }
            // Validate confidence
            if (result.element.confidence < this.confidenceThreshold) {
                console.warn(`Vision confidence (${result.element.confidence}%) below threshold (${this.confidenceThreshold}%). Trying fallback.`);
                if (fallbackSelector) {
                    return await this.findByDOMSelector(page, fallbackSelector);
                }
            }
            return {
                id: (0, uuid_1.v4)(),
                ...result.element,
            };
        }
        catch (error) {
            console.error("Vision element finding failed:", error);
            // Try fallback
            if (fallbackSelector) {
                return await this.findByDOMSelector(page, fallbackSelector);
            }
            return null;
        }
    }
    /**
     * Find all interactive elements on the page
     */
    async findAllInteractiveElements(page) {
        const screenshotPath = await this.takeScreenshot(page);
        const domContext = await this.getDOMContext(page);
        const prompt = `You are analyzing a web page to find ALL interactive elements (buttons, links, inputs, etc.).

**DOM Context**:
${domContext}

Return a JSON array of all interactive elements you can identify:
\`\`\`json
{
  "elements": [
    {
      "description": "Clear description",
      "type": "button|link|input|form|etc",
      "visualCoordinates": { "x": number, "y": number, "width": number, "height": number },
      "domSelector": "CSS selector if determinable",
      "confidence": number (0-100),
      "interactable": true,
      "textContent": "Text visible in element"
    }
  ]
}
\`\`\`

Be thorough but focus on elements users can interact with.`;
        try {
            const result = await this.askVisionAI(screenshotPath, prompt);
            return result.elements.map((el) => ({
                id: (0, uuid_1.v4)(),
                ...el,
            }));
        }
        catch (error) {
            console.error("Failed to find interactive elements:", error);
            return [];
        }
    }
    /**
     * Decide next action based on current page state
     */
    async decideNextAction(page, mission, previousSteps, primeObjective, testData) {
        const screenshotPath = await this.takeScreenshot(page);
        const domContext = await this.getDOMContext(page);
        const currentUrl = page.url();
        const manifest = await this.getFormManifestForUrl(currentUrl);
        const prompt = `You are an AI agent testing a web application using a MANIFEST-DRIVEN system.
    
**PRIME OBJECTIVE (Hard Gravity)**: "${primeObjective}"

**Your Mission**: ${mission}

**Structured Test Data (JSON)**:
${testData ? JSON.stringify(testData, null, 2) : "None provided"}

**FORM MANIFEST (Ground Truth)**:
${manifest ? JSON.stringify(manifest, null, 2) : "No manifest found for this URL. Use visual discovery."}

**Current URL**: ${currentUrl}

**Previous Steps Taken**:
${previousSteps.map((step, i) => `${i + 1}. ${step}`).join("\n")}

**DOM Context**:
${domContext}

**Task**: Analyze the screenshot and decide the NEXT ACTION to accomplish the mission while staying aligned with the Prime Objective.

Return JSON:
\`\`\`json
{
  "action": "click|fill|scroll|wait|navigate|upload|complete|multi_action",
  "confidence": number (0-100),
  "reasoning": "Why you chose this action",
  "visualContext": "What you see on screen",
  "targetElement": {
    "description": "What element to interact with",
    "visualCoordinates": { "x": number, "y": number },
    "domSelector": "CSS selector if possible",
    "confidence": number
  },
  "value": "value for fill/upload actions",
  "subActions": [
    {
      "action": "click|fill|wait",
      "targetElement": { "description": "...", "visualCoordinates": { "x": number, "y": number }, "domSelector": "...", "confidence": number },
      "value": "..."
    }
  ],
  "intent": "High-level intent (e.g., 'submitting form')",
  "alignsWithPrimeObjective": true/false,
  "gravityScore": number (0-100, alignment score)
}
\`\`\`

If mission is complete, set "action": "complete".
CRITICAL: DO NOT choose "complete" just because you're unsure what to do. Only return "complete" when you have strong evidence that the mission's end state is achieved (e.g., the expected final page/state is visible, and your reasoning cites on-screen proof). If the mission is not clearly finished, choose the next smallest concrete step (click/fill/navigate/wait) that advances the mission.

CRITICAL: Always check for validation errors. If you see high-contrast red messages, 'data-testid="error-message"' elements, or the URL remains the same after a 'Submit', these suggest a validation block. You must read and address these errors in your reasoning.

LOADING STATE RULE: If you see a loading spinner (rotating circle), "Submitting..." text, or an 'aria-busy="true"' attribute on a button, DO NOT click it again. The request is in flight. You MUST return "action": "wait" to allow the process to complete.

SURGICAL REPAIR RULE: Use the FORM MANIFEST to assess which fields are missing or incorrect. DO NOT re-fill fields that appear correct unless you are clearing a known error. Prioritize picking up from the LAST EMPTY field (e.g. if 03 is filled but 04 is empty, start your batch at 04).

BATCHING: If you see multiple related fields (like a form) that can be filled sequentially without waiting for page navigation, set "action": "multi_action" and provide the sequence in "subActions". 

CRITICAL: Always check alignment with Prime Objective. If your action deviates, set alignsWithPrimeObjective to false.
`;
        const validateDecision = (d) => {
            if (!d || typeof d !== "object" || !d.action) {
                return { ok: false, error: "Malformed AI response: missing action" };
            }
            const normalized = { ...d };
            // Normalize common alias: some models put URLs in `value`
            if ((normalized.action === "navigate" || normalized.action === "openTab") && !normalized.url && normalized.value) {
                normalized.url = normalized.value;
            }
            const action = normalized.action;
            if (action === "click") {
                if (!normalized.targetElement)
                    return { ok: false, error: "Invalid decision: click requires targetElement" };
            }
            else if (action === "fill") {
                if (!normalized.targetElement || !normalized.value)
                    return { ok: false, error: "Invalid decision: fill requires targetElement and value" };
            }
            else if (action === "upload") {
                if (!normalized.targetElement || !normalized.value)
                    return { ok: false, error: "Invalid decision: upload requires targetElement and value" };
            }
            else if (action === "navigate") {
                if (!normalized.url)
                    return { ok: false, error: "Invalid decision: navigate requires url" };
            }
            else if (action === "openTab") {
                if (!normalized.url)
                    return { ok: false, error: "Invalid decision: openTab requires url" };
            }
            else if (action === "switchTab") {
                if (typeof normalized.tabIndex !== "number")
                    return { ok: false, error: "Invalid decision: switchTab requires tabIndex (number)" };
            }
            else if (action === "multi_action") {
                if (!Array.isArray(normalized.subActions) || normalized.subActions.length === 0)
                    return { ok: false, error: "Invalid decision: multi_action requires subActions[]" };
            }
            return { ok: true, normalized };
        };
        const safeFallback = (reasoning) => ({
            action: "wait",
            confidence: 20,
            reasoning,
            visualContext: "Falling back to wait after invalid decision",
            intent: "Stabilize and retry",
            alignsWithPrimeObjective: true,
            gravityScore: 50,
        });
        try {
            let attempt = 0;
            let lastDecision = null;
            let lastError = "";
            while (attempt < 3) {
                let decision;
                try {
                    decision = await this.askVisionAI(screenshotPath, prompt +
                        (attempt === 0
                            ? ""
                            : `\n\nIMPORTANT: Your last JSON was invalid: ${lastError}\nFix it and return corrected JSON ONLY.\nLast JSON:\n${JSON.stringify(lastDecision)}`));
                }
                catch (visionError) {
                    const msg = visionError?.message || String(visionError);
                    console.warn(`⚠️ Vision decision failed, falling back to DOM-only: ${msg.substring(0, 120)}`);
                    return await this.decideNextActionDomOnly(page, mission, previousSteps, primeObjective, testData);
                }
                lastDecision = decision;
                const v = validateDecision(decision);
                if (v.ok)
                    return v.normalized;
                lastError = v.error || "Unknown validation error";
                console.warn(`⚠️  Vision AI returned invalid decision (${lastError}). Re-prompting...`);
                attempt += 1;
            }
            return safeFallback(`Invalid AI decision after retries: ${lastError}`);
        }
        catch (error) {
            throw new Error(`Failed to decide next action: ${error instanceof Error ? error.message : "Unknown error"}`);
        }
    }
    /**
     * Click an element using visual coordinates or selector
     */
    async clickElement(page, element) {
        try {
            // 1. Prefer DOM Selector if it looks valid (Playwright has better smart-waiting/auto-scrolling for selectors)
            if (element.domSelector && element.domSelector.length > 2) {
                try {
                    // Normalize common AI selector hallucinations
                    let normalizedSelector = element.domSelector
                        .replace(/:text-content\(['"](.*?)['"]\)/g, ':has-text("$1")')
                        .replace(/:text\(['"](.*?)['"]\)/g, ':has-text("$1")');
                    // Handle absolute URLs in href selectors by making them relative-friendly
                    if (normalizedSelector.includes("href='http")) {
                        normalizedSelector = normalizedSelector.replace(/href='https?:\/\/[^\/]+(\/[^']*)'/, "href*='$1'");
                    }
                    console.log(`   Trying selector-based click: ${normalizedSelector}`);
                    await page.click(normalizedSelector, { timeout: 5000 });
                    return true;
                }
                catch (selectorError) {
                    console.warn(`   Selector click failed, falling back to coordinates: ${element.domSelector}`);
                }
            }
            // 2. Try visual coordinates
            if (element.visualCoordinates) {
                const { x, y } = element.visualCoordinates;
                console.log(`   Trying coordinate-based click: (${x}, ${y})`);
                // Move mouse first to ensure hover state/triggering if needed
                await page.mouse.move(x, y);
                await page.mouse.click(x, y);
                return true;
            }
            throw new Error("No valid method to click element");
        }
        catch (error) {
            console.error("Click failed:", error);
            return false;
        }
    }
    /**
     * Fill an input element using visual coordinates or selector
     */
    async fillElement(page, element, value) {
        try {
            // 1. CLICK TO FOCUS
            if (element.visualCoordinates) {
                const { x, y } = element.visualCoordinates;
                await page.mouse.click(x, y);
            }
            else if (element.domSelector) {
                await page.locator(element.domSelector).click();
            }
            // FOCUS VERIFICATION
            const activeDesc = await page.evaluate(() => {
                const el = document.activeElement;
                return el?.getAttribute("data-description") || el?.ariaLabel || "Unknown Field";
            });
            console.log(`   🔎 Interaction Focus: "${activeDesc}" (Target: "${element.description}")`);
            // 2. NO-MERCY DESTRUCTIVE CLEAR LOOP
            const isMac = process.platform === "darwin";
            const modifier = isMac ? "Meta" : "Control";
            // Step A: Home + Shift+End + Backspace (Universal Select)
            await page.keyboard.press("Home");
            await page.keyboard.down("Shift");
            await page.keyboard.press("End");
            await page.keyboard.up("Shift");
            await page.keyboard.press("Backspace");
            // Step B: Modifier+A + Backspace
            await page.keyboard.down(modifier);
            await page.keyboard.press("a");
            await page.keyboard.up(modifier);
            await page.keyboard.press("Backspace");
            // Step C: Windows fallback if on Mac (sometimes needed in cross-env)
            if (isMac) {
                await page.keyboard.down("Control");
                await page.keyboard.press("a");
                await page.keyboard.up("Control");
                await page.keyboard.press("Backspace");
            }
            // Step D: Triple-Click + Backspace (Mouse fallback)
            if (element.visualCoordinates) {
                const { x, y } = element.visualCoordinates;
                await page.mouse.click(x, y, { clickCount: 3 });
                await page.keyboard.press("Backspace");
            }
            // 3. FILLING
            if (element.domSelector) {
                // Clear again via Playwright just to be sure
                await page.locator(element.domSelector).clear();
                await page.locator(element.domSelector).fill(value);
            }
            else {
                await page.keyboard.type(value, { delay: 30 });
            }
            // 4. VERIFICATION
            const actualValue = await this.verifyElementValue(page, element);
            if (actualValue !== value) {
                console.log(`   ⚠️ Verification mismatch! Intended: "${value}", Actual: "${actualValue}". Retrying aggressively...`);
                // One final "No-Mercy" attempt if mismatched
                if (element.domSelector) {
                    await page.locator(element.domSelector).fill("");
                    await page.locator(element.domSelector).fill(value);
                }
            }
            return true;
        }
        catch (error) {
            console.error("Fill failed:", error);
            return false;
        }
    }
    /**
     * Reads the actual DOM value of an element to verify a fill action
     */
    async verifyElementValue(page, element) {
        try {
            if (element.domSelector) {
                return await page.locator(element.domSelector).inputValue();
            }
            // If we only have coordinates, we try to find the active element's value
            return await page.evaluate(() => {
                const el = document.activeElement;
                return el ? el.value : "";
            });
        }
        catch (e) {
            return "";
        }
    }
    // Private helper methods
    /**
     * Take screenshot of current page
     */
    async takeScreenshot(page) {
        const filename = `vision-${Date.now()}-${(0, uuid_1.v4)()}.png`;
        const filepath = path.join(this.screenshotsDir, filename);
        await page.screenshot({ path: filepath, fullPage: true });
        return filepath;
    }
    /**
     * Get DOM context for hybrid approach
     */
    async getDOMContext(page) {
        try {
            return await page.evaluate(() => {
                const title = document.title;
                const url = window.location.href;
                // Get interactive elements
                const buttons = Array.from(document.querySelectorAll("button"))
                    .slice(0, 20)
                    .map((b, i) => `Button ${i + 1}: "${b.textContent?.trim()}" (${b.className})`);
                const links = Array.from(document.querySelectorAll("a"))
                    .slice(0, 20)
                    .map((a, i) => `Link ${i + 1}: "${a.textContent?.trim()}" -> ${a.href} (${a.className})`);
                const inputs = Array.from(document.querySelectorAll("input, textarea"))
                    .slice(0, 15)
                    .map((i, idx) => `Input ${idx + 1}: type="${i.getAttribute("type")}" placeholder="${i.getAttribute("placeholder")}" name="${i.getAttribute("name")}"`);
                const forms = Array.from(document.querySelectorAll("form"))
                    .slice(0, 5)
                    .map((f, i) => `Form ${i + 1}: action="${f.action}" (${f.className})`);
                // Detect Dialogs/Drawers (Radix/Aria)
                const dialogs = Array.from(document.querySelectorAll('[role="dialog"], [role="alertdialog"], [data-state="open"]'))
                    .map((d, i) => {
                    const label = d.getAttribute('aria-label') || d.getAttribute('aria-labelledby') || "Unnamed Dialog";
                    return `Dialog ${i + 1}: "${label}" (Status: ${d.getAttribute('data-state') || 'open'})`;
                });
                return `
Page Title: ${title}
URL: ${url}

MODALS/DIALOGS ACTIVE:
${dialogs.length > 0 ? dialogs.join("\n") : "None visible"}

Interactive Elements:

BUTTONS:
${buttons.join("\n")}

LINKS:
${links.join("\n")}

INPUTS:
${inputs.join("\n")}

FORMS:
${forms.join("\n")}
        `.trim();
            });
        }
        catch (error) {
            return "DOM context unavailable";
        }
    }
    /**
     * Identifies if the current URL has a registered form manifest for synchronization
     */
    async getFormManifestForUrl(url) {
        try {
            const manifestsDir = path.join(process.cwd(), ".mycontext", "forms");
            if (!fs.existsSync(manifestsDir))
                return null;
            const files = await fs.readdir(manifestsDir);
            for (const file of files) {
                if (!file.endsWith(".json"))
                    continue;
                const manifest = await fs.readJson(path.join(manifestsDir, file));
                // Simple URL matching or keyword matching
                if (manifest.registration && url.includes("register")) {
                    return manifest.registration;
                }
            }
            return null;
        }
        catch (e) {
            return null;
        }
    }
    /**
     * Build comprehensive analysis prompt
     */
    buildAnalysisPrompt(domContext) {
        return `You are analyzing a web page screenshot to understand the UI state.

**DOM Context**:
${domContext}

Return a comprehensive JSON analysis:
\`\`\`json
{
  "elements": [/* array of VisualElement objects */],
  "interactiveElements": [/* filteredarray of interactive elements */],
  "uiState": {
    "pageTitle": "string",
    "url": "string",
    "loadingState": "loading|loaded|error",
    "modalOpen": true/false,
    "mainContent": "description of main content"
  },
  "designSystem": {
    "colors": ["#hex1", "#hex2"],
    "typography": ["Font family, size"],
    "spacing": "spacing pattern"
  },
  "layoutStructure": {
    "type": "grid|flex|fixed|responsive",
    "sections": ["header", "sidebar", "main", "footer"]
  },
  "aiInterpretation": "Natural language summary of what's on screen"
}
\`\`\``;
    }
    /**
     * Analyze screenshot with custom prompt using AICore fallback
     */
    async analyzeScreenshotWithPrompt(screenshotPath, prompt) {
        return this.askVisionAI(screenshotPath, prompt);
    }
    /**
     * Ask vision AI with a custom prompt via AICore (with fallback)
     */
    async askVisionAI(screenshotPath, prompt) {
        try {
            const text = await this.aiCore.generateVisionText(prompt, screenshotPath);
            // Extract JSON from response
            let jsonText = text.trim();
            const sanitizeJsonText = (raw) => {
                let s = raw.trim();
                // Remove JS-style comments that models sometimes include inside JSON
                s = s.replace(/\/\*[\s\S]*?\*\//g, "");
                s = s.replace(/(^|\s)\/\/.*$/gm, "$1");
                // Remove trailing commas before } or ]
                s = s.replace(/,\s*([}\]])/g, "$1");
                return s.trim();
            };
            // 1. Try to find content within any code blocks (json or plain)
            const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
            if (codeBlockMatch && codeBlockMatch[1]) {
                jsonText = codeBlockMatch[1].trim();
            }
            else {
                // 2. If no code block, find the first '{' and last '}'
                const firstBrace = text.indexOf('{');
                const lastBrace = text.lastIndexOf('}');
                if (firstBrace !== -1 && lastBrace !== -1) {
                    jsonText = text.substring(firstBrace, lastBrace + 1);
                }
            }
            // 3. Final safety: remove ANY markdown formatting that might still be there
            jsonText = jsonText.replace(/^```json/i, "").replace(/```$/i, "").trim();
            jsonText = sanitizeJsonText(jsonText);
            try {
                return JSON.parse(jsonText);
            }
            catch (parseError) {
                // PROGRESSIVE REPAIR STRATEGY
                console.warn(`⚠️ JSON parse failed, attempting repair: ${parseError.message}`);
                let repaired = sanitizeJsonText(jsonText);
                // 1. Remove trailing junk after the last '}'
                const lastIndex = repaired.lastIndexOf('}');
                if (lastIndex !== -1) {
                    repaired = repaired.substring(0, lastIndex + 1);
                }
                // 2. Fix unclosed blocks (common if AI is cut off)
                const openBraces = (repaired.match(/\{/g) || []).length;
                const closeBraces = (repaired.match(/\}/g) || []).length;
                if (openBraces > closeBraces) {
                    repaired += '}'.repeat(openBraces - closeBraces);
                }
                const openBrackets = (repaired.match(/\[/g) || []).length;
                const closeBrackets = (repaired.match(/\]/g) || []).length;
                if (openBrackets > closeBrackets) {
                    repaired += ']'.repeat(openBrackets - closeBrackets);
                }
                try {
                    return JSON.parse(sanitizeJsonText(repaired));
                }
                catch (secondError) {
                    // 3. Last resort: try to extract ANY valid JSON object from the middle
                    const objects = repaired.match(/\{[\s\S]*?\}/g);
                    if (objects && objects.length > 0) {
                        for (const obj of objects.reverse()) { // Try from end first
                            try {
                                return JSON.parse(sanitizeJsonText(obj));
                            }
                            catch { }
                        }
                    }
                    throw parseError; // Rethrow original error if repair fails
                }
            }
        }
        catch (error) {
            console.error("Vision AI query failed:", error);
            throw error;
        }
    }
    /**
     * Fallback: Find element by DOM selector
     */
    async findByDOMSelector(page, selector) {
        try {
            const element = await page.$(selector);
            if (!element) {
                return null;
            }
            // Get bounding box for coordinates
            const box = await element.boundingBox();
            if (!box) {
                return null;
            }
            // Get text content
            const textContent = await element.textContent();
            return {
                id: (0, uuid_1.v4)(),
                description: `Element found by selector: ${selector}`,
                type: "unknown",
                visualCoordinates: {
                    x: box.x + box.width / 2,
                    y: box.y + box.height / 2,
                    width: box.width,
                    height: box.height,
                },
                domSelector: selector,
                confidence: 60, // Lower confidence for DOM fallback
                interactable: true,
                textContent: textContent || undefined,
            };
        }
        catch (error) {
            return null;
        }
    }
}
exports.VisionElementFinder = VisionElementFinder;
//# sourceMappingURL=vision-element-finder.js.map