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

const TwoCaptcha = require('2captcha');
const solver = new TwoCaptcha.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'],
        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(imageData, {
                numeric: 0,
                minLength: 1,
                maxLength: 10,
                phrase: 0,
                caseSensitive: 1,
                calc: 0,
                lang: 'en'
            });
            
            console.log(`   2Captcha solved: "${result.data}"`);
            return { 
                type: 'image', 
                answer: result.data.trim(), 
                originalText: result.data,
                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;
}

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