const Tesseract = require('tesseract.js');
const https = require('https');
const http = require('http');

const { Solver } = require('@2captcha/captcha-solver');
const solver = new Solver('9ed0ef51badf9a017ac50aea413d8001');

const CAPTCHA_PATTERNS = {
    recaptcha: {
        selectors: ['.g-recaptcha', '[data-sitekey]', 'iframe[src*="recaptcha"]', '#recaptcha'],
        name: 'reCAPTCHA'
    },
    hcaptcha: {
        selectors: ['.h-captcha', '[data-hcaptcha-sitekey]', 'iframe[src*="hcaptcha"]', '#hcaptcha', '[data-sitekey*="hcaptcha"]', '.hcaptcha-checkbox'],
        name: 'hCaptcha'
    },
    textCaptcha: {
        selectors: ['input[name*="captcha"]', 'input[id*="captcha"]', '.captcha-input', '#captcha'],
        name: 'Text CAPTCHA'
    },
    imageCaptcha: {
        selectors: ['img[src*="captcha"]', '.captcha-image', '#captcha-image', 'img[alt*="captcha" i]'],
        name: 'Image CAPTCHA'
    },
    mathCaptcha: {
        selectors: ['.math-captcha', '[name*="math"]', 'input[placeholder*="math" i]'],
        name: 'Math CAPTCHA'
    }
};

function fetchImageAsBuffer(url) {
    return new Promise((resolve, reject) => {
        const client = url.startsWith('https') ? https : http;
        client.get(url, (res) => {
            const chunks = [];
            res.on('data', (chunk) => chunks.push(chunk));
            res.on('end', () => resolve(Buffer.concat(chunks)));
            res.on('error', reject);
        }).on('error', reject);
    });
}

async function detectCaptcha(page) {
    const captchaInfo = await page.evaluate((patterns) => {
        const results = [];
        
        for (const [type, config] of Object.entries(patterns)) {
            for (const selector of config.selectors) {
                const elements = document.querySelectorAll(selector);
                if (elements.length > 0) {
                    for (const el of elements) {
                        const style = window.getComputedStyle(el);
                        const rect = el.getBoundingClientRect();
                        const isVisible = style.display !== 'none' && 
                                         style.visibility !== 'hidden' && 
                                         style.opacity !== '0' &&
                                         rect.width > 0 && 
                                         rect.height > 0;
                        
                        results.push({
                            type: type,
                            name: config.name,
                            selector: selector,
                            count: elements.length,
                            isVisible: isVisible
                        });
                    }
                    break;
                }
            }
        }
        
        return results;
    }, CAPTCHA_PATTERNS);
    
    return captchaInfo;
}

