/**
 * Playwright Executor
 *
 * Executes golden path steps using Playwright for full browser automation.
 * This module is optional - falls back to HTTP-based execution when Playwright
 * is not available.
 *
 * @module scanners/runtime/playwright-executor
 */
import { logger } from "../../logger.js";
let playwrightAvailable = false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let chromium;
/**
 * Check if Playwright is available
 */
export async function isPlaywrightAvailable() {
    if (playwrightAvailable)
        return true;
    try {
        // Dynamic import to avoid requiring playwright as a dependency
        // @ts-expect-error - playwright is optional peer dependency
        const playwright = await import("playwright");
        chromium = playwright.chromium;
        playwrightAvailable = true;
        logger.debug("runtime.playwright_available");
        return true;
    }
    catch {
        logger.debug("runtime.playwright_not_available");
        return false;
    }
}
/**
 * Playwright browser instance management
 */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let browserInstance;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let pageInstance;
/**
 * Launch browser for testing
 */
export async function launchBrowser(options = {}) {
    if (!await isPlaywrightAvailable()) {
        return false;
    }
    try {
        browserInstance = await chromium.launch({
            headless: options.headless ?? true,
            slowMo: options.slowMo,
        });
        pageInstance = await browserInstance.newPage();
        logger.info("runtime.browser_launched", { headless: options.headless ?? true });
        return true;
    }
    catch (error) {
        logger.error("runtime.browser_launch_failed", {
            error: error.message,
        });
        return false;
    }
}
/**
 * Close browser
 */
export async function closeBrowser() {
    if (pageInstance) {
        await pageInstance.close().catch(() => { });
        pageInstance = undefined;
    }
    if (browserInstance) {
        await browserInstance.close().catch(() => { });
        browserInstance = undefined;
    }
    logger.debug("runtime.browser_closed");
}
/**
 * Execute a step using Playwright
 */
export async function executeStepWithPlaywright(step, stepIndex, baseUrl, context) {
    if (!pageInstance) {
        return {
            step: stepIndex,
            action: step.action,
            success: false,
            duration: 0,
            error: "Browser not launched",
        };
    }
    const startTime = Date.now();
    const page = pageInstance;
    try {
        switch (step.action) {
            case "navigate": {
                const url = step.url?.startsWith("http")
                    ? step.url
                    : `${baseUrl}${step.url}`;
                await page.goto(url, {
                    timeout: step.timeout || 30000,
                    waitUntil: "networkidle",
                });
                context.currentUrl = page.url();
                return {
                    step: stepIndex,
                    action: step.action,
                    success: true,
                    duration: Date.now() - startTime,
                };
            }
            case "click": {
                if (!step.selector) {
                    return {
                        step: stepIndex,
                        action: step.action,
                        success: false,
                        duration: Date.now() - startTime,
                        error: "No selector provided for click action",
                    };
                }
                await page.click(step.selector, {
                    timeout: step.timeout || 10000,
                });
                return {
                    step: stepIndex,
                    action: step.action,
                    success: true,
                    duration: Date.now() - startTime,
                };
            }
            case "fill": {
                if (!step.selector || step.value === undefined) {
                    return {
                        step: stepIndex,
                        action: step.action,
                        success: false,
                        duration: Date.now() - startTime,
                        error: "Selector and value required for fill action",
                    };
                }
                await page.fill(step.selector, step.value, {
                    timeout: step.timeout || 10000,
                });
                return {
                    step: stepIndex,
                    action: step.action,
                    success: true,
                    duration: Date.now() - startTime,
                };
            }
            case "select": {
                if (!step.selector || step.value === undefined) {
                    return {
                        step: stepIndex,
                        action: step.action,
                        success: false,
                        duration: Date.now() - startTime,
                        error: "Selector and value required for select action",
                    };
                }
                await page.selectOption(step.selector, step.value, {
                    timeout: step.timeout || 10000,
                });
                return {
                    step: stepIndex,
                    action: step.action,
                    success: true,
                    duration: Date.now() - startTime,
                };
            }
            case "assert": {
                if (step.url) {
                    const currentUrl = page.url();
                    const passed = currentUrl.includes(step.url);
                    return {
                        step: stepIndex,
                        action: step.action,
                        success: passed,
                        duration: Date.now() - startTime,
                        assertion: {
                            expected: step.url,
                            actual: currentUrl,
                            passed,
                        },
                    };
                }
                if (step.selector) {
                    const element = await page.$(step.selector);
                    const exists = element !== null;
                    if (step.value !== undefined && element) {
                        const text = await element.textContent();
                        const passed = text?.includes(step.value) ?? false;
                        return {
                            step: stepIndex,
                            action: step.action,
                            success: passed,
                            duration: Date.now() - startTime,
                            assertion: {
                                expected: step.value,
                                actual: text || "",
                                passed,
                            },
                        };
                    }
                    return {
                        step: stepIndex,
                        action: step.action,
                        success: exists,
                        duration: Date.now() - startTime,
                        assertion: {
                            expected: "element exists",
                            actual: exists ? "found" : "not found",
                            passed: exists,
                        },
                    };
                }
                return {
                    step: stepIndex,
                    action: step.action,
                    success: true,
                    duration: Date.now() - startTime,
                    error: "No assertion criteria provided",
                };
            }
            case "wait": {
                const waitTime = step.timeout || 1000;
                await page.waitForTimeout(waitTime);
                return {
                    step: stepIndex,
                    action: step.action,
                    success: true,
                    duration: Date.now() - startTime,
                };
            }
            case "screenshot": {
                const screenshotPath = context.screenshotsDir
                    ? `${context.screenshotsDir}/step-${stepIndex}.png`
                    : `/tmp/vaspera-screenshot-${Date.now()}.png`;
                await page.screenshot({
                    path: screenshotPath,
                    fullPage: true,
                });
                return {
                    step: stepIndex,
                    action: step.action,
                    success: true,
                    duration: Date.now() - startTime,
                    screenshot: screenshotPath,
                };
            }
            case "api": {
                const url = step.url?.startsWith("http")
                    ? step.url
                    : `${baseUrl}${step.url}`;
                const response = await page.request.fetch(url, {
                    method: step.method || "GET",
                    headers: step.headers,
                    data: step.body,
                    timeout: step.timeout || 10000,
                });
                return {
                    step: stepIndex,
                    action: step.action,
                    success: response.ok(),
                    duration: Date.now() - startTime,
                    error: response.ok() ? undefined : `HTTP ${response.status()}`,
                };
            }
            default:
                return {
                    step: stepIndex,
                    action: step.action,
                    success: false,
                    duration: Date.now() - startTime,
                    error: `Unknown action: ${step.action}`,
                };
        }
    }
    catch (error) {
        return {
            step: stepIndex,
            action: step.action,
            success: false,
            duration: Date.now() - startTime,
            error: error.message,
        };
    }
}
/**
 * Run a complete flow with Playwright
 */
