import { launch } from 'cloakbrowser';
import axios from 'axios';
import fs from 'fs/promises';
import crypto from 'crypto';

const TEMP_MAIL_API = "https://api.mail.tm";
const EPIC_GRAPHQL_URL = "https://graphql.epicgames.com/graphql";
const OUTPUT_FILE = "nitro.txt";

class EpicAutomatorJS {
    constructor() {
        this.mailConfig = null;
        this.bearerToken = null;
        this.session = axios.create();
    }

    generateRandomString(length = 10) {
        return crypto.randomBytes(length).toString('hex').substring(0, length);
    }

    async createTempMail() {
        try {
            console.log("[*] Requesting explicit custom domain from tempmail.lol...");
            // Use POST body to explicitly request the working domain per API docs!
            const payload = { "domain": "blaizesmp.net" };
            const res = await this.session.post(`https://api.tempmail.lol/v2/inbox/create`, payload);

            const address = res.data.address;
            const token = res.data.token;

            console.log(`[+] Secured pristine inbox: ${address}`);
            console.log(`[+] API Inbox Token: ${token}`);
            console.log(`[+] Manual API Link: https://api.tempmail.lol/v2/inbox?token=${token}`);

            this.mailConfig = { address, token };
            return this.mailConfig;
        } catch (error) {
            console.log(`[-] Temp Mail Error: ${error.message}`);
        }
        return null;
    }

    async extractSessionTokens(context) {
        const cookies = await context.cookies();
        for (const cookie of cookies) {
            if (cookie.name === 'EPIC_BEARER_TOKEN') {
                this.bearerToken = cookie.value;
                this.session.defaults.headers.common['Authorization'] = `bearer ${this.bearerToken}`;
                return true;
            }
        }
        return false;
    }

    async claimNitroPromotion() {
        console.log("[*] Executing direct catalog checkout for promotion payload...");
        const query = `
        mutation($id: String!, $namespace: String!, $quantity: Int!) {
            Checkout {
                addToCart(input: { items: [{ id: $id, namespace: $namespace, quantity: $quantity }] }) {
                    cartId
                    success
                }
            }
        }
        `;
        const variables = {
            id: "p-discord-nitro-1-month-promo-identifier", // Target dynamic catalog promotion entry
            namespace: "epic",
            quantity: 1
        };

        try {
            const res = await this.session.post(
                EPIC_GRAPHQL_URL,
                { query, variables }
            );
            if (res.status === 200 && !res.data.errors) {
                return true;
            }
        } catch (error) {
            console.log(`[-] Checkout Error: ${error.message}`);
        }
        return false;
    }

    async pollForPromoLink(timeout = 300000) { // Increased to 5 minutes for safety
        console.log("[*] Intercepting downstream delivery vectors from tempmail.lol...");
        const startTime = Date.now();
        const { token } = this.mailConfig;

        while (Date.now() - startTime < timeout) {
            try {
                console.log(`[*] Polling inbox for Discord Nitro Promotion link...`);
                const messagesRes = await this.session.get(`https://api.tempmail.lol/v2/inbox?token=${token}`);
                const messages = messagesRes.data.emails || [];

                if (messages.length > 0) {
                    console.log(`[*] Found ${messages.length} email(s) during Promo search!`);
                }

                for (const msg of messages) {
                    const subject = msg.subject?.toLowerCase() || "";
                    console.log(`[*] Scanning promo email subject: "${subject}"`);

                    if (subject.includes("nitro") || subject.includes("discord")) {
                        const htmlContent = msg.html || msg.body || "";

                        // Match BOTH old and new Discord promo link formats!
                        const linkMatch = htmlContent.match(/https:\/\/promos\.discord\.gg\/[A-Za-z0-9]+/) ||
                                          htmlContent.match(/https:\/\/discord\.com\/billing\/promotions\/[A-Za-z0-9]+/);
                        if (linkMatch) {
                            console.log(`[+] SUCCESS! Intercepted Promo Link: ${linkMatch[0]}`);
                            return linkMatch[0];
                        }
                    }
                }
            } catch (error) {
                if (error.message.includes('429')) {
                    console.log(`[!] Rate limited by tempmail API. Waiting 10 seconds...`);
                    await new Promise(r => setTimeout(r, 10000));
                    continue;
                } else if (error.message.includes('401') || error.message.includes('404') || error.message.includes('410')) {
                    console.log(`[!] INBOX EXPIRED! Token is no longer valid. Aborting promo poll.`);
                    return null;
                }
                console.log(`[-] Silent polling error for promo link: ${error.message}`);
            }
            await new Promise(r => setTimeout(r, 5000));
        }
        return null;
    }