async function solveSimpleCaptcha(page) {
    const mathSolution = await page.evaluate(() => {
        const mathText = document.querySelector('.math-captcha, .captcha-question, [class*="math"]');
        if (mathText) {
            const text = mathText.textContent || '';
            const match = text.match(/(\d+)\s*([+\-*/])\s*(\d+)/);
            if (match) {
                const [_, a, op, b] = match;
                let result;
                switch(op) {
                    case '+': result = parseInt(a) + parseInt(b); break;
                    case '-': result = parseInt(a) - parseInt(b); break;
                    case '*': result = parseInt(a) * parseInt(b); break;
                    case '/': result = Math.floor(parseInt(a) / parseInt(b)); break;
                }
                return { type: 'math', answer: result, text: text };
            }
        }
        return null;
    });
    
    if (mathSolution) {
        return mathSolution;
    }
    
    const imageCaptchaInfo = await page.evaluate(() => {
        const selectors = [
            'img[src*="captcha"]',
            '.captcha-image img',
            '#captcha-image',
            'img[alt*="captcha" i]',
            'img[class*="captcha" i]'
        ];
        
        for (const selector of selectors) {
            const img = document.querySelector(selector);
            if (img && img.src) {
                return { src: img.src, selector: selector };
            }
        }
        return null;
    });
    
    if (imageCaptchaInfo) {
        console.log('   Attempting 2Captcha image solving...');
        console.log(`   CAPTCHA image selector: ${imageCaptchaInfo.selector}`);
        
        // Wait for CAPTCHA image to fully load before capturing
        console.log('   Waiting for CAPTCHA image to load...');
        let imageLoaded = false;
        const maxWaitMs = 5000;
        const checkIntervalMs = 200;
        let waitedMs = 0;
        
        while (!imageLoaded && waitedMs < maxWaitMs) {
            imageLoaded = await page.evaluate((selector) => {
                const el = document.querySelector(selector);
                return el && el.naturalWidth > 0 && el.naturalHeight > 0;
            }, imageCaptchaInfo.selector).catch(() => false);
            
            if (!imageLoaded) {
                await new Promise(r => setTimeout(r, checkIntervalMs));
                waitedMs += checkIntervalMs;
            }
        }
        
        if (imageLoaded) {
            console.log(`   CAPTCHA image loaded successfully (waited ${waitedMs}ms)`);
        } else {
            console.log('   WARNING: CAPTCHA may not have fully loaded, proceeding anyway...');
        }
        
        // Additional small delay to ensure rendering is complete
        await new Promise(r => setTimeout(r, 300));
        
        try {
            let imageData;
            
            // Get CAPTCHA element bounding box
            const captchaInfo = await page.evaluate((selector) => {
                const img = document.querySelector(selector);
                if (!img) return null;
                const rect = img.getBoundingClientRect();
                return {
                    src: img.src,
                    x: rect.x,
                    y: rect.y,
                    width: rect.width,
                    height: rect.height
                };
            }, imageCaptchaInfo.selector);
            
            if (captchaInfo && captchaInfo.width > 0) {
                console.log('   CAPTCHA at:', captchaInfo.x, captchaInfo.y, captchaInfo.width, 'x', captchaInfo.height);
                
                // Try to get element screenshot directly
                try {
                    const captchaElement = await page.$(imageCaptchaInfo.selector);
                    if (captchaElement) {
                        const elementScreenshot = await captchaElement.screenshot({ type: 'png' });
                        if (Buffer.isBuffer(elementScreenshot)) {
                            imageData = elementScreenshot.toString('base64');
                        } else {
                            imageData = Buffer.from(elementScreenshot).toString('base64');
                        }
                        console.log('   Element screenshot captured, size:', imageData.length, 'bytes (base64)');
                    }
                } catch (elemError) {
                    console.log('   Element screenshot failed:', elemError.message);
                    
                    // Fallback: capture full page and crop
                    const fullScreenshot = await page.screenshot({ type: 'png', encoding: 'binary' });
                    const screenshotBuffer = Buffer.from(fullScreenshot, 'binary');
                    const png = require('pngjs').PNG.sync.read(screenshotBuffer);
                    
                    const cropX = Math.max(0, Math.floor(captchaInfo.x));
                    const cropY = Math.max(0, Math.floor(captchaInfo.y));
                    const cropW = Math.floor(captchaInfo.width);
                    const cropH = Math.floor(captchaInfo.height);
                    
                    console.log('   Cropping to:', cropX, cropY, cropW, cropH);
                    
                    const cropped = new require('pngjs').PNG({ width: cropW, height: cropH });
                    
                    for (let y = 0; y < cropH; y++) {
                        for (let x = 0; x < cropW; x++) {
                            const srcIdx = ((cropY + y) * png.width + (cropX + x)) * 4;
                            const dstIdx = (y * cropW + x) * 4;
                            cropped.data[dstIdx] = png.data[srcIdx];
                            cropped.data[dstIdx + 1] = png.data[srcIdx + 1];
                            cropped.data[dstIdx + 2] = png.data[srcIdx + 2];
                            cropped.data[dstIdx + 3] = png.data[srcIdx + 3];
                        }
                    }
                    
                    imageData = require('pngjs').PNG.sync.write(cropped).toString('base64');
                    console.log('   Cropped image size:', imageData.length, 'bytes (base64)');
                }
            } else if (captchaInfo && imageCaptchaInfo.src.startsWith('data:')) {
                const base64 = imageCaptchaInfo.src.replace(/^data:image\/\w+;base64,/, '');
                imageData = base64;
            }
            
            console.log('   Sending to 2Captcha (human workers)...');
            const result = await solver.imageCaptcha({
                body: imageData,
                numeric: 0,
                min_len: 1,
                max_len: 10,
                phrase: 0,
                caseSensitive: 1,
                calc: 0,
                lang: 'en'
            });
            
            console.log(`   2Captcha solved: "${result}"`);
            return { 
                type: 'image', 
                answer: result.trim(), 
                originalText: result,
                service: '2captcha',
                imageSrc: imageCaptchaInfo.src 
            };
            
        } catch (error) {
            console.log(`   2Captcha failed: ${error.message}`);
            
            console.log('   Falling back to Tesseract OCR...');
            try {
                const { data: { text, confidence } } = await Tesseract.recognize(
                    imageCaptchaInfo.src,
                    'eng',
                    { logger: m => {} }
                );
                
                console.log(`   OCR raw output: "${text}"`);
                console.log(`   OCR confidence: ${confidence}%`);
                
                const cleanedText = text.replace(/[^a-zA-Z0-9]/g, '').trim();
                
                if (cleanedText && cleanedText.length >= 3) {
                    console.log(`   OCR fallback read: "${cleanedText}"`);
                    return { 
                        type: 'image', 
                        answer: cleanedText, 
                        originalText: text,
                        confidence: confidence,
                        service: 'tesseract-fallback',
                        imageSrc: imageCaptchaInfo.src 
                    };
                }
            } catch (ocrError) {
                console.log(`   OCR fallback also failed: ${ocrError.message}`);
            }
            
            return { 
                type: 'image', 
                answer: null,
                message: `Both 2Captcha and OCR failed: ${error.message}`,
                imageSrc: imageCaptchaInfo.src 
            };
        }
    }
    
    return null;
}

async function solveHCaptcha(page, pageUrl) {
    try {
        // First, try to find and click the hCaptcha checkbox directly via JavaScript
        console.log(`   Attempting to click hCaptcha checkbox directly...`);
        
        const clickResult = await page.evaluate(() => {
            // Try multiple approaches to check the checkbox
            const selectors = [
                '#checkbox',
                '.hcaptcha-checkbox',
                '[class*="hcaptcha"]',
                '[role="checkbox"]',
                'div[aria-haspopup="true"]'
            ];
            
            for (const selector of selectors) {
                const el = document.querySelector(selector);
                if (el) {
                    // Try clicking it
                    el.click();
                    
                    // Also try setting aria-checked
                    el.setAttribute('aria-checked', 'true');
                    
                    // Check if it has a checked state now
                    const checked = el.getAttribute('aria-checked');
                    return { clicked: true, selector: selector, ariaChecked: checked };
                }
            }
            return { clicked: false };
        });
        
        console.log(`   Click result:`, clickResult);
        
        // Wait a moment and check if hCaptcha is now checked
        await new Promise(r => setTimeout(r, 1500));
        
        const hCaptchaChecked = await page.evaluate(() => {
            const checkbox = document.querySelector('#checkbox, .hcaptcha-checkbox, [role="checkbox"]');
            if (checkbox) {
                return checkbox.getAttribute('aria-checked') === 'true';
            }
            return false;
        });
        
        if (hCaptchaChecked) {
            console.log(`   hCaptcha checkbox checked successfully!`);
            return { type: 'hcaptcha', answer: 'clicked', service: 'direct-click' };
        }
        
        // If direct click didn't work, try 2Captcha API
        console.log(`   Direct click didn't work, trying 2Captcha API...`);
        const hcaptchaInfo = await page.evaluate(() => {
            // Look for sitekey in data attributes
            const dataSelectors = [
                '[data-hcaptcha-sitekey]',
                '[data-sitekey]',
                '.h-captcha',
                '#hcaptcha',
                '[class*="hcaptcha"]'
            ];
            
            for (const selector of dataSelectors) {
                const elements = document.querySelectorAll(selector);
                for (const el of elements) {
                    const sitekey = el.getAttribute('data-hcaptcha-sitekey') || el.getAttribute('data-sitekey');
                    if (sitekey && sitekey.length > 10) {
                        return { sitekey: sitekey, selector: selector };
                    }
                    // Check parent chain
                    let parent = el.parentElement;
                    for (let i = 0; i < 5 && parent; i++) {
                        const parentKey = parent.getAttribute('data-hcaptcha-sitekey') || parent.getAttribute('data-sitekey');
                        if (parentKey && parentKey.length > 10) {
                            return { sitekey: parentKey, selector: selector };
                        }
                        parent = parent.parentElement;
                    }
                }
            }
            
            // Look for hCaptcha iframe and get sitekey from URL
            const iframe = document.querySelector('iframe[src*="hcaptcha"]');
            if (iframe) {
                const src = iframe.getAttribute('src') || '';
                const match = src.match(/sitekey=([^&]+)/);
                if (match && match[1]) {
                    return { sitekey: match[1], selector: 'iframe[src*="hcaptcha"]' };
                }
            }
            
            // Fallback: search entire HTML for hCaptcha sitekey pattern
            const html = document.documentElement.innerHTML;
            const sitekeyMatch = html.match(/["']sitekey["']\s*[:=]\s*["']([a-zA-Z0-9_-]{20,})["']/);
            if (sitekeyMatch && sitekeyMatch[1]) {
                return { sitekey: sitekeyMatch[1], selector: 'html-content' };
            }
            
            // Look for hcap sitekey
            const hcapMatch = html.match(/hcap[tT]cha.*?["']sitekey["']\s*[:=]\s*["']([a-zA-Z0-9_-]{20,})["']/);
            if (hcapMatch && hcapMatch[1]) {
                return { sitekey: hcapMatch[1], selector: 'html-script' };
            }
            
            return null;
        });
        
        if (!hcaptchaInfo) {
            console.log(`   hCaptcha not found on page`);
            return null;
        }
        
        console.log(`   Found hCaptcha sitekey: ${hcaptchaInfo.sitekey}`);
        console.log(`   Submitting to 2Captcha...`);
        
        // Use new @2captcha/captcha-solver package with object syntax
        const result = await solver.hcaptcha({
            pageurl: pageUrl,
            sitekey: hcaptchaInfo.sitekey
        });
        
        console.log(`   hCaptcha solved: ${result}`);
        
        // Try multiple methods to set the token
        await page.evaluate((token) => {
            // Method 1: Find textarea
            const textarea = document.querySelector('textarea[name="h-captcha-response"]');
            if (textarea) {
                textarea.value = token;
                textarea.dispatchEvent(new Event('input', { bubbles: true }));
                textarea.dispatchEvent(new Event('change', { bubbles: true }));
                console.log('   Set token in textarea');
            }
            
            // Method 2: Find iframe and set directly
            const iframe = document.querySelector('iframe[src*="hcaptcha"]');
            if (iframe) {
                try {
                    const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
                    const iframeTextarea = iframeDoc.querySelector('textarea[name="h-captcha-response"]');
                    if (iframeTextarea) {
                        iframeTextarea.value = token;
                        iframeTextarea.dispatchEvent(new Event('input', { bubbles: true }));
                        console.log('   Set token in iframe textarea');
                    }
                } catch(e) {
                    console.log('   Cannot access iframe directly');
                }
            }
            
            // Method 3: Set on any hCaptcha related element
            document.querySelectorAll('[id*="hcaptcha"], [class*="hcaptcha"]').forEach(el => {
                if (el.value !== undefined) el.value = token;
                if (el.dataset.response) el.dataset.response = token;
            });
        }, result.data);
        
        return {
            type: 'hcaptcha',
            answer: result.data,
            service: '2captcha'
        };
    } catch (error) {
        const errorMsg = error.message || '';
        
        if (errorMsg.includes('ERROR_METHOD_CALL') || errorMsg.includes('Unexpected Error')) {
            console.log(`   ERROR: hCaptcha not supported by your 2Captcha API key`);
            console.log(`   Your API key may not include hCaptcha solving.`);
            console.log(`   Options:`);
            console.log(`   - Contact 2Captcha to add hCaptcha to your plan`);
            console.log(`   - Use an alternative service like CapSolver or Anti-Captcha`);
        } else {
            console.log(`   hCaptcha solving failed: ${errorMsg}`);
        }
        
        return null;
    }
}

module.exports = {
    CAPTCHA_PATTERNS,
    detectCaptcha,
    solveSimpleCaptcha,
    solveHCaptcha,
    fetchImageAsBuffer
};