export async function runFlowWithPlaywright(flow, baseUrl, options = {}) {
    const startTime = Date.now();
    const stepResults = [];
    const screenshots = [];
    let failed = false;
    // Launch browser
    const launched = await launchBrowser({
        headless: options.headless,
        slowMo: options.slowMo,
    });
    if (!launched) {
        return {
            flowName: flow.name,
            success: false,
            totalSteps: flow.steps.length,
            passedSteps: 0,
            failedSteps: flow.steps.length,
            duration: Date.now() - startTime,
            steps: flow.steps.map((step, i) => ({
                step: i,
                action: step.action,
                success: false,
                duration: 0,
                error: "Failed to launch browser",
            })),
            screenshots: [],
        };
    }
    const context = {
        currentUrl: baseUrl,
        screenshotsDir: options.screenshotsDir,
    };
    logger.info("runtime.playwright_flow_started", {
        name: flow.name,
        steps: flow.steps.length,
    });
    try {
        for (let i = 0; i < flow.steps.length; i++) {
            const step = flow.steps[i];
            if (failed) {
                stepResults.push({
                    step: i,
                    action: step.action,
                    success: false,
                    duration: 0,
                    error: "Skipped due to previous failure",
                });
                continue;
            }
            const result = await executeStepWithPlaywright(step, i, baseUrl, context);
            stepResults.push(result);
            if (result.screenshot) {
                screenshots.push(result.screenshot);
            }
            if (!result.success) {
                failed = true;
                logger.warn("runtime.playwright_step_failed", {
                    flow: flow.name,
                    step: i,
                    action: step.action,
                    error: result.error,
                });
            }
            else {
                logger.debug("runtime.playwright_step_passed", {
                    flow: flow.name,
                    step: i,
                    action: step.action,
                    duration: result.duration,
                });
            }
        }
    }
    finally {
        await closeBrowser();
    }
    const passedSteps = stepResults.filter((r) => r.success).length;
    const failedSteps = stepResults.filter((r) => !r.success && !r.error?.includes("Skipped")).length;
    logger.info("runtime.playwright_flow_completed", {
        name: flow.name,
        passed: passedSteps,
        failed: failedSteps,
        duration: Date.now() - startTime,
    });
    return {
        flowName: flow.name,
        success: !failed,
        totalSteps: flow.steps.length,
        passedSteps,
        failedSteps,
        duration: Date.now() - startTime,
        steps: stepResults,
        screenshots,
    };
}
//# sourceMappingURL=playwright-executor.js.map