#!/usr/bin/env node
/**
 * Judge evaluation CLI that reuses the window.__judgeEvaluation hook.
 *
 * Workflow:
 *   1. Launch Playwright and navigate to the Code Mode editor
 *   2. Ensure the user is authenticated (optionally logging in)
 *   3. Wait for window.__judgeEvaluation to be available
 *   4. Invoke judge evaluation through the socket handler
 *   5. Print the evaluation result
 */
import fs from "node:fs";
import path from "node:path";
function parseArgs() {
    const args = process.argv.slice(2);
    const positional = [];
    const opts = {};
    for (const arg of args) {
        if (arg.startsWith("--")) {
            const [key, value] = arg.slice(2).split("=");
            switch (key) {
                case "app-url":
                    opts.appUrl = value;
                    break;
                case "prompt-id":
                    opts.promptId = value;
                    break;
                case "prompt":
                    opts.prompt = value;
                    break;
                case "storage-state":
                    opts.storageStatePath = value;
                    break;
                case "headed":
                    opts.headed = true;
                    break;
                case "headless":
                    opts.headed = false;
                    break;
                case "login-email":
                    opts.loginEmail = value;
                    break;
                case "login-password":
                    opts.loginPassword = value;
                    break;
                case "login-url":
                    opts.loginUrl = value;
                    break;
                case "app-id":
                    opts.appId = value;
                    break;
                case "complexity":
                    opts.complexity = normalizeComplexity(value);
                    break;
                case "jwt":
                    opts.jwt = value;
                    break;
            }
        }
        else {
            positional.push(arg);
        }
    }
    if (positional.length >= 3) {
        opts.appUrl = positional[0];
        opts.promptId = positional[1];
        opts.prompt = positional[2];
        if (positional[3]) {
            opts.complexity = normalizeComplexity(positional[3]);
        }
    }
    if (!opts.appUrl || !opts.promptId || !opts.prompt) {
        console.error("Usage: pnpm exec tsx judge-eval-http.ts [--app-url=...] [--prompt-id=...] [--prompt=...] [--complexity=...] [--jwt=...] [--login-email=...] [--login-password=...] [--login-url=...]");
        console.error("Example:");
        console.error('  pnpm exec tsx judge-eval-http.ts --app-url=http://localhost:3000 --prompt-id=example --prompt="Create a todo app"');
        process.exit(1);
    }
    // Env fallbacks for login options
    opts.loginEmail = opts.loginEmail ?? process.env.JUDGE_LOGIN_EMAIL;
    opts.loginPassword = opts.loginPassword ?? process.env.JUDGE_LOGIN_PASSWORD;
    opts.loginUrl = opts.loginUrl ?? process.env.JUDGE_LOGIN_URL;
    return opts;
}
function normalizeComplexity(value) {
    if (!value)
        return undefined;
    return value.replace(/^\[/, "").replace(/\]$/, "");
}
function resolveHeadlessFlag(override) {
    if (override === true) {
        return false;
    }
    if (override === false) {
        return true;
    }
    const envValue = process.env.JUDGE_HEADLESS;
    if (!envValue) {
        return true;
    }
    return !["false", "0", "off"].includes(envValue.toLowerCase());
}
function resolveStorageStatePath(explicit) {
    if (explicit) {
        return explicit;
    }
    if (process.env.JUDGE_STORAGE_STATE_PATH) {
        return process.env.JUDGE_STORAGE_STATE_PATH;
    }
    const repoRoot = findWorkspaceRoot(process.cwd());
    return path.join(repoRoot, "packages/ui-code-mode/playwright/temp/storageState.json");
}
function resolveJwt(cliJwt) {
    if (cliJwt) {
        return { jwt: cliJwt, source: "cli" };
    }
    return { jwt: undefined, source: "browser" };
}
async function ensureStorageDirectory(storageStatePath) {
    await fs.promises.mkdir(path.dirname(storageStatePath), { recursive: true });
}
async function invokeJudgeHook(page, args) {
    console.log("⏳ Waiting for app to initialize...");
    await page.waitForTimeout(3000);
    console.log("⏳ Waiting for socket to initialize...");
    const hasFunctionBefore = await page.evaluate(() => typeof window.__judgeEvaluation);
    console.log(`📋 __judgeEvaluation type before wait: ${hasFunctionBefore}`);
    const maxAttempts = 180; // 90 seconds (500ms interval)
    for (let attempts = 0; attempts < maxAttempts; attempts++) {
        const hasFunction = await page.evaluate(() => typeof window.__judgeEvaluation === "function");
        if (hasFunction) {
            console.log("✅ Socket initialized and __judgeEvaluation is available");
            break;
        }
        if (attempts === maxAttempts - 1) {
            throw new Error("Timeout waiting for __judgeEvaluation to be available. " +
                "Make sure the dev server is running and the socket connection succeeded.");
        }
        if (attempts % 10 === 0) {
            console.log(`   Still waiting... (${(attempts / 2).toFixed(1)}s)`);
        }
        await new Promise((resolve) => setTimeout(resolve, 500));
    }
    console.log("🎯 Invoking judge evaluation...");
    const result = await page.evaluate(async (args) => {
        const { promptId, prompt, appId, appUrl, complexity, jwt, storageStatePath, connectWsEndpoint, storageStateData, } = args;
        console.log("HOOK -> handleJudgeEvaluation options", JSON.stringify({
            storageStatePath,
            connectWsEndpoint,
            hasJwt: !!jwt,
            complexity,
        }));
        const judgeEval = window.__judgeEvaluation;
        if (!judgeEval) {
            throw new Error("Judge evaluation handler not available. Is the dev server running?");
        }
        const evaluation = await judgeEval({
            promptId,
            prompt,
            appId,
            appUrl,
            jwt,
            options: {
                complexity,
                storageStatePath,
                connectWsEndpoint,
                storageStateData,
            },
        });
        try {
            return JSON.parse(JSON.stringify(evaluation));
        }
        catch {
            return evaluation;
        }
    }, args);
    return result;
}
async function main() {
    const args = parseArgs();
    const storageStatePath = resolveStorageStatePath(args.storageStatePath);
    const headless = resolveHeadlessFlag(args.headed);
    const { jwt, source: jwtSource } = resolveJwt(args.jwt);
    // Launch browser server first so we can log the WS endpoint
    const playwright = await import("playwright");
    const browserServer = await playwright.chromium.launchServer({
        headless,
    });
    const connectWsEndpoint = browserServer.wsEndpoint();
    console.log("🔍 Judge Evaluation - Browser Hook Mode");
    console.log("=======================================");
    console.log(`Prompt ID: ${args.promptId}`);
    console.log(`App URL: ${args.appUrl}`);
    console.log(`Prompt: ${args.prompt}`);
    console.log(`Complexity: ${args.complexity ?? "medium"}`);
    console.log(`App ID: ${args.appId ?? args.promptId}`);
    console.log(`Storage State: ${storageStatePath}`);
    console.log(`Playwright: ${headless ? "headless" : "headed"}`);
    console.log(`Playwright WS (reuse for judge): ${connectWsEndpoint}`);
    if (args.loginEmail && args.loginPassword) {
        console.log(`Login: using credentials for ${maskEmail(args.loginEmail)}`);
    }
    else {
        console.log("Login: using existing session or storage state");
    }
    console.log(`JWT: ${jwtSource === "cli" ? "provided via --jwt" : "using browser session (TokenProvider)"}`);
    console.log("");
    const browser = await playwright.chromium.connect({
        wsEndpoint: connectWsEndpoint,
    });
    const contextOptions = {};
    if (storageStatePath && fs.existsSync(storageStatePath)) {
        console.log("📦 Loading auth state from storageState.json...");
        contextOptions.storageState = storageStatePath;
    }
    const context = await browser.newContext(contextOptions);
    const page = await context.newPage();
    page.on("console", (msg) => console.log("PAGE LOG:", msg.text()));
    page.on("pageerror", (error) => console.error("PAGE ERROR:", error.message));
    try {
        console.log(`📍 Navigating to ${args.appUrl}...`);
        await page.goto(args.appUrl, {
            waitUntil: "domcontentloaded",
            timeout: 60000,
        });
        const loginUrlUsed = await ensureAuthenticated(page, {
            loginEmail: args.loginEmail,
            loginPassword: args.loginPassword,
            loginUrl: args.loginUrl,
        });
        await ensureOnTargetPage(page, args.appUrl);
        const extraOrigins = [];
        if (loginUrlUsed) {
            const loginOrigin = new URL(loginUrlUsed).origin;
            const loginStorage = await captureOriginStorage(context, loginOrigin);
            if (loginStorage) {
                extraOrigins.push(loginStorage);
            }
            const auth0Cache = await captureAuth0SpaStorage(context, loginOrigin);
            if (auth0Cache &&
                (auth0Cache.localStorage.length || auth0Cache.sessionStorage?.length)) {
                extraOrigins.push(auth0Cache);
            }
        }
        if (storageStatePath) {
            await ensureStorageDirectory(storageStatePath);
            await context.storageState({ path: storageStatePath });
        }
        const storageStateData = await context.storageState();
        if (extraOrigins.length) {
            storageStateData.origins = [
                ...(storageStateData.origins || []),
                ...extraOrigins,
            ];
        }
        const sessionStorageData = await page.evaluate(() => {
            const items = [];
            for (let i = 0; i < sessionStorage.length; i++) {
                const key = sessionStorage.key(i);
                if (key) {
                    items.push({ name: key, value: sessionStorage.getItem(key) || "" });
                }
            }
            return { origin: location.origin, items };
        });
        const result = await invokeJudgeHook(page, {
            promptId: args.promptId,
            prompt: args.prompt,
            appId: args.appId ?? args.promptId,
            appUrl: args.appUrl,
            complexity: (args.complexity ?? "medium"),
            jwt,
            storageStatePath,
            connectWsEndpoint,
            storageStateData,
            sessionStorageData,
            extraOrigins,
        });
        printResult(result);
        process.exit(result.passed ? 0 : 1);
    }
    catch (error) {
        console.error("");
        console.error("❌ Evaluation Failed!");
        console.error("======================");
        console.error(error);
        process.exit(1);
    }
    finally {
        await browser.close().catch(() => { });
        await browserServer.close().catch(() => { });
    }
}
async function captureOriginStorage(context, origin) {
    const page = await context.newPage();
    try {
        await page.goto(origin, { waitUntil: "domcontentloaded", timeout: 15000 });
        const localStorageItems = await page.evaluate(() => {
            const items = [];
            for (let i = 0; i < localStorage.length; i++) {
                const key = localStorage.key(i);
                if (key) {
                    items.push({ name: key, value: localStorage.getItem(key) || "" });
                }
            }
            return items;
        });
        const sessionStorageItems = await page.evaluate(() => {
            const items = [];
            for (let i = 0; i < sessionStorage.length; i++) {
                const key = sessionStorage.key(i);
                if (key) {
                    items.push({ name: key, value: sessionStorage.getItem(key) || "" });
                }
            }
            return items;
        });
        return {
            origin,
            localStorage: localStorageItems,
            sessionStorage: sessionStorageItems,
        };
    }
    catch (error) {
        console.warn(`⚠️ Failed to capture storage for origin ${origin}: ${String(error)}`);
        return null;
    }
    finally {
        await page.close().catch(() => { });
    }
}
async function captureAuth0SpaStorage(context, origin) {
    const page = await context.newPage();
    try {
        await page.goto(origin, { waitUntil: "domcontentloaded", timeout: 15000 });
        const localStorageItems = await page.evaluate(() => {
            const items = [];
            for (let i = 0; i < localStorage.length; i++) {
                const key = localStorage.key(i);
                if (key && key.startsWith("@@auth0spajs@@")) {
                    items.push({ name: key, value: localStorage.getItem(key) || "" });
                }
            }
            return items;
        });
        if (!localStorageItems.length) {
            return null;
        }
        return { origin, localStorage: localStorageItems, sessionStorage: [] };
    }
    catch (error) {
        console.warn(`⚠️ Failed to capture Auth0 SPA storage for origin ${origin}: ${String(error)}`);
        return null;
    }
    finally {
        await page.close().catch(() => { });
    }
}
function maskEmail(email) {
    const [name, domain] = email.split("@");
    if (!domain || !name)
        return email;
    const masked = name.length <= 2
        ? `${name[0] || ""}*`
        : `${name[0]}${"*".repeat(Math.max(1, name.length - 2))}${name.slice(-1)}`;
    return `${masked}@${domain}`;
}
async function ensureAuthenticated(page, opts) {
    let needsLogin = await pageNeedsLogin(page);
    if (!needsLogin) {
        // Wait briefly in case the app redirects us to a login page after initial navigation
        needsLogin = await waitForLoginRedirect(page);
    }
    if (!needsLogin) {
        return;
    }
    if (!opts.loginEmail || !opts.loginPassword) {
        throw new Error("Login required but no credentials provided. Supply --login-email/--login-password or a storage state.");
    }
    const loginUrl = opts.loginUrl ||
        process.env.JUDGE_LOGIN_URL ||
        `${process.env.SUPERBLOCKS_BASE_URL || "http://localhost:3000"}/login`;
    console.log(`🔐 Detected login page, attempting login for ${maskEmail(opts.loginEmail)}...`);
    await fillLoginForm(page, opts.loginEmail, opts.loginPassword, loginUrl);
    return loginUrl;
}
async function ensureOnTargetPage(page, targetUrl) {
    const currentUrl = page.url();
    const hasOauthCode = /[?&]code=/.test(currentUrl);
    if (hasOauthCode || !isSamePath(currentUrl, targetUrl)) {
        console.log("🔁 Navigating to target app URL after auth...");
        await page.goto(targetUrl, {
            waitUntil: "domcontentloaded",
            timeout: 60000,
        });
    }
}
async function pageNeedsLogin(page) {
    const currentUrl = page.url();
    if (/login|auth|signin/i.test(currentUrl)) {
        return true;
    }
    const emailCount = await page
        .locator('input[type="email"], input[name="email"], input#email, input[name="username"], input#username')
        .count()
        .catch(() => 0);
    const passwordCount = await page
        .locator('input[type="password"], input[name="password"], input#password, input#pass')
        .count()
        .catch(() => 0);
    return emailCount > 0 && passwordCount > 0;
}
async function waitForLoginRedirect(page, timeoutMs = 15000) {
    const loginSelector = 'input[type="email"], input[name="email"], input#email, input[name="username"], input#username';
    try {
        await Promise.race([
            page.waitForNavigation({
                waitUntil: "domcontentloaded",
                timeout: timeoutMs,
            }),
            page.waitForSelector(loginSelector, { timeout: timeoutMs }),
        ]);
    }
    catch {
        // ignore timeout here
    }
    return await pageNeedsLogin(page);
}
async function fillLoginForm(page, email, password, loginUrl) {
    for (let attempt = 0; attempt < 3; attempt++) {
        if (!(await pageNeedsLogin(page))) {
            return;
        }
        const emailSelector = 'input[type="email"], input[name="email"], input#email, input[name="username"], input#username';
        const passwordSelector = 'input[type="password"], input[name="password"], input#password, input#pass';
        const emailField = page.locator(emailSelector);
        await emailField.first().waitFor({ timeout: 30000 });
        await emailField.first().fill(email);
        await emailField
            .first()
            .press("Enter")
            .catch(() => { });
        const continueButton = page.locator('button[type="submit"], button:has-text("Continue"), button:has-text("Next")');
        if ((await continueButton.count().catch(() => 0)) > 0) {
            await continueButton
                .first()
                .click()
                .catch(() => { });
        }
        const passwordField = page.locator(passwordSelector);
        await passwordField.first().waitFor({ timeout: 30000 });
        await passwordField.first().fill(password);
        const submitButton = page.locator('button[type="submit"], button:has-text("Continue"), button:has-text("Log in"), button:has-text("Sign in")');
        if ((await submitButton.count().catch(() => 0)) > 0) {
            await submitButton.first().click();
        }
        else {
            await passwordField
                .first()
                .press("Enter")
                .catch(() => { });
        }
        await page
            .waitForNavigation({
            waitUntil: "domcontentloaded",
            timeout: 60000,
        })
            .catch(() => { });
        if (!(await pageNeedsLogin(page))) {
            return;
        }
        await page.goto(loginUrl, {
            waitUntil: "domcontentloaded",
            timeout: 60000,
        });
    }
    if (await pageNeedsLogin(page)) {
        throw new Error("Login attempt failed - still on login page.");
    }
}
function isSamePath(urlA, urlB) {
    try {
        const a = new URL(urlA);
        const b = new URL(urlB);
        return a.origin === b.origin && a.pathname === b.pathname;
    }
    catch {
        return false;
    }
}
function printResult(result) {
    console.log("");
    console.log("✅ Evaluation Complete!");
    console.log("========================");
    console.log(`📊 Overall Score: ${result.overallScore}/100`);
    console.log(`${result.passed ? "✅" : "❌"} Status: ${result.passed ? "PASSED" : "FAILED"}`);
    console.log("");
    console.log("Summary:");
    console.log(result.summary);
    console.log("");
    if (result.criteriaEvaluations?.length) {
        console.log("Criteria Evaluations:");
        for (const criterion of result.criteriaEvaluations) {
            const icon = criterion.passed ? "✅" : "❌";
            console.log(`  ${icon} ${criterion.criterion} - ${criterion.score}/100`);
            if (criterion.reasoning) {
                console.log(`     ${criterion.reasoning}`);
            }
        }
        console.log("");
    }
    if (result.suggestions?.length) {
        console.log("Suggestions:");
        for (const suggestion of result.suggestions) {
            console.log(`  💡 ${suggestion}`);
        }
        console.log("");
    }
}
function findWorkspaceRoot(startDir) {
    let current = startDir;
    let last = "";
    while (current !== last) {
        const packageJsonPath = path.join(current, "package.json");
        if (fs.existsSync(packageJsonPath)) {
            try {
                const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
                if (pkg.workspaces) {
                    return current;
                }
            }
            catch {
                // ignore parse errors and continue
            }
        }
        last = current;
        current = path.dirname(current);
    }
    return startDir;
}
await main();
//# sourceMappingURL=judge-eval-http.js.map