import { OpenAI } from 'openai';
import { GoogleGenerativeAI } from '@google/generative-ai';
export class AIClient {
    static instance;
    constructor() { }
    static getInstance() {
        if (!AIClient.instance) {
            AIClient.instance = new AIClient();
        }
        return AIClient.instance;
    }
    /**
     * Attempts to find a new selector for a failed element using LLM.
     */
    async suggestHealedSelector(config, prompt) {
        const systemPrompt = `You are a Playwright Automation Expert specializing in self-healing mechanisms.
A test action has failed because the intended element could not be located with the original selector.

### CONTEXT:
- **Action**: ${prompt.action}
- **Intended Target**: ${prompt.target}
- **Original Error**: ${prompt.error}

### TASK:
Analyze the provided DOM snippet and suggest a **highly robust** CSS or XPath selector for the intended element.

### SELECTOR HIERARCHY (Prefer in order):
1. Stable attributes like 'data-testid', 'data-cy', or unique 'id'.
2. Semantic text content (e.g., text="Submit Order").
3. Stable ARIA roles and labels.
4. Relative paths from a stable parent.

### DOM SNIPPET:
${prompt.domSnippet}

### OUTPUT INSTRUCTIONS:
- Return ONLY the raw selector string.
- No markdown formatting, no quotes, no explanation.
- If no match is found, return "NULL".`;
        try {
            if (config.provider === 'openai') {
                const openai = new OpenAI({ apiKey: config.apiKey });
                const response = await openai.chat.completions.create({
                    model: config.modelName || 'gpt-4o',
                    messages: [{ role: 'user', content: systemPrompt }],
                    temperature: 0,
                });
                const suggestion = response.choices[0].message.content?.trim();
                return suggestion === 'NULL' ? null : suggestion || null;
            }
            if (config.provider === 'gemini') {
                const genAI = new GoogleGenerativeAI(config.apiKey);
                const model = genAI.getGenerativeModel({ model: config.modelName || 'gemini-2.5-flash' });
                const result = await model.generateContent(systemPrompt);
                const response = await result.response;
                const suggestion = response.text().trim();
                return suggestion === 'NULL' ? null : suggestion || null;
            }
            // Add Claude later...
            return null;
        }
        catch (err) {
            console.error('[AI-CLIENT] Error suggesting healed selector:', err);
            return null;
        }
    }
    /**
     * Generates a full scenario JSON from a natural language prompt and optional screenshots.
     */
    async generateScenario(config, userPrompt, images, // Base64 strings
    availableFlows) {
        const systemPrompt = `You are a Senior Test Automation Architect. Convert the following natural language request into a valid, modular Scrapit DSL JSON scenario. 
${images && images.length > 0 ? "You have been provided with screenshots of the target application. Use them to identify selectors and flow logic." : ""}
${availableFlows && availableFlows.length > 0 ? `The following reusable FLOWS are available in the library: [${availableFlows.join(', ')}]. Use them by name in the "flows" array if they match the requested logic.` : ""}

### 1. ROOT CONFIGURATION (ScenarioConfig)
- "name": string (Descriptive name)
- "mode": "scrape" | "validate"
   - Use "scrape" if the primary goal is data extraction.
   - Use "validate" if the primary goal is testing/verifying functionality.
- "baseUrl": string (The entry point URL)
- "flows": Array of string (Names of existing reusable flows to include IN ORDER)
- "steps": Array of DSLStep objects (Sequence of actions to perform AFTER the flows)

### 2. COMPLETE ACTION REFERENCE (DSLStep)
Every step must have an "action" and a descriptive "label".

#### A. Navigation & Control
- { "action": "navigate", "target": "URL", "label": "go_to_url" }
- { "action": "new_tab", "target": "URL", "label": "open_tab" }
- { "action": "wait", "target": "selector", "value": "visible" | "hidden", "timeout": 30000, "label": "wait_for_el" }
- { "action": "sleep", "value": "ms_string", "label": "pause" }
- { "action": "network_idle", "label": "wait_for_network" }
- { "action": "humanize", "label": "stealth_mode" } (CRITICAL: Use this before interactions on high-security sites to apply bot-bypass patterns)

#### B. Interaction
- { "action": "click", "target": "selector", "optional": true, "label": "click_button" }
- { "action": "double_click", "target": "selector", "label": "db_click" }
- { "action": "right_click", "target": "selector", "label": "context_menu" }
- { "action": "type", "target": "selector", "value": "text", "label": "input_field" } (Used to fill input fields and textareas)
- { "action": "select", "target": "selector", "value": "option_val", "label": "dropdown" }
- { "action": "hover", "target": "selector", "label": "move_to" }
- { "action": "drag_and_drop", "target": "source_selector", "value": "target_selector", "label": "move_item" }
- { "action": "upload", "target": "selector", "value": "file_path", "label": "file_upload" }
- { "action": "dispatch_event", "target": "selector", "attribute": "eventName", "value": "json_payload", "label": "trigger_event" }

#### C. Data Extraction (Scraping)
- { "action": "scrape", "target": "selector", "mapTo": "key", "label": "get_text" }
- { "action": "scrapeAttr", "target": "selector", "attribute": "attr_name", "mapTo": "key", "label": "get_attr" }
- { "action": "scrapeProp", "target": "selector", "value": "prop_name", "mapTo": "key", "label": "get_prop" }
- { "action": "scrapePairs", "target": "container_selector", "label": "extract_kv_pairs" }
- { "action": "scrapeItems", "target": "container", "value": "{\"key\": \"inner_selector\"}", "label": "list_extraction" }
- { "action": "scrapeDeep", "target": "link_selector", "paginateTarget": "next_btn", "steps": [...], "label": "detail_scrape_loop" }

#### D. Validation & Advanced
- { "action": "exists", "target": "selector", "label": "check_presence" }
- { "action": "validate", "target": "selector", "value": "expected_text", "label": "verify_text" }
- { "action": "regex", "target": "selector", "regex": "pattern", "label": "pattern_match" }
- { "action": "assert", "code": "js_boolean_expression", "label": "logic_check" }
- { "action": "eval", "code": "js_code", "label": "custom_script" }

#### E. Captcha Solvers
- { "action": "solve_captcha", "label": "auto_solve" } (Use for simple reCAPTCHA/HCaptcha widgets)
- { "action": "solve_captcha_api", "target": "img_selector", "label": "ocr_solve" } (Use for custom image-based OCR challenges)
- { "action": "solve_captcha_service", "label": "external_solve" } (BEST: Use for high-security reCAPTCHA v3/Enterprise. Requires 2Captcha/Anti-Captcha)

### 3. CONSOLIDATED MASTER EXAMPLE
{
  "name": "high_security_login",
  "mode": "validate",
  "baseUrl": "https://secure-site.com/login",
  "flows": ["login_v2"],
  "steps": [
    { "action": "navigate", "target": "https://secure-site.com/dashboard", "label": "post_login_nav" },
    { "action": "humanize", "label": "stealth_init" },
    { "action": "solve_captcha_service", "label": "bypass_bot_detection" },
    { "action": "click", "target": "button.profile", "label": "view_profile" }
  ]
}

Request: ${userPrompt}

Return ONLY the valid JSON object.`;
        try {
            if (config.provider === 'openai') {
                const openai = new OpenAI({ apiKey: config.apiKey });
                const content = [{ type: 'text', text: systemPrompt }];
                if (images && images.length > 0) {
                    images.forEach(img => {
                        content.push({
                            type: 'image_url',
                            image_url: { url: img.startsWith('data:') ? img : `data:image/png;base64,${img}` }
                        });
                    });
                }
                const response = await openai.chat.completions.create({
                    model: config.modelName || 'gpt-4o',
                    messages: [{ role: 'user', content }],
                    response_format: { type: "json_object" },
                    temperature: 0.2,
                });
                return JSON.parse(response.choices[0].message.content || '{}');
            }
            if (config.provider === 'gemini') {
                const genAI = new GoogleGenerativeAI(config.apiKey);
                const model = genAI.getGenerativeModel({ model: config.modelName || 'gemini-2.5-flash' });
                const parts = [systemPrompt];
                if (images && images.length > 0) {
                    images.forEach(img => {
                        const base64Data = img.includes('base64,') ? img.split('base64,')[1] : img;
                        parts.push({
                            inlineData: {
                                data: base64Data,
                                mimeType: 'image/png'
                            }
                        });
                    });
                }
                const result = await model.generateContent(parts);
                const response = await result.response;
                if (!response)
                    throw new Error('Empty response from Gemini');
                const text = response.text();
                if (!text)
                    return null;
                // Extract JSON if model wraps it in markdown
                const jsonMatch = text.match(/\{[\s\S]*\}/);
                try {
                    return JSON.parse(jsonMatch ? jsonMatch[0] : text);
                }
                catch (parseErr) {
                    console.error('[AI-CLIENT] Failed to parse Gemini JSON:', text);
                    return null;
                }
            }
        }
        catch (err) {
            console.error('[AI-CLIENT] Error generating scenario:', err);
            return null;
        }
    }
    /**
     * Analyzes a failed test run and provides actionable insights.
     */
    async analyzeFailure(config, context) {
        const systemPrompt = `You are a Senior Debugging Expert. A test run has failed in the Scrapit Automation platform. 
Analyze the following context to provide a surgical debugging report:

### FAILURE CONTEXT:
- **Scenario**: ${context.scenarioName}
- **Primary Error**: ${context.error}

### RECENT EXECUTION LOGS:
${context.logs.slice(-50).join('\n')}

### YOUR TASK:
1. **Root Cause**: Identify exactly why the test failed (e.g., timeout, element changed, network issue, logic error).
2. **Actionable Fix**: Provide a specific solution. If it's a selector issue, suggest a likely alternative. If it's a timing issue, suggest the correct wait action.
3. **DSL Correction**: If possible, provide the corrected DSL JSON snippet for the failing step.
4. **Severity**: Categorize as CRITICAL (blocker), MAJOR (flaky/unstable), or MINOR (cosmetic/ignorable).

Format your response in a professional, easy-to-read Markdown report.`;
        try {
            if (config.provider === 'openai') {
                const openai = new OpenAI({ apiKey: config.apiKey });
                const response = await openai.chat.completions.create({
                    model: config.modelName || 'gpt-4o',
                    messages: [{ role: 'user', content: systemPrompt }],
                    temperature: 0.1,
                });
                return response.choices[0].message.content || 'Failed to analyze.';
            }
            if (config.provider === 'gemini') {
                const genAI = new GoogleGenerativeAI(config.apiKey);
                const model = genAI.getGenerativeModel({ model: config.modelName || 'gemini-2.5-flash' });
                const result = await model.generateContent(systemPrompt);
                const response = await result.response;
                return response.text();
            }
            return 'Provider not supported.';
        }
        catch (err) {
            console.error('[AI-CLIENT] Error analyzing failure:', err);
            return 'Error during AI analysis.';
        }
    }
}
