"use strict";
/**
 * Vision Test Runner
 *
 * Main execution engine for vision-based testing.
 * Manages Playwright browser lifecycle, coordinates vision agents,
 * and produces comprehensive test reports.
 *
 * REPLACES: browser-test-runner.ts (old selector-based testing)
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.VisionTestRunner = void 0;
const playwright_1 = require("playwright");
const path = __importStar(require("path"));
const fs = __importStar(require("fs-extra"));
const VisionTestCoordinator_1 = require("../agents/implementations/VisionTestCoordinator");
const ContextService_1 = require("../services/ContextService");
const uuid_1 = require("uuid");
const chalk_1 = __importDefault(require("chalk"));
class VisionTestRunner {
    constructor(projectPath) {
        this.projectPath = projectPath;
        this.contextService = new ContextService_1.ContextService(projectPath);
        this.screenshotsDir = path.join(projectPath, ".mycontext", "vision-screenshots");
        this.videosDir = path.join(projectPath, ".mycontext", "vision-videos");
        fs.ensureDirSync(this.screenshotsDir);
        fs.ensureDirSync(this.videosDir);
    }
    /**
     * Run a vision test mission
     */
    async runTest(mission, config = { headless: true }) {
        const executionId = (0, uuid_1.v4)();
        const startTime = Date.now();
        console.log(chalk_1.default.bold.magenta(`\n${"═".repeat(70)}`));
        console.log(chalk_1.default.bold.magenta(`🚀 VISION TEST RUNNER`));
        console.log(chalk_1.default.bold.magenta(`${"═".repeat(70)}\n`));
        console.log(chalk_1.default.white(`Mission: ${mission.name}`));
        console.log(chalk_1.default.gray(`Description: ${mission.description}`));
        console.log(chalk_1.default.gray(`Expected Outcome: ${mission.expectedOutcome}`));
        console.log(chalk_1.default.gray(`Browser: ${config.headless ? "Headless" : "Headed"}`));
        console.log(chalk_1.default.gray(`Execution ID: ${executionId}\n`));
        try {
            // Initialize context service
            await this.contextService.initialize();
            // Launch browser
            await this.launchBrowser(config);
            if (!this.context) {
                throw new Error("Failed to create browser context");
            }
            // Create new page
            const page = await this.context.newPage();
            // Set viewport if specified
            if (config.viewport) {
                await page.setViewportSize(config.viewport);
            }
            // Navigate to start URL
            const startUrlRaw = mission.startUrl ||
                config.baseUrl ||
                mission.sourceFlow ||
                "http://localhost:3000";
            // If a mission supplies a relative path (e.g. "/en/login"), resolve against baseUrl/default.
            const startUrl = (() => {
                try {
                    return new URL(startUrlRaw).toString();
                }
                catch {
                    const base = (config.baseUrl && (() => { try {
                        return new URL(config.baseUrl).origin;
                    }
                    catch {
                        return null;
                    } })()) ||
                        "http://localhost:3000";
                    return new URL(startUrlRaw, base).toString();
                }
            })();
            console.log(chalk_1.default.cyan(`\n🌐 Navigating to: ${startUrl}\n`));
            await page.goto(startUrl, { waitUntil: "domcontentloaded" });
            // Wait a moment for page to settle
            await page.waitForTimeout(1000);
            // Create and run vision test coordinator
            const coordinator = new VisionTestCoordinator_1.VisionTestCoordinator();
            const coordinatorOutput = await coordinator.run({
                page,
                browserContext: this.context,
                mission,
                projectPath: this.projectPath,
            });
            // Get the result
            const result = coordinatorOutput.result;
            // Capture video path before closing page (Playwright only flushes video on close)
            let videoPath;
            if (page.video()) {
                try {
                    videoPath = await page.video().path();
                }
                catch {
                    // Video path may not be available if recording wasn't configured
                }
            }
            // Close page (this flushes the video file)
            await page.close();
            // Store video path for external access
            if (videoPath) {
                this.lastVideoPath = videoPath;
                // Update demo artifacts with real video path and move file
                if (result.demoArtifacts && result.demoArtifacts.formats.video) {
                    const targetPath = result.demoArtifacts.formats.video.path;
                    try {
                        // Ensure target directory exists
                        await fs.ensureDir(path.dirname(targetPath));
                        // Move/copy video to target location
                        await fs.copy(videoPath, targetPath);
                        // Update result with final path and size
                        const stats = await fs.stat(targetPath);
                        result.demoArtifacts.formats.video.path = targetPath;
                        result.demoArtifacts.formats.video.size = stats.size;
                    }
                    catch (copyError) {
                        console.warn(chalk_1.default.yellow(`\n⚠️  Failed to move video recording to demo directory: ${copyError}`));
                        // Fallback: keep the original Playwright path
                        result.demoArtifacts.formats.video.path = videoPath;
                    }
                }
            }
            // Display final summary
            this.displaySummary(result);
            return result;
        }
        catch (error) {
            console.error(chalk_1.default.red(`\n❌ Test execution failed: ${error.message}`));
            console.error(chalk_1.default.gray(error.stack));
            const endTime = Date.now();
            // Return error result
            const errorResult = {
                missionId: mission.id,
                executionId,
                status: "error",
                startedAt: new Date(startTime).toISOString(),
                completedAt: new Date(endTime).toISOString(),
                duration: endTime - startTime,
                steps: [],
                validationResults: [],
                finalState: {
                    url: "",
                },
                error: {
                    message: error.message,
                    stack: error.stack,
                },
            };
            return errorResult;
        }
        finally {
            // Always close browser
            await this.closeBrowser();
        }
    }
    /**
     * Generate a demo (record a flow without assertions)
     */
    async generateDemo(request) {
        const requestId = (0, uuid_1.v4)();
        const startTime = Date.now();
        console.log(chalk_1.default.bold.blue(`\n${"═".repeat(70)}`));
        console.log(chalk_1.default.bold.blue(`🎬 DEMO GENERATION`));
        console.log(chalk_1.default.bold.blue(`${"═".repeat(70)}\n`));
        console.log(chalk_1.default.white(`Flow: ${request.flow}`));
        console.log(chalk_1.default.gray(`Start URL: ${request.startUrl}`));
        console.log(chalk_1.default.gray(`Formats: ${request.config.outputFormats.join(", ")}\n`));
        try {
            // Create a temporary mission for demo recording
            const demoMission = {
                id: requestId,
                name: request.metadata?.title || "Demo Recording",
                description: request.metadata?.description || request.flow,
                mission: request.flow,
                expectedOutcome: "Demo recorded successfully",
                tags: request.metadata?.tags || ["demo"],
                createdAt: new Date().toISOString(),
                updatedAt: new Date().toISOString(),
                recordDemo: true,
                demoConfig: request.config,
                sourceFlow: request.startUrl,
            };
            // Run test with demo recording enabled
            const result = await this.runTest(demoMission, {
                headless: false, // Show browser for demos
                recordVideo: true,
            });
            if (result.status === "error") {
                throw new Error(result.error?.message || "Demo generation failed");
            }
            const endTime = Date.now();
            const demoResult = {
                requestId,
                status: result.demoArtifacts ? "success" : "failed",
                artifacts: result.demoArtifacts,
                duration: endTime - startTime,
            };
            console.log(chalk_1.default.green(`\n✅ Demo generation complete!`));
            console.log(chalk_1.default.gray(`Duration: ${(demoResult.duration / 1000).toFixed(1)}s\n`));
            return demoResult;
        }
        catch (error) {
            console.error(chalk_1.default.red(`\n❌ Demo generation failed: ${error.message}`));
            const endTime = Date.now();
            return {
                requestId,
                status: "failed",
                artifacts: {
                    generatedAt: new Date().toISOString(),
                    formats: {},
                },
                duration: endTime - startTime,
                error: error.message,
            };
        }
    }
    /**
     * Run multiple tests in sequence
     */
    async runTestSuite(missions, config = { headless: true }) {
        console.log(chalk_1.default.bold.cyan(`\n${"═".repeat(70)}`));
        console.log(chalk_1.default.bold.cyan(`🧪 VISION TEST SUITE`));
        console.log(chalk_1.default.bold.cyan(`${"═".repeat(70)}\n`));
        console.log(chalk_1.default.white(`Tests: ${missions.length}\n`));
        const results = [];
        for (let i = 0; i < missions.length; i++) {
            const mission = missions[i];
            if (!mission)
                continue; // Safety check for undefined
            console.log(chalk_1.default.yellow(`\n[${i + 1}/${missions.length}] Running: ${mission.name}`));
            const result = await this.runTest(mission, config);
            results.push(result);
            // Brief pause between tests
            await new Promise((resolve) => setTimeout(resolve, 2000));
        }
        // Print suite summary
        console.log(chalk_1.default.bold.cyan(`\n${"═".repeat(70)}`));
        console.log(chalk_1.default.bold.cyan(`📊 TEST SUITE SUMMARY`));
        console.log(chalk_1.default.bold.cyan(`${"═".repeat(70)}\n`));
        const passed = results.filter((r) => r.status === "passed").length;
        const failed = results.filter((r) => r.status === "failed").length;
        const errors = results.filter((r) => r.status === "error").length;
        console.log(chalk_1.default.green(`✓ Passed: ${passed}`));
        console.log(chalk_1.default.red(`✗ Failed: ${failed}`));
        console.log(chalk_1.default.yellow(`⚠ Errors: ${errors}`));
        console.log(chalk_1.default.white(`Total: ${results.length}\n`));
        return results;
    }
    /**
     * Launch browser with configuration
     */
    async launchBrowser(config) {
        console.log(chalk_1.default.gray(`\n🌐 Launching browser...`));
        this.browser = await playwright_1.chromium.launch({
            headless: config.headless !== false,
            slowMo: config.slowMo || 0,
        });
        const contextOptions = {
            viewport: config.viewport || { width: 1280, height: 720 },
        };
        // Enable video recording if requested
        if (config.recordVideo) {
            contextOptions.recordVideo = {
                dir: this.videosDir,
                size: config.viewport || { width: 1280, height: 720 },
            };
        }
        this.context = await this.browser.newContext(contextOptions);
        console.log(chalk_1.default.green(`✓ Browser ready\n`));
    }
    /**
     * Close browser
     */
    async closeBrowser() {
        if (this.context) {
            await this.context.close();
            this.context = undefined;
        }
        if (this.browser) {
            await this.browser.close();
            this.browser = undefined;
        }
    }
    /**
     * Display test summary
     */
    displaySummary(result) {
        console.log(chalk_1.default.bold.magenta(`\n${"═".repeat(70)}`));
        console.log(chalk_1.default.bold.magenta(`📋 TEST SUMMARY`));
        console.log(chalk_1.default.bold.magenta(`${"═".repeat(70)}\n`));
        // Status
        const statusColor = result.status === "passed" ? "green" : "red";
        console.log(chalk_1.default[statusColor](`Status: ${result.status.toUpperCase()}`));
        // Duration
        console.log(chalk_1.default.white(`Duration: ${((result.duration || 0) / 1000).toFixed(1)}s`));
        // Steps
        const successfulSteps = result.steps.filter((s) => s.success).length;
        console.log(chalk_1.default.white(`Steps: ${successfulSteps}/${result.steps.length} successful`));
        // Validations
        if (result.validationResults.length > 0) {
            const passedValidations = result.validationResults.filter((v) => v.passed).length;
            console.log(chalk_1.default.white(`Validations: ${passedValidations}/${result.validationResults.length} passed`));
        }
        // Visual Summary
        if (result.visualSummary) {
            console.log(chalk_1.default.white(`Screenshots: ${result.visualSummary.totalScreenshots}`));
            if (result.visualSummary.visualRegressionsDetected > 0) {
                console.log(chalk_1.default.yellow(`Visual Regressions: ${result.visualSummary.visualRegressionsDetected}`));
            }
        }
        // Demo Artifacts
        if (result.demoArtifacts) {
            console.log(chalk_1.default.white(`Demo Formats: ${Object.keys(result.demoArtifacts.formats).join(", ")}`));
        }
        // Final URL
        console.log(chalk_1.default.gray(`\nFinal URL: ${result.finalState.url}`));
        // Error details
        if (result.error) {
            console.log(chalk_1.default.red(`\nError: ${result.error.message}`));
        }
        console.log(chalk_1.default.bold.magenta(`\n${"═".repeat(70)}\n`));
    }
    /**
     * Take a screenshot (utility method)
     */
    async takeScreenshot(page, name) {
        const filename = `${name}-${Date.now()}.png`;
        const filepath = path.join(this.screenshotsDir, filename);
        await page.screenshot({ path: filepath, fullPage: true });
        return filepath;
    }
    /**
     * Get browser instance (for external use)
     */
    getBrowser() {
        return this.browser;
    }
    /**
     * Get browser context (for external use)
     */
    getContext() {
        return this.context;
    }
    /**
     * Get the last recorded video path
     */
    getLastVideoPath() {
        return this.lastVideoPath;
    }
}
exports.VisionTestRunner = VisionTestRunner;
//# sourceMappingURL=vision-test-runner.js.map