    async execute() {
        const mailConfig = await this.createTempMail();
        if (!mailConfig) {
            console.log("[-] Critical: Failed to spin up temp identity.");
            return;
        }
        console.log(`[+] Operational Inbox: ${mailConfig.address}`);

        console.log("[*] Instantiating native CloakBrowser GUI sandbox...");

        // Reverting to CloakBrowser because Selenium is getting caught by Cloudflare!
        // We let CloakBrowser use its own stealth Chromium to guarantee bypassing Cloudflare.
        const browser = await launch({
            headless: false, // Visible so you can solve the CAPTCHA manually!
            humanize: true,  // Stealth mode enabled!
            args: ['--window-size=1200,800']
        });

        const context = await browser.newContext();
        const page = await context.newPage();

        // Override default timeout + set fast navigation
        page.setDefaultTimeout(600000);
        page.setDefaultNavigationTimeout(30000);

        // Advanced Network Monitoring + EULA Token Capture
        let eulaContinuationToken = null;
        page.on('response', async (response) => {
            if (response.url().includes('epicgames.com') && response.status() >= 400) {
                console.log(`[!] SILENT API ERROR DETECTED: HTTP ${response.status()} at ${response.url()}`);
                try {
                    const body = await response.text();
                    console.log(`[!] Epic Games Backend Response: ${body.substring(0, 400)}`);
                    
                    // Capture EULA continuation token from API errors!
                    const parsed = JSON.parse(body);
                    if (parsed?.metadata?.correctiveAction === 'EULA_ACCEPTANCE' && parsed?.metadata?.continuation) {
                        eulaContinuationToken = parsed.metadata.continuation;
                        console.log(`[!] EULA Token Captured: ${eulaContinuationToken}`);
                    }
                } catch (e) { }
            }
        });

        console.log("[+] GUI context active. Initiating automated registration sequence...");

        await page.goto('https://www.epicgames.com/id/register/epic');

        try {
            // Step 1: Date of Birth Age Verification
            try {
                await page.waitForSelector('#year', { timeout: 10000 });
                console.log("[*] Date of Birth prompt detected. Injecting age verification...");

                const monthStr = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0');
                const dayStr = String(Math.floor(Math.random() * 28) + 1).padStart(2, '0');
                const yearStr = String(Math.floor(Math.random() * (2004 - 1980 + 1)) + 1980);

                await page.click('#month');
                await new Promise(r => setTimeout(r, 80));
                await page.keyboard.press('ArrowDown');
                await new Promise(r => setTimeout(r, 50));
                await page.keyboard.press('Enter');

                await page.type('#day', dayStr, { delay: 0 });
                await page.type('#year', yearStr, { delay: 0 });

                await new Promise(r => setTimeout(r, 150));
                await page.click('#continue');
                console.log("[+] Age verification bypassed.");
            } catch (dobError) {
                console.log("[*] No DOB prompt detected, skipping to email form...");
            }

            // Step 2: Epic Games asks "What's your email?"
            await page.waitForSelector('input[type="email"], #email', { timeout: 30000 });
            console.log("[*] Initial Email prompt detected. Injecting temp mail...");

            await page.fill('input[type="email"], #email', mailConfig.address);
            await new Promise(r => setTimeout(r, 150));

            await page.keyboard.press('Enter');
            console.log("[+] Email submitted.");

            await new Promise(r => setTimeout(r, 200));

            // Step 3: Wait for the main Name/Password registration form to load
            await page.waitForSelector('#name, input[name="name"]', { timeout: 30000 });

            console.log("[*] Injecting account credentials...");

            const firstNames = ["Gabriel", "John", "Kyle", "Karl", "Mike", "James", "Robert", "David", "William", "Richard", "Joseph", "Thomas", "Charles", "Christopher", "Daniel", "Matthew", "Anthony", "Mark", "Donald", "Steven"];
            const lastNames = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez", "Wilson", "Anderson", "Thomas", "Taylor", "Moore", "Jackson", "Martin"];

            const randomFirstName = firstNames[Math.floor(Math.random() * firstNames.length)];
            const randomLastName = lastNames[Math.floor(Math.random() * lastNames.length)];

            // Absolutely guaranteed unique display name - USING LETTERS to bypass Epic's "Too many numbers" anti-bot filter!
            const alphaHash = crypto.randomBytes(4).toString('hex').substring(0, 7); // 7 random letters/numbers
            const randomDisplayName = `${randomFirstName}${alphaHash}`;

            // Quick DOM settle
            await new Promise(r => setTimeout(r, 300));

            // Blitz-fill all fields INSTANTLY using native value setters (Bypasses J A M E S slow typing)
            await page.evaluate((data) => {
                const setNativeValue = (selector, val) => {
                    const el = document.querySelector(selector);
                    if (el) {
                        const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
                        nativeSetter.call(el, val);
                        el.dispatchEvent(new Event('input', { bubbles: true }));
                        el.dispatchEvent(new Event('change', { bubbles: true }));
                    }
                };
                setNativeValue('#name, input[name="name"]', data.first);
                setNativeValue('#lastName, input[name="lastName"]', data.last);
                setNativeValue('#displayName, input[name="displayName"]', data.display);
                setNativeValue('#password, input[name="password"]', data.pass);
            }, { first: randomFirstName, last: randomLastName, display: randomDisplayName, pass: 'Haikal2021!@#' });

            // CSRF token attach
            await new Promise(r => setTimeout(r, 300));

            // Accept TOS
            await page.locator('input#tos, #tos, [name="tos"]').first().evaluate(node => node.click());

            await new Promise(r => setTimeout(r, 150));

            // Submit form
            await page.click('#btn-submit');

            console.log("[+] Credentials submitted.");

            // === POST-SUBMISSION: Wait for CAPTCHA, OTP, or success ===
            console.log("[*] Waiting for post-submission response (CAPTCHA / OTP / redirect)...");

            // Wait for ANY of: URL change, CAPTCHA, or OTP verification form
            await page.waitForFunction(() => {
                const urlChanged = !window.location.href.includes('register');
                const captchaVisible = document.querySelector('.talon_challenge_container, iframe[title*="captcha"]') !== null;
                const otpVisible = document.querySelector('input[name="code-input-0"]') !== null;
                return urlChanged || captchaVisible || otpVisible;
            }, { timeout: 300000 });

            // Determine what triggered the resolution
            const postState = await page.evaluate(() => ({
                url: window.location.href,
                hasCaptcha: document.querySelector('.talon_challenge_container, iframe[title*="captcha"]') !== null,
                hasOtp: document.querySelector('input[name="code-input-0"]') !== null
            }));

            console.log(`[*] Post-submission state: URL=${postState.url}, CAPTCHA=${postState.hasCaptcha}, OTP=${postState.hasOtp}`);

            // Handle CAPTCHA if present
            if (postState.hasCaptcha) {
                console.log("[!] CAPTCHA INTERCEPTED!");
                console.log("[!] The script is now PAUSED. Please solve the CAPTCHA manually.");

                await page.waitForFunction(() => {
                    const captchaGone = document.querySelector('.talon_challenge_container, iframe[title*="captcha"]') === null;
                    const otpVisible = document.querySelector('input[name="code-input-0"]') !== null;
                    const urlChanged = !window.location.href.includes('register');
                    return (captchaGone && urlChanged) || otpVisible;
                }, { timeout: 600000 });

                console.log("[+] CAPTCHA solved! Resuming...");
                await new Promise(r => setTimeout(r, 500));
            }

            // Re-check for OTP after potential CAPTCHA solve
            const needsOtp = await page.evaluate(() => document.querySelector('input[name="code-input-0"]') !== null);

            if (needsOtp || postState.hasOtp) {
                // === EMAIL VERIFICATION OTP BYPASS ===
                console.log("========================================");
                console.log("[!] EMAIL VERIFICATION GATE DETECTED!");
                console.log("[!] Fetching OTP from tempmail.lol...");
                console.log("========================================");

                const { token } = mailConfig;
                let verificationCode = null;

                // Poll inbox every 3 seconds until we find the OTP
                while (true) {
                    try {
                        const messagesRes = await this.session.get(`https://api.tempmail.lol/v2/inbox?token=${token}`);
                        const messages = messagesRes.data.emails || [];

                        console.log(`[*] Inbox poll: ${messages.length} email(s) found`);

                        for (const msg of messages) {
                            const subject = msg.subject?.toLowerCase() || "";
                            if (subject.includes("verification") || subject.includes("verify") || subject.includes("epic")) {
                                const body = msg.html || msg.body || "";

                                // Primary regex: your custom pattern
                                const match1 = body.match(/(\d{6})\s*<\/p>[\s\S]*?Code will expire/);
                                // Fallback: any 6-digit number in the email
                                const match2 = body.match(/>\s*(\d{6})\s*</);
                                // Last resort: bare 6-digit number
                                const match3 = body.match(/\b(\d{6})\b/);

                                const found = match1?.[1] || match2?.[1] || match3?.[1];
                                if (found) {
                                    verificationCode = found;
                                    break;
                                }
                            }
                        }
                    } catch (e) {
                        console.log(`[-] Polling error: ${e.message}`);
                    }

                    if (verificationCode) break;

                    console.log("[*] OTP not found yet. Retrying in 3 seconds...");
                    await new Promise(r => setTimeout(r, 3000));
                }

                console.log("========================================");
                console.log(`[★] OTP CODE FOUND: ${verificationCode}`);
                console.log("========================================");

                // === INJECT OTP INTO REACT FORM ===
                // Strategy 1: Direct React __reactProps.onChange call (bypasses everything)
                // Strategy 2: _valueTracker reset + nativeInputValueSetter (fallback)
                // Strategy 3: Playwright keyboard (last resort)

                const injected = await page.evaluate((code) => {
                    const results = [];
                    const otpInputs = document.querySelectorAll('input[name^="code-input-"]');
                    results.push(`Found ${otpInputs.length} OTP input boxes`);

                    if (otpInputs.length === 0) return results;

                    for (let i = 0; i < Math.min(otpInputs.length, 6); i++) {
                        const input = otpInputs[i];
                        const digit = code[i];

                        // Find the React props key dynamically
                        const reactPropsKey = Object.keys(input).find(key => key.startsWith('__reactProps$'));

                        // Reset _valueTracker so React doesn't ignore the change
                        if (input._valueTracker) {
                            input._valueTracker.setValue('');
                        }

                        // Set the value via native setter
                        const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
                        nativeSetter.call(input, digit);

                        // Strategy 1: Call React's onChange directly
                        if (reactPropsKey) {
                            const props = input[reactPropsKey];
                            if (props && typeof props.onChange === 'function') {
                                props.onChange({ target: input, currentTarget: input, type: 'change' });
                                results.push(`Box ${i}: React onChange called with digit "${digit}"`);
                                continue;
                            }
                        }

                        // Strategy 2: Dispatch synthetic events
                        input.dispatchEvent(new Event('input', { bubbles: true }));
                        input.dispatchEvent(new Event('change', { bubbles: true }));
                        input.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: digit, code: `Digit${digit}` }));
                        input.dispatchEvent(new KeyboardEvent('keypress', { bubbles: true, key: digit, code: `Digit${digit}` }));
                        input.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, key: digit, code: `Digit${digit}` }));
                        results.push(`Box ${i}: Synthetic events dispatched with digit "${digit}"`);
                    }

                    return results;
                }, verificationCode);

                // Log injection results
                for (const line of injected) {
                    console.log(`[OTP] ${line}`);
                }

                // Strategy 3: If React props didn't work, try keyboard as last resort
                await new Promise(r => setTimeout(r, 200));
                const stillEmpty = await page.evaluate(() => {
                    const first = document.querySelector('input[name="code-input-0"]');
                    return first ? first.value === '' : true;
                });

                if (stillEmpty) {
                    console.log("[!] React injection didn't stick! Trying keyboard fallback...");
                    for (let i = 0; i < 6; i++) {
                        const box = page.locator(`input[name="code-input-${i}"]`);
                        await box.click();
                        await new Promise(r => setTimeout(r, 50));
                        await page.keyboard.press(verificationCode[i]);
                        await new Promise(r => setTimeout(r, 50));
                    }
                }

                await new Promise(r => setTimeout(r, 300));

                // Click Verify
                console.log("[*] Clicking Verify Email...");
                await page.locator('button:has-text("Verify Email"), button:has-text("Continue"), button:has-text("Confirm")').first().click();
                console.log("[+] Email verification submitted!");
                
                // Wait intelligently for Epic's backend to process and redirect to EULA or Account Settings
                console.log("[*] Waiting strictly for Epic's backend to load the Account Settings / EULA page...");
                try {
                    await page.waitForFunction(() => {
                        const currentUrl = window.location.href;
                        // Keep waiting until the URL actually changes to the post-registration destinations
                        return currentUrl.includes('/id/account') || 
                               currentUrl.includes('/eula') || 
                               currentUrl.includes('/correction');
                    }, { timeout: 60000 });
                } catch(e) {
                    console.log("[!] Wait timeout exceeded or page already shifted. Checking current state...");
                }
                
                await new Promise(r => setTimeout(r, 2000));
                console.log(`[*] Post-OTP landed on: ${page.url()}`);
                
                // If Epic dumped us straight on the EULA page, handle it right now before touching Nitro!
                const postOtpUrl = page.url();
                const postOtpHasEula = await page.evaluate(() => {
                    const text = document.body.innerText.toLowerCase();
                    return text.includes('end user license') || text.includes('license agreement');
                });
                
                if (postOtpUrl.includes('/eula') || postOtpUrl.includes('/correction') || postOtpHasEula) {
                    console.log("[!] EULA Appeared directly after OTP! Handling it now...");
                    await handleEulaPage(); // This calls the helper function defined below
                    await new Promise(r => setTimeout(r, 2000));
                }

            } else {
                console.log("[+] Registration successful! No OTP or CAPTCHA needed.");
            }
        } catch (regError) {
            console.log(`[-] Registration/OTP flow error: ${regError.message}`);
            try { await page.screenshot({ path: 'registration_error.png', fullPage: true }); } catch(e) {}
        }

        // === HELPER: Handle EULA acceptance on any page ===
        async function handleEulaPage() {
            console.log("[!] EULA PAGE DETECTED! Scrolling to find Accept button...");
            
            // Speed-scroll to bottom
            for (let s = 0; s < 30; s++) {
                await page.evaluate(() => window.scrollBy(0, 800));
                await new Promise(r => setTimeout(r, 50));
            }
            // Also scroll all overflow containers
            await page.evaluate(() => {
                document.querySelectorAll('*').forEach(el => {
                    if (el.scrollHeight > el.clientHeight + 10 && el.clientHeight > 50) el.scrollTop = el.scrollHeight;
                });
            });
            await new Promise(r => setTimeout(r, 500));

            // Try every possible Accept/Agree button
            for (const sel of [
                'button:has-text("Accept")', 'button:has-text("I Accept")',
                'button:has-text("Agree")', 'button:has-text("Continue")',
                'button[type="submit"]', 'input[type="submit"]',
                'a:has-text("Accept")', 'a:has-text("Agree")'
            ]) {
                try {
                    const btn = page.locator(sel).first();
                    if (await btn.isVisible({ timeout: 500 })) {
                        await btn.click();
                        console.log(`[+] EULA Accepted via: ${sel}`);
                        return true;
                    }
                } catch(e) {}
            }

            // If no button found, pause for manual acceptance
            console.log("[!] PAUSED - Accept button not found. Please accept EULA manually.");
            console.log("[!] Script will resume automatically when you leave the EULA page.");
            await page.waitForFunction(() => {
                return !window.location.href.includes('eula') && !window.location.href.includes('correction');
            }, { timeout: 300000 });
            console.log("[+] EULA page dismissed!");
            return true;
        };

        // === STEP 4: CLAIM DISCORD NITRO ===
        const NITRO_URL = 'https://store.epicgames.com/p/discord--discord-nitro';
        
        for (let storeAttempt = 0; storeAttempt < 5; storeAttempt++) {
            try {
                console.log(`\n[*] === STORE CLAIM ATTEMPT ${storeAttempt + 1}/5 ===`);
                
                // Reset EULA token for this attempt
                eulaContinuationToken = null;

                // If we have a captured EULA token from a previous API error, go directly to EULA page first!
                if (eulaContinuationToken) {
                    const eulaUrl = `https://www.epicgames.com/id/login/correction/eula?corrective=${eulaContinuationToken}&lang=en-US`;
                    console.log(`[*] Navigating directly to EULA page: ${eulaUrl}`);
                    await page.goto(eulaUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });
                    await new Promise(r => setTimeout(r, 1000));
                    await handleEulaPage();
                    await new Promise(r => setTimeout(r, 1000));
                }

                console.log(`[*] Navigating to: ${NITRO_URL}`);
                await page.goto(NITRO_URL, { waitUntil: 'domcontentloaded', timeout: 30000 });
                await new Promise(r => setTimeout(r, 1500));

                // Check where we landed
                const storeUrl = page.url();
                console.log(`[*] Landed on: ${storeUrl}`);

                // Case 1: Redirected to EULA page (URL-based detection)
                if (storeUrl.includes('/eula') || storeUrl.includes('/correction') || storeUrl.includes('corrective')) {
                    await handleEulaPage();
                    await new Promise(r => setTimeout(r, 1000));
                    continue; // Re-navigate to store
                }

                // Case 2: Redirected to login page (Epic does this for EULA sometimes!)
                if (storeUrl.includes('/id/login')) {
                    console.log("[*] Redirected to login page. Waiting for auto-redirect...");
                    
                    // Wait for Epic to auto-login and redirect
                    await new Promise(r => setTimeout(r, 2000));
                    
                    // Check if EULA token was captured during this redirect
                    if (eulaContinuationToken) {
                        console.log(`[!] EULA required! Navigating to acceptance page...`);
                        const eulaUrl = `https://www.epicgames.com/id/login/correction/eula?corrective=${eulaContinuationToken}&lang=en-US`;
                        await page.goto(eulaUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });
                        await new Promise(r => setTimeout(r, 1000));
                        await handleEulaPage();
                        await new Promise(r => setTimeout(r, 1000));
                        eulaContinuationToken = null;
                    } else {
                        // Wait for login to complete naturally
                        try {
                            await page.waitForFunction(() => !window.location.href.includes('/id/login'), { timeout: 30000 });
                        } catch(e) {
                            console.log("[*] Still on login page, retrying...");
                        }
                    }
                    continue; // Re-navigate to store
                }

                // Case 3: EULA page detected via content (not URL)
                const pageHasEula = await page.evaluate(() => {
                    const text = document.body.innerText.toLowerCase();
                    return text.includes('end user license') || text.includes('license agreement');
                });
                if (pageHasEula) {
                    await handleEulaPage();
                    await new Promise(r => setTimeout(r, 1000));
                    continue;
                }

                // === We're on the store page! ===
                console.log("[+] Successfully landed on store page!");

                // Handle Store Age Gate
                try {
                    const hasAgeGate = await page.evaluate(() => document.body.innerText.includes('Enter your date of birth'));
                    if (hasAgeGate) {
                        console.log("[*] Age Gate detected. Bypassing...");
                        const dropdowns = page.locator('[data-testid="DropdownMenuButton"]');
                        await dropdowns.nth(0).click(); await new Promise(r => setTimeout(r, 150));
                        await page.locator('li:has-text("01"), [role="option"]:has-text("01")').first().click(); await new Promise(r => setTimeout(r, 150));
                        await dropdowns.nth(1).click(); await new Promise(r => setTimeout(r, 150));
                        await page.locator('li:has-text("15"), [role="option"]:has-text("15")').first().click(); await new Promise(r => setTimeout(r, 150));
                        await dropdowns.nth(2).click(); await new Promise(r => setTimeout(r, 150));
                        await page.locator('li:has-text("1998"), [role="option"]:has-text("1998")').first().click(); await new Promise(r => setTimeout(r, 150));
                        await page.locator('button:has-text("Continue")').click();
                        console.log("[+] Age Gate bypassed!");
                        await new Promise(r => setTimeout(r, 800));
                    }
                } catch (ageErr) {
                    console.log("[*] No Age Gate or already bypassed.");
                }

                // Click GET button
                console.log("[*] Looking for GET button...");
                const getButton = page.locator('button[data-testid="purchase-cta-button"], button:has-text("Get")').first();
                await getButton.waitFor({ state: 'visible', timeout: 20000 });
                await getButton.click();
                
                console.log("[+] GET clicked!");
                console.log("[+] ★ DISCORD NITRO OFFICIALLY CLAIMED (1-Click Promo)! ★");
                
                await new Promise(r => setTimeout(r, 2000));
                break; // Success! Exit retry loop

            } catch (storeError) {
                console.log(`[-] Store claim attempt ${storeAttempt + 1} failed: ${storeError.message}`);
                try { await page.screenshot({ path: `store_error_${storeAttempt}.png`, fullPage: true }); } catch(e) {}
                if (storeAttempt < 4) {
                    console.log("[*] Retrying in 3 seconds...");
                    await new Promise(r => setTimeout(r, 3000));
                }
            }
        }

        // === STEP 5: Poll for Nitro promo link email ===
        console.log("[*] Checking inbox for Nitro Promotion email...");
        const promoUrl = await this.pollForPromoLink(600000); // 10 minutes max

        if (promoUrl) {
            // Guarantee it writes exactly to nitro.txt in the current execution folder
            const absolutePath = require('path').join(process.cwd(), OUTPUT_FILE);
            try {
                await fs.appendFile(absolutePath, `${promoUrl}\n`);
                console.log(`\n======================================================`);
                console.log(`[★] DISCORD NITRO PROMO LINK SECURED: ${promoUrl}`);
                console.log(`[★] Synchronized to persistent store: ${absolutePath}`);
                console.log(`======================================================\n`);
            } catch (err) {
                console.log(`[-] FAILED TO WRITE TO nitro.txt: ${err.message}`);
                console.log(`[-] PLEASE COPY THE LINK MANUALLY: ${promoUrl}`);
            }
        } else {
            console.log("[-] Promo link not found (inbox may have expired).");
        }

        console.log("[*] Deallocating GUI sandbox...");
        await browser.close();
    }
}

// Multi-threading control script
async function runBots() {
    const args = process.argv.slice(2);
    const numThreads = args.length > 0 ? parseInt(args[0]) : 1;

    console.log(`[*] Initiating Epic Games Nitro Bot Swarm...`);
    console.log(`[*] Target Threads: ${numThreads}`);

    const promises = [];

    for (let i = 0; i < numThreads; i++) {
        // Stagger the starts slightly so they don't all hit the API at the exact same millisecond
        promises.push(new Promise(resolve => {
            setTimeout(async () => {
                console.log(`[+] Launching Thread #${i + 1}...`);
                const automator = new EpicAutomatorJS();
                await automator.execute().catch(e => console.log(`[-] Thread #${i + 1} Error: ${e.message}`));
                resolve();
            }, i * 5000);
        }));
    }

    await Promise.all(promises);
    console.log(`[+] All assigned threads have completed their runs.`);
}

runBots();

//Edited
