'use strict';
const path = require('path');
const fs = require('fs');
const { ScriptExecutor } = require('../scriptExecutor');

/**
 * Handler para tareas de tipo EXECUTE_SCRIPT.
 *
 * Recibe un array de steps en task.payload.steps y los ejecuta con ScriptExecutor.
 * Usa CloakBrowser con contexto persistente (sesión guardada en disco) para que
 * el login de Instagram se mantenga entre tareas.
 *
 * La lógica de negocio (selectores, flujos, Gemini) vive exclusivamente en el
 * servidor, que genera el array de steps. Este handler no contiene ninguna
 * referencia a Instagram.
 */
class ScriptTaskHandler {
    log(msg) {
        console.log(`[SCRIPT_HANDLER] ${new Date().toISOString()} ${msg}`);
    }

    async execute(task) {
        const { username } = task.account || {};
        const { steps = [], sessionDir, contextOptions = {} } = task.payload || {};

        if (!steps.length) throw new Error('El payload no contiene pasos (steps)');

        this.log(`Ejecutando script de ${steps.length} pasos para @${username}`);

        // ── Directorio de sesión persistente ─────────────────────────────────
        // El servidor indica qué directorio usar (ej. "sessions/usuario").
        // Si no se especifica, se usa "sessions/<username>" por defecto.
        const dataDir = process.env.XPONENCIA_DATA_DIR || require('../workerConfig').dataDir || process.cwd();
        const relDir = sessionDir || `sessions/${username || 'default'}`;
        const userDataDir = path.isAbsolute(relDir) ? relDir : path.join(dataDir, relDir);
        fs.mkdirSync(userDataDir, { recursive: true });

        // ── Limpiar archivos de bloqueo de Chromium ───────────────────────────
        for (const lockFile of ['SingletonLock', 'SingletonSocket', 'SingletonCookie']) {
            const lp = path.join(userDataDir, lockFile);
            if (fs.existsSync(lp)) {
                try { fs.unlinkSync(lp); } catch (_) { /* ignorar */ }
            }
        }

        const headless     = process.env.BROWSER_HEADLESS === 'true';
        // Default: playwright (patchright + stealth + channel chrome).
        // CloakBrowser solo si BROWSER_ENGINE=cloakbrowser explícitamente.
        const useCloakBrowser = process.env.BROWSER_ENGINE === 'cloakbrowser';

        // ── Abrir navegador con contexto persistente ──────────────────────────
        let browser;

        if (useCloakBrowser) {
            // CloakBrowser — Real PC Mode (solo si BROWSER_ENGINE=cloakbrowser)
            const { REAL_PC_LAUNCH_OPTIONS, getLaunchArgs, applyRealPcPatches } = require('../realPcContext');
            try {
                const { launchPersistentContext } = await require('../../src/cloakbrowserLoader').getCloakBrowser();
                browser = await launchPersistentContext({
                    userDataDir,
                    headless,
                    ...REAL_PC_LAUNCH_OPTIONS,
                    args: getLaunchArgs(userDataDir),
                    ...contextOptions,
                });
                await applyRealPcPatches(browser);
                this.log(`CloakBrowser lanzado con sesión en: ${userDataDir}`);
            } catch (cloakErr) {
                // Fallback a Patchright/Playwright + stealth si CloakBrowser no está disponible
                this.log(`CloakBrowser no disponible (${cloakErr.message}), fallback a Patchright+stealth`);
                const { getChromium, applyPlaywrightStealth } = require('../realPcContext');
                const chromium = getChromium();
                const channel  = process.env.BROWSER_CHANNEL || undefined;
                browser = await chromium.launchPersistentContext(userDataDir, {
                    headless,
                    channel,
                    args: ['--no-sandbox', '--disable-blink-features=AutomationControlled'],
                    viewport: null,
                    locale: 'es-AR',
                    timezoneId: 'America/Argentina/Buenos_Aires',
                    ...contextOptions,
                });
                await applyPlaywrightStealth(browser);
            }
        } else {
            // Patchright (→ fallback playwright-core) + stealth + channel chrome (modo por defecto)
            const { getChromium, applyPlaywrightStealth } = require('../realPcContext');
            const chromium = getChromium();
            const channel  = process.env.BROWSER_CHANNEL || undefined; // 'chrome' → Google Chrome del sistema
            this.log(`Browser: patchright + channel=${channel || 'chromium'} + stealth`);
            browser = await chromium.launchPersistentContext(userDataDir, {
                headless,
                channel,
                args: [
                    '--no-sandbox',
                    '--disable-blink-features=AutomationControlled',
                ],
                viewport: null,   // null = Chrome decide el viewport según la ventana real
                locale: 'es-AR',
                timezoneId: 'America/Argentina/Buenos_Aires',
                ...contextOptions,
            });
            await applyPlaywrightStealth(browser);
        }

        // Usar la primera página existente o abrir una nueva
        const pages = browser.pages ? browser.pages() : [];
        const page = pages.length ? pages[0] : await browser.newPage();

        // ── Detector de cierre inesperado del navegador ───────────────────────
        // Si el usuario cierra la ventana o el proceso del browser muere,
        // rechazamos inmediatamente con un error para que workerRuntime
        // llame a reportFailure y la tarea no quede colgada en RUNNING.
        let _browserClosedReject = null;
        const browserClosedPromise = new Promise((_, reject) => {
            _browserClosedReject = reject;
        });
        const onBrowserDisconnect = () => {
            if (_browserClosedReject) {
                _browserClosedReject(new Error('BROWSER_CLOSED: el navegador se cerró inesperadamente'));
            }
        };
        browser.on('disconnected', onBrowserDisconnect);

        try {
            const executor = new ScriptExecutor(page, {
                serverUrl:   process.env.TASK_SERVER_URL,
                workerId:    process.env.WORKER_ID,
                taskId:      task.id,
                cancelToken: task._cancelToken || null,
            });
            // Correr la ejecución en carrera con el detector de cierre:
            // si el browser se cierra, browserClosedPromise rechaza y cancela.
            const result = await Promise.race([
                executor.execute(steps),
                browserClosedPromise,
            ]);
            this.log(`@${username} script completado (${result.stepsCompleted}/${result.totalSteps} pasos)`);
            return { processedType: 'EXECUTE_SCRIPT', account: username, ...result };
        } finally {
            browser.off('disconnected', onBrowserDisconnect);
            _browserClosedReject = null;
            await browser.close().catch(() => {});
        }
    }
}

module.exports = ScriptTaskHandler;
