/**
 * JSDOM-based executor for simulating IIFE bundles
 *
 * Executes actual production IIFE bundles in a virtual DOM environment
 * with env-based mocking from destination examples.
 */
import { JSDOM, VirtualConsole } from 'jsdom';
import fs from 'fs-extra';
import { getErrorMessage } from '../../core/index.js';
/**
 * Build sandbox from destination-provided envs
 *
 * Merges env mocks from each destination's envs, wrapping
 * specified paths with CallTracker for API call tracking.
 */
function buildSandboxFromEnvs(envs, destinations, tracker) {
    // Base browser APIs (not provided by destinations)
    const baseBrowserMocks = {
        Image: class MockImage {
            src = '';
            onload = (() => { });
            onerror = (() => { });
        },
        fetch: async () => ({ ok: true, json: async () => ({}) }),
        location: { href: 'http://localhost' },
        navigator: { userAgent: 'Mozilla/5.0 (walkerOS Simulation)' },
    };
    const sandbox = {
        window: { ...baseBrowserMocks },
        document: {},
    };
    // Add destination-specific env mocks
    for (const [destKey, destConfig] of Object.entries(destinations)) {
        const destEnv = envs[destKey];
        if (!destEnv?.push)
            continue;
        const mockEnv = destEnv.push;
        const trackPaths = destEnv.simulation || [];
        // Use existing CallTracker to wrap env
        const trackedEnv = tracker.wrapEnv(mockEnv, trackPaths.map((p) => `${destKey}:${p}`));
        // Merge window properties
        if (trackedEnv.window && typeof trackedEnv.window === 'object') {
            Object.assign(sandbox.window, trackedEnv.window);
        }
        // Merge document properties
        if (trackedEnv.document && typeof trackedEnv.document === 'object') {
            Object.assign(sandbox.document, trackedEnv.document);
        }
    }
    return sandbox;
}
/**
 * Wait for async window property assignment
 *
 * IIFE bundles execute asynchronously and assign to window.
 * This helper polls until the property appears or timeout occurs.
 */
function waitForWindowProperty(window, prop, timeout = 5000) {
    return new Promise((resolve, reject) => {
        const start = Date.now();
        const check = () => {
            if (window[prop] !== undefined) {
                resolve();
            }
            else if (Date.now() - start > timeout) {
                reject(new Error(`Timeout waiting for window.${prop}. ` +
                    `IIFE may have failed to execute or assign to window.`));
            }
            else {
                setImmediate(check);
            }
        };
        check();
    });
}
/**
 * Execute production IIFE bundle in JSDOM with env-based mocking
 *
 * Main orchestration function that:
 * 1. Creates JSDOM virtual DOM
 * 2. Builds sandbox from dynamically loaded envs
 * 3. Executes IIFE in JSDOM
 * 4. Waits for window.collector/elb assignment
 * 5. Runs event through elb
 * 6. Returns tracked API calls
 */
export async function executeInJSDOM(bundlePath, destinations, event, tracker, envs, timeout = 10000) {
    const start = Date.now();
    // 1. Create JSDOM virtual DOM with console suppression
    const virtualConsole = new VirtualConsole();
    // Silent mode - don't pipe console output to process stdout/stderr
    const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
        url: 'http://localhost',
        runScripts: 'dangerously', // Allow script execution
        resources: 'usable',
        virtualConsole,
    });
    const { window } = dom;
    // 2. Build sandbox from dynamically loaded envs and inject into window
    const sandbox = buildSandboxFromEnvs(envs, destinations, tracker);
    Object.assign(window, sandbox.window);
    Object.assign(window.document, sandbox.document);
    // 3. Load and execute bundle code
    const bundleCode = await fs.readFile(bundlePath, 'utf8');
    try {
        window.eval(bundleCode);
    }
    catch (error) {
        throw new Error(`Bundle execution failed: ${getErrorMessage(error)}`);
    }
    // 4. Wait for window.collector and window.elb assignments
    try {
        await waitForWindowProperty(window, 'collector', timeout);
        await waitForWindowProperty(window, 'elb', timeout);
    }
    catch (error) {
        throw new Error(`Window property assignment failed: ${getErrorMessage(error)}`);
    }
    const { collector, elb } = window;
    // 5. Run event through elb
    let elbResult;
    try {
        elbResult = (await elb(event.name, event.data));
    }
    catch (error) {
        throw new Error(`Event execution failed: ${getErrorMessage(error)}`);
    }
    // 6. Return results with tracked calls
    return {
        collector,
        elb,
        elbResult,
        usage: tracker.getCalls(),
        duration: Date.now() - start,
    };
}
//# sourceMappingURL=jsdom-executor.js.map