"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildResolveIORunnerQaAuthBootstrapScript = buildResolveIORunnerQaAuthBootstrapScript;
function buildResolveIORunnerQaAuthBootstrapScript(options) {
    if (options === void 0) { options = {}; }
    var defaultUsername = options.defaultUsername || 'admin';
    var defaultPassword = options.defaultPassword || '';
    return [
        '#!/usr/bin/env node',
        "'use strict';",
        '',
        'const fs = require("fs");',
        'const crypto = require("crypto");',
        'const http = require("http");',
        'const https = require("https");',
        'const path = require("path");',
        '',
        'if (isBuildLane()) {',
        '	console.error("ResolveIO support lane guard: auth bootstrap is owned by the QA lane, not the 5.3 build lane.");',
        '	process.exit(86);',
        '}',
        '',
        'const projectRoot = path.resolve(process.argv[2] || process.cwd());',
        'const routeArg = process.argv[3] || process.env.RESOLVEIO_RUNNER_QA_TARGET_ROUTE || process.env.RESOLVEIO_SUPPORT_QA_TARGET_ROUTE || process.env.RESOLVEIO_SUPPORT_QA_LAST_URL || "/";',
        'const targetRoute = normalizeTargetRouteInput(routeArg);',
        'const clientUrl = stripTrailingSlash(process.env.RESOLVEIO_RUNNER_QA_CLIENT_URL || process.env.RESOLVEIO_SUPPORT_QA_CLIENT_URL || `http://localhost:${process.env.RESOLVEIO_SUPPORT_QA_CLIENT_PORT || "4200"}`);',
        'const artifactDir = path.resolve(process.env.RESOLVEIO_RUNNER_QA_ARTIFACT_DIR || process.env.RESOLVEIO_SUPPORT_QA_ARTIFACT_DIR || path.join(projectRoot, "qa-artifacts"));',
        'const serverUrl = stripTrailingSlash(process.env.RESOLVEIO_RUNNER_QA_SERVER_URL || process.env.RESOLVEIO_SUPPORT_QA_SERVER_URL || "http://localhost:8080");',
        'const explicitQaUsername = process.env.RESOLVEIO_RUNNER_QA_AFFECTED_USER_EMAIL || process.env.RESOLVEIO_SUPPORT_QA_AFFECTED_USER_EMAIL || process.env.RESOLVEIO_RUNNER_QA_TICKET_REPORTER_EMAIL || process.env.RESOLVEIO_SUPPORT_QA_TICKET_REPORTER_EMAIL || "";',
        'const envQaUsername = process.env.RESOLVEIO_RUNNER_QA_USERNAME || process.env.RESOLVEIO_SUPPORT_QA_USERNAME || "";',
        'const seededQaUsername = readSeededPreferredQaUsername();',
        "const username = explicitQaUsername || seededQaUsername || envQaUsername || ".concat(JSON.stringify(defaultUsername), ";"),
        "const password = process.env.RESOLVEIO_RUNNER_QA_PASSWORD || process.env.RESOLVEIO_SUPPORT_QA_PASSWORD || ".concat(JSON.stringify(defaultPassword), ";"),
        'const viewportWidth = Number(process.env.RESOLVEIO_RUNNER_QA_VIEWPORT_WIDTH || process.env.RESOLVEIO_SUPPORT_QA_VIEWPORT_WIDTH || 1920);',
        'const viewportHeight = Number(process.env.RESOLVEIO_RUNNER_QA_VIEWPORT_HEIGHT || process.env.RESOLVEIO_SUPPORT_QA_VIEWPORT_HEIGHT || 1080);',
        'const startupTimeoutMs = Math.max(1000, Number(process.env.RESOLVEIO_RUNNER_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS || process.env.RESOLVEIO_SUPPORT_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS || 900) * 1000);',
        'const resultPath = path.join(artifactDir, "auth-bootstrap-result.json");',
        'const storageStatePath = path.join(artifactDir, "auth-bootstrap-storage-state.json");',
        'const readyScreenshotPath = path.join(artifactDir, "auth-bootstrap-ready.jpg");',
        'const failureScreenshotPath = path.join(artifactDir, "auth-bootstrap-failed.jpg");',
        '',
        'function isBuildLane() {',
        '	return /^(build|support_build|support-build)$/i.test(String(process.env.RESOLVEIO_SUPPORT_CODEX_LANE || "").trim());',
        '}',
        '',
        'function stripTrailingSlash(value) {',
        '	return String(value || "").replace(/\\/+$/, "");',
        '}',
        '',
        'function extractRoutePathFromText(value) {',
        '	const raw = String(value || "").trim();',
        '	if (!raw) return "";',
        '	if (/^https?:\\/\\//i.test(raw)) {',
        '		try { return new URL(raw).pathname || "/"; } catch (error) { return ""; }',
        '	}',
        "	const match = raw.match(/(?:^|[\\s\\\"'`(])((?:\\/[a-z0-9][a-z0-9._~!$&'()*+,;=:@%\\/-]*)(?:\\?[^\\s\\\"'`<>)]*)?)/i);",
        '	const candidate = (match && match[1]) || raw;',
        '	return candidate.split(/\\s*->\\s*/)[0].split(/\\s+-\\s+/)[0].trim().replace(/[),.;]+$/g, "");',
        '}',
        '',
        'function normalizeTargetRouteInput(value) {',
        '	const extracted = extractRoutePathFromText(value);',
        '	if (!extracted) return "/";',
        '	return extracted.startsWith("/") ? extracted : `/${extracted}`;',
        '}',
        '',
        'function isLocalhostUrl(value) {',
        '	try {',
        '		const parsed = new URL(value);',
        '		return ["localhost", "127.0.0.1", "::1"].includes(parsed.hostname);',
        '	}',
        '	catch (error) {',
        '		return false;',
        '	}',
        '}',
        '',
        'function readSeededPreferredQaUsername() {',
        '	try {',
        '		const seed = JSON.parse(fs.readFileSync(path.join(artifactDir, "qa-live-data-seed-result.json"), "utf8"));',
        '		const context = seed && seed.selected && seed.selected.qa_user_context;',
        '		const preferred = context && (context.preferred_username || context.preferred_email || context.affected_user_email || context.reporter_email);',
        '		return String(preferred || "").trim();',
        '	} catch (error) {',
        '		return "";',
        '	}',
        '}',
        '',
        'function isExplicitQaIdentity(value) {',
        '	const normalized = String(value || "").trim().toLowerCase();',
        '	if (!normalized || /^(admin|dev@resolveio\\.com)$/i.test(normalized)) return false;',
        '	const explicit = [',
        '		process.env.RESOLVEIO_RUNNER_QA_AFFECTED_USER_EMAIL,',
        '		process.env.RESOLVEIO_SUPPORT_QA_AFFECTED_USER_EMAIL,',
        '		process.env.RESOLVEIO_RUNNER_QA_TICKET_REPORTER_EMAIL,',
        '		process.env.RESOLVEIO_SUPPORT_QA_TICKET_REPORTER_EMAIL,',
        '		readSeededPreferredQaUsername()',
        '	].map((entry) => String(entry || "").trim().toLowerCase()).filter(Boolean);',
        '	return explicit.includes(normalized) || normalized.includes("@");',
        '}',
        '',
        'function resolveLocalMongoUrl() {',
        '	const localQaMongoUrl = process.env.RESOLVEIO_RUNNER_QA_MONGO_URL || process.env.RESOLVEIO_SUPPORT_QA_MONGO_URL || `mongodb://127.0.0.1:${process.env.RESOLVEIO_SUPPORT_QA_MONGO_PORT || "3001"}/resolveio?directConnection=true`;',
        '	if (isLocalhostUrl(serverUrl)) {',
        '		return localQaMongoUrl;',
        '	}',
        '	return process.env.MONGO_URL || localQaMongoUrl;',
        '}',
        '',
        'function requireMongoClient() {',
        '	const candidates = [',
        '		path.join(projectRoot, "server", "node_modules", "mongodb"),',
        '		path.join(projectRoot, "node_modules", "mongodb"),',
        '		path.join(process.cwd(), "server", "node_modules", "mongodb"),',
        '		path.join(process.cwd(), "node_modules", "mongodb"),',
        '		"mongodb"',
        '	];',
        '	for (const candidate of candidates) {',
        '		try { return require(candidate).MongoClient; }',
        '		catch (error) {}',
        '	}',
        '	throw new Error("Unable to require mongodb from project/server node_modules or global resolution");',
        '}',
        '',
        'function buildDefaultQaUserSettings() {',
        '	return {',
        '		table_color: "#3b3ee3",',
        '		table_font_color: "#ffffff",',
        '		secondary_table_color: "#87ceeb",',
        '		secondary_table_font_color: "#000000",',
        '		tertiary_table_color: "#ff4500",',
        '		tertiary_table_font_color: "#000000",',
        '		font_size: 12,',
        '		collapsable_menu: false,',
        '		entries_per_page: "25",',
        '		warning_color: "#ffc107",',
        '		warning_font_color: "#000000",',
        '		warning_hover_color: "#e0a800",',
        '		success_color: "#28a745",',
        '		success_font_color: "#ffffff",',
        '		success_hover_color: "#218838",',
        '		danger_color: "#dc3545",',
        '		danger_font_color: "#ffffff",',
        '		danger_hover_color: "#c82333",',
        '		info_color: "#17a2b8",',
        '		info_font_color: "#ffffff",',
        '		info_hover_color: "#138496",',
        '		primary_color: "#007bff",',
        '		primary_font_color: "#ffffff",',
        '		primary_hover_color: "#0069d9",',
        '		secondary_color: "#868e96",',
        '		secondary_font_color: "#ffffff",',
        '		secondary_hover_color: "#5a6268",',
        '		routing_preference: "",',
        '		opening_route: targetRoute,',
        '		rio_select_search_mode: "exact"',
        '	};',
        '}',
        '',
        'function normalizeQaUserForBrowser(user) {',
        '	const normalized = { ...(user || {}) };',
        '	normalized.other = {',
        '		...(normalized.other || {}),',
        '		yards: Array.isArray(normalized.other && normalized.other.yards) ? normalized.other.yards : [],',
        '		tour_completed: true,',
        '		took_tour: true,',
        '		core_tour_completed: true,',
        '		welcome_tour_completed: true,',
        '		top_navigation_tour_completed: true,',
        '		user_settings_tour_completed: true',
        '	};',
        '	normalized.settings = {',
        '		...buildDefaultQaUserSettings(),',
        '		...(normalized.settings || {}),',
        '		opening_route: targetRoute',
        '	};',
        '	return normalized;',
        '}',
        '',
        'async function ensureLocalQaUser() {',
        '	if (!isLocalhostUrl(serverUrl) || process.env.RESOLVEIO_RUNNER_QA_DISABLE_LOCAL_USER_REPAIR === "true" || process.env.RESOLVEIO_SUPPORT_QA_DISABLE_LOCAL_USER_REPAIR === "true") {',
        '		return false;',
        '	}',
        '	const MongoClient = requireMongoClient();',
        '	const client = new MongoClient(resolveLocalMongoUrl());',
        '	await client.connect();',
        '	try {',
        '		const db = client.db();',
        '		const users = db.collection("users");',
        '		const now = new Date();',
        '		const salt = crypto.randomBytes(32).toString("hex");',
        '		const hash = crypto.pbkdf2Sync(password, salt, 25000, 512, "sha256").toString("hex");',
        '		const existing = await users.findOne({ $or: [{ username }, { email: username }] });',
        '		const defaultSettings = buildDefaultQaUserSettings();',
        '		const defaultOther = { yards: [], date_picker_day_start: "S", tour_completed: true, took_tour: true, core_tour_completed: true, welcome_tour_completed: true, top_navigation_tour_completed: true, user_settings_tour_completed: true, tour_completed_at: now.toISOString() };',
        '		if (existing && existing._id && isExplicitQaIdentity(username)) {',
        '			await users.updateOne({ _id: existing._id }, {',
        '				$set: {',
        '					active: true,',
        '					other: { ...(existing.other || {}), ...defaultOther, yards: Array.isArray(existing.other && existing.other.yards) ? existing.other.yards : [] },',
        '					settings: { ...defaultSettings, ...(existing.settings || {}), opening_route: targetRoute, collapsable_menu: false },',
        '					hash,',
        '					salt,',
        '					attempts: 0,',
        '					readonly: false,',
        '					updatedAt: now',
        '				}',
        '			});',
        '			return true;',
        '		}',
        '		const baseUser = {',
        '			__v: 0,',
        '			username,',
        '			email: username.includes("@") ? username : "dev@resolveio.com",',
        '			fullname: "QA Admin",',
        '			roles: { super_admin: true, approvals: [], groups: [], notifications: [], miscs: [] },',
        '			active: true,',
        '			other: defaultOther,',
        '			settings: defaultSettings,',
        '			phonenumber: "",',
        '			readonly: false,',
        '			is_customer: false,',
        '			hash,',
        '			salt,',
        '			attempts: 0,',
        '			services: {},',
        '			updatedAt: now',
        '		};',
        '		const qaUserFilter = {',
        '			$or: [',
        '				{ username },',
        '				{ email: username },',
        '				{ username: "admin" },',
        '				{ email: "admin" },',
        '				{ email: "dev@resolveio.com" }',
        '			]',
        '		};',
        '		await users.updateMany(qaUserFilter, {',
        '			$set: {',
        '				active: true,',
        '				roles: baseUser.roles,',
        '				other: defaultOther,',
        '				settings: defaultSettings,',
        '				hash,',
        '				salt,',
        '				attempts: 0,',
        '				readonly: false,',
        '				is_customer: false,',
        '				updatedAt: now',
        '			}',
        '		});',
        '		if (existing && existing._id) {',
        '			await users.updateOne({ _id: existing._id }, { $set: baseUser });',
        '		}',
        '		else {',
        '			await users.insertOne({ _id: crypto.randomBytes(12).toString("hex"), createdAt: now, ...baseUser });',
        '		}',
        '		const repaired = await users.findOne(qaUserFilter, { projection: { _id: 1 } });',
        '		if (repaired && repaired._id) {',
        '			await users.updateOne({ _id: repaired._id }, {',
        '				$set: {',
        '					active: true,',
        '					roles: baseUser.roles,',
        '					other: defaultOther,',
        '					settings: defaultSettings,',
        '					hash,',
        '					salt,',
        '					attempts: 0,',
        '					readonly: false,',
        '					is_customer: false,',
        '					updatedAt: now',
        '				}',
        '			});',
        '		}',
        '		return true;',
        '	}',
        '	finally {',
        '		await client.close().catch(() => undefined);',
        '	}',
        '}',
        '',
        'async function refreshAuthAndSeed(page) {',
        '	const auth = await login();',
        '	await seedAuth(page, auth);',
        '	return auth;',
        '}',
        '',
        'function writeResult(payload) {',
        '	fs.mkdirSync(artifactDir, { recursive: true });',
        '	fs.writeFileSync(resultPath, JSON.stringify(payload, null, 2));',
        '}',
        '',
        'function writeAuthStorageState(auth) {',
        '	const normalizedUser = normalizeQaUserForBrowser(auth && auth.user);',
        '	const bootstrappedAt = new Date().toISOString();',
        '	const localStorage = {',
        '		refreshToken: auth && auth.refreshToken,',
        '		accessToken: auth && auth.accessToken,',
        '		user: JSON.stringify(normalizedUser),',
        '		lastURL: targetRoute,',
        '		"resolveio.runnerQaAuthBootstrappedAt": bootstrappedAt',
        '	};',
        '	const localStorageEntries = Object.entries(localStorage).map(([name, value]) => ({ name, key: name, value: String(value == null ? "" : value) }));',
        '	const payload = {',
        '		status: "pass",',
        '		clientUrl,',
        '		serverUrl,',
        '		targetRoute,',
        '		createdAt: bootstrappedAt,',
        '		refreshToken: auth && auth.refreshToken,',
        '		accessToken: auth && auth.accessToken,',
        '		user: normalizedUser,',
        '		localStorage,',
        '		localStorageEntries,',
        '		cookies: [],',
        '		origins: [{',
        '			origin: clientUrl,',
        '			localStorage: localStorageEntries.map(({ name, value }) => ({ name, value }))',
        '		}]',
        '	};',
        '	fs.mkdirSync(artifactDir, { recursive: true });',
        '	fs.writeFileSync(storageStatePath, JSON.stringify(payload, null, 2));',
        '	return payload;',
        '}',
        '',
        'function requestJson(url, payload) {',
        '	return new Promise((resolve, reject) => {',
        '		const body = JSON.stringify(payload || {});',
        '		const parsed = new URL(url);',
        '		const mod = parsed.protocol === "https:" ? https : http;',
        '		const req = mod.request(parsed, {',
        '			method: "POST",',
        '			timeout: 20000,',
        '			headers: {',
        '				"content-type": "application/json",',
        '				"content-length": Buffer.byteLength(body),',
        '				"origin": clientUrl',
        '			}',
        '		}, (res) => {',
        '			let raw = "";',
        '			res.setEncoding("utf8");',
        '			res.on("data", (chunk) => { raw += chunk; });',
        '			res.on("end", () => {',
        '				let json = null;',
        '				try { json = raw ? JSON.parse(raw) : {}; }',
        '				catch (error) {',
        '					reject(new Error(`${url} returned non-JSON HTTP ${res.statusCode}: ${raw.slice(0, 300)}`));',
        '					return;',
        '				}',
        '				if (!res.statusCode || res.statusCode >= 400) {',
        '					reject(new Error(`${url} returned HTTP ${res.statusCode}: ${JSON.stringify(json).slice(0, 500)}`));',
        '					return;',
        '				}',
        '				resolve(json);',
        '			});',
        '		});',
        '		req.on("timeout", () => req.destroy(new Error(`${url} timed out`)));',
        '		req.on("error", reject);',
        '		req.write(body);',
        '		req.end();',
        '	});',
        '}',
        '',
        'function requestReady(url) {',
        '	return new Promise((resolve) => {',
        '		try {',
        '			const parsed = new URL(url);',
        '			const mod = parsed.protocol === "https:" ? https : http;',
        '			const req = mod.get(parsed, { timeout: 2500 }, (res) => {',
        '				res.resume();',
        '				resolve(Boolean(res.statusCode && res.statusCode < 500));',
        '			});',
        '			req.on("timeout", () => req.destroy(new Error("timeout")));',
        '			req.on("error", () => resolve(false));',
        '		}',
        '		catch (error) {',
        '			resolve(false);',
        '		}',
        '	});',
        '}',
        '',
        'async function waitForHttpReady(url, label) {',
        '	const deadline = Date.now() + startupTimeoutMs;',
        '	while (Date.now() < deadline) {',
        '		if (await requestReady(url)) {',
        '			return;',
        '		}',
        '		await delay(3000);',
        '	}',
        '	throw new Error(`${label} did not become ready at ${url} within ${Math.round(startupTimeoutMs / 1000)}s`);',
        '}',
        '',
        'function requirePuppeteer() {',
        '	const candidates = [',
        '		path.join(projectRoot, "server", "node_modules", "puppeteer", "lib", "cjs", "puppeteer", "puppeteer.js"),',
        '		path.join(projectRoot, "node_modules", "puppeteer", "lib", "cjs", "puppeteer", "puppeteer.js"),',
        '		path.join(process.cwd(), "server", "node_modules", "puppeteer", "lib", "cjs", "puppeteer", "puppeteer.js"),',
        '		path.join(process.cwd(), "node_modules", "puppeteer", "lib", "cjs", "puppeteer", "puppeteer.js"),',
        '		"puppeteer"',
        '	];',
        '	for (const candidate of candidates) {',
        '		try { return require(candidate); }',
        '		catch (error) {}',
        '	}',
        '	throw new Error("Unable to require puppeteer from project/server node_modules or global resolution");',
        '}',
        '',
        'async function login() {',
        '	if (!password) {',
        '		throw new Error("QA password is empty; source .resolveio-support-tools/env.sh or set RESOLVEIO_RUNNER_QA_PASSWORD before auth bootstrap");',
        '	}',
        '	await waitForHttpReady(serverUrl, "QA server");',
        '	await ensureLocalQaUser();',
        '	let loginJson = await requestJson(`${serverUrl}/login`, { username, password });',
        '	if ((loginJson && loginJson.error) && /Invalid Username And Password|Too Many Attempts/i.test(String(loginJson.result || ""))) {',
        '		const repaired = await ensureLocalQaUser();',
        '		if (repaired) {',
        '			loginJson = await requestJson(`${serverUrl}/login`, { username, password });',
        '		}',
        '	}',
        '	const refreshToken = loginJson && loginJson.result && loginJson.result.token;',
        '	if (loginJson.error || !refreshToken) {',
        '		throw new Error(`Login failed: ${JSON.stringify(loginJson).slice(0, 800)}`);',
        '	}',
        '	const accessJson = await requestJson(`${serverUrl}/accessToken`, { refreshToken });',
        '	const accessToken = accessJson && accessJson.result && accessJson.result.token;',
        '	const user = accessJson && accessJson.result && accessJson.result.user;',
        '	if (accessJson.error || !accessToken || !user) {',
        '		throw new Error(`Access token failed: ${JSON.stringify(accessJson).slice(0, 800)}`);',
        '	}',
        '	return { refreshToken, accessToken, user: normalizeQaUserForBrowser(user) };',
        '}',
        '',
        'async function launchBrowser(puppeteer) {',
        '	const browserUrl = process.env.RESOLVEIO_RUNNER_QA_BROWSER_URL || process.env.RESOLVEIO_SUPPORT_QA_BROWSER_URL || "";',
        '	if (browserUrl) {',
        '		try {',
        '			const debugUrl = `${browserUrl.replace(/\\/$/, "")}/json/version`;',
        '			if (!(await requestReady(debugUrl))) {',
        '				throw new Error(`QA browser debug endpoint did not answer at ${debugUrl}`);',
        '			}',
        '			return puppeteer.connect({ browserURL: browserUrl, protocolTimeout: 30000, defaultViewport: null });',
        '		}',
        '		catch (error) {',
        '			console.warn(`QA browser debug endpoint unavailable at ${browserUrl}; launching scoped browser instead: ${error.message || error}`);',
        '		}',
        '	}',
        '	const launchOptions = {',
        '		headless: true,',
        '		defaultViewport: { width: viewportWidth, height: viewportHeight },',
        '		args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", `--window-size=${viewportWidth},${viewportHeight}`]',
        '	};',
        '	if (process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN) {',
        '		launchOptions.executablePath = process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN;',
        '	}',
        '	return puppeteer.launch(launchOptions);',
        '}',
        '',
        'async function resetBrowserState(page) {',
        '	await waitForHttpReady(clientUrl, "QA client");',
        '	await page.goto(clientUrl, { waitUntil: "domcontentloaded", timeout: 45000 });',
        '	await page.evaluate(async () => {',
        '		try {',
        '			const registrations = await navigator.serviceWorker.getRegistrations();',
        '			await Promise.all(registrations.map((registration) => registration.unregister().catch(() => false)));',
        '		} catch (error) {}',
        '		try {',
        '			if (window.caches && window.caches.keys) {',
        '				const keys = await window.caches.keys();',
        '				await Promise.all(keys.map((key) => window.caches.delete(key).catch(() => false)));',
        '			}',
        '		} catch (error) {}',
        '		try {',
        '			if (window.indexedDB && window.indexedDB.databases) {',
        '				const databases = await window.indexedDB.databases();',
        '				await Promise.all(databases.filter((database) => database && database.name).map((database) => new Promise((resolve) => {',
        '					const request = window.indexedDB.deleteDatabase(database.name);',
        '					request.onsuccess = () => resolve(true);',
        '					request.onerror = () => resolve(false);',
        '					request.onblocked = () => resolve(false);',
        '				})));',
        '			}',
        '		} catch (error) {}',
        '		localStorage.clear();',
        '		sessionStorage.clear();',
        '	});',
        '}',
        '',
        'async function seedAuth(page, auth) {',
        '	auth.user = normalizeQaUserForBrowser(auth.user);',
        '	writeAuthStorageState(auth);',
        '	await page.evaluate((payload) => {',
        '		localStorage.setItem("refreshToken", payload.refreshToken);',
        '		localStorage.setItem("accessToken", payload.accessToken);',
        '		localStorage.setItem("user", JSON.stringify(payload.user));',
        '		localStorage.setItem("lastURL", payload.lastURL);',
        '		localStorage.setItem("resolveio.runnerQaAuthBootstrappedAt", payload.bootstrappedAt);',
        '	}, { ...auth, lastURL: targetRoute, bootstrappedAt: new Date().toISOString() });',
        '}',
        '',
        'async function patchBrowserQaUser(page) {',
        '	await page.evaluate((nextRoute) => {',
        '		let user = {};',
        '		try { user = JSON.parse(localStorage.getItem("user") || "{}"); } catch (error) { user = {}; }',
        '		const settings = {',
        '			table_color: "#3b3ee3",',
        '			table_font_color: "#ffffff",',
        '			secondary_table_color: "#87ceeb",',
        '			secondary_table_font_color: "#000000",',
        '			tertiary_table_color: "#ff4500",',
        '			tertiary_table_font_color: "#000000",',
        '			font_size: 12,',
        '			collapsable_menu: false,',
        '			entries_per_page: "25",',
        '			warning_color: "#ffc107",',
        '			warning_font_color: "#000000",',
        '			warning_hover_color: "#e0a800",',
        '			success_color: "#28a745",',
        '			success_font_color: "#ffffff",',
        '			success_hover_color: "#218838",',
        '			danger_color: "#dc3545",',
        '			danger_font_color: "#ffffff",',
        '			danger_hover_color: "#c82333",',
        '			info_color: "#17a2b8",',
        '			info_font_color: "#ffffff",',
        '			info_hover_color: "#138496",',
        '			primary_color: "#007bff",',
        '			primary_font_color: "#ffffff",',
        '			primary_hover_color: "#0069d9",',
        '			secondary_color: "#868e96",',
        '			secondary_font_color: "#ffffff",',
        '			secondary_hover_color: "#5a6268",',
        '			routing_preference: "",',
        '			opening_route: nextRoute,',
        '			rio_select_search_mode: "exact"',
        '		};',
        '		user = {',
        '			...user,',
        '			other: {',
        '				...(user.other || {}),',
        '				yards: Array.isArray(user.other && user.other.yards) ? user.other.yards : [],',
        '				tour_completed: true,',
        '				took_tour: true,',
        '				core_tour_completed: true,',
        '				welcome_tour_completed: true,',
        '				top_navigation_tour_completed: true,',
        '				user_settings_tour_completed: true',
        '			},',
        '			settings: { ...settings, ...(user.settings || {}), opening_route: nextRoute }',
        '		};',
        '		localStorage.setItem("user", JSON.stringify(user));',
        '		localStorage.setItem("lastURL", nextRoute);',
        '		localStorage.setItem("resolveio.runnerQaPostLoginGateRepairedAt", new Date().toISOString());',
        '	});',
        '}',
        '',
        'async function dismissVisibleTourOrSetupGate(page) {',
        '	return page.evaluate(() => {',
        '		const controls = Array.from(document.querySelectorAll("button, a, [role=\'button\']"));',
        '		const control = controls.find((candidate) => {',
        '			const text = (candidate.textContent || "").replace(/\\s+/g, " ").trim();',
        '			return /^(skip|finish|done|close)$/i.test(text);',
        '		});',
        '		if (control && typeof control.click === "function") {',
        '			control.click();',
        '			return true;',
        '		}',
        '		return false;',
        '	});',
        '}',
        '',
        'function isPostLoginSetupOrTourGate(currentRoute, summary) {',
        '	const text = String(summary && summary.bodyTextSnippet || "");',
        '	return currentRoute === "/user-settings/settings" || /Top Navigation Step \\d+ of \\d+|\\bSkip\\b|User Settings/i.test(text);',
        '}',
        '',
        'async function repairPostLoginSetupOrTourGate(page, expectedRoute) {',
        '	if (process.env.RESOLVEIO_RUNNER_QA_DISABLE_POST_LOGIN_ROUTE_REPAIR === "true" || process.env.RESOLVEIO_SUPPORT_QA_DISABLE_POST_LOGIN_ROUTE_REPAIR === "true") {',
        '		return false;',
        '	}',
        '	const current = normalizeRoutePath(await page.evaluate(() => location.href));',
        '	const summary = await pageSummary(page).catch(() => ({}));',
        '	if (!isPostLoginSetupOrTourGate(current, summary)) {',
        '		return false;',
        '	}',
        '	await ensureLocalQaUser().catch(() => false);',
        '	await refreshAuthAndSeed(page).catch(() => undefined);',
        '	await dismissVisibleTourOrSetupGate(page).catch(() => false);',
        '	await patchBrowserQaUser(page);',
        '	await page.goto(`${clientUrl}${expectedRoute}`, { waitUntil: "domcontentloaded", timeout: 60000 });',
        '	await delay(1500);',
        '	await dismissVisibleTourOrSetupGate(page).catch(() => false);',
        '	await patchBrowserQaUser(page);',
        '	await delay(500);',
        '	const repairedCurrent = canonicalizeRoutePath(await page.evaluate(() => location.href));',
        '	return routesMatch(repairedCurrent, expectedRoute);',
        '}',
        '',
        'function delay(ms) {',
        '	return new Promise((resolve) => setTimeout(resolve, ms));',
        '}',
        '',
        'function normalizeRoutePath(value) {',
        '	try {',
        '			const parsed = new URL(value, clientUrl);',
        '			let pathname = parsed.pathname || "/";',
        '			pathname = pathname.replace(/\\/+$/, "") || "/";',
        '			return pathname;',
        '	}',
        '	catch (error) {',
        '			return String(value || "/").split("?")[0].replace(/\\/+$/, "") || "/";',
        '	}',
        '}',
        '',
        'function canonicalizeRoutePath(value) {',
        '	const route = normalizeRoutePath(value);',
        '	const aliases = {',
        '		"/billing": "/dashboard/billing",',
        '		"/invoice": "/dashboard/billing",',
        '		"/invoices": "/dashboard/billing",',
        '		"/customer": "/manage/customer",',
        '		"/customers": "/manage/customer",',
        '		"/pricing": "/manage/pricing",',
        '		"/items": "/manage/item",',
        '		"/invoice-items": "/manage/item",',
        '		"/inventory": "/manage/inventory"',
        '	};',
        '	return aliases[route] || route;',
        '}',
        '',
        'function routesMatch(currentRoute, expectedRoute) {',
        '	return canonicalizeRoutePath(currentRoute) === canonicalizeRoutePath(expectedRoute);',
        '}',
        '',
        'function isGenericAuthTargetRoute(routePath) {',
        '	return ["/", "/screen", "/home"].includes(canonicalizeRoutePath(routePath));',
        '}',
        '',
        'function isAuthenticatedNonSetupRoute(summary) {',
        '	const current = normalizeRoutePath(summary && summary.url || "");',
        '	return !!summary',
        '		&& !!summary.hasRefreshToken',
        '		&& !!summary.hasAccessToken',
        '		&& !!summary.hasUser',
        '		&& !summary.hasLoginText',
        '		&& !summary.hasOfflineModeText',
        '		&& current !== "/user-settings/settings";',
        '}',
        '',
        'async function assertTargetRoute(page) {',
        '	const expected = canonicalizeRoutePath(targetRoute);',
        '	if (expected === "/") {',
        '		return;',
        '	}',
        '	const current = canonicalizeRoutePath(await page.evaluate(() => location.href));',
        '	if (!routesMatch(current, expected)) {',
        '		const repaired = await repairPostLoginSetupOrTourGate(page, expected);',
        '		if (repaired) {',
        '			return;',
        '		}',
        '		const summary = await pageSummary(page).catch(() => ({}));',
        '		if (isGenericAuthTargetRoute(expected) && isAuthenticatedNonSetupRoute(summary)) {',
        '			return;',
        '		}',
        '		throw new Error(`QA auth bootstrap reached ${current}, not requested target route ${expected}. This is a route blocker; do not continue browser QA until the runner/app can reach the requested screen. Page summary: ${JSON.stringify(summary).slice(0, 1200)}`);',
        '	}',
        '}',
        '',
        'async function waitForStableTargetRoute(page) {',
        '	const expected = canonicalizeRoutePath(targetRoute);',
        '	if (isGenericAuthTargetRoute(expected)) {',
        '		return;',
        '	}',
        '	const deadline = Date.now() + Number(process.env.RESOLVEIO_RUNNER_QA_ROUTE_STABILITY_TIMEOUT_MS || process.env.RESOLVEIO_SUPPORT_QA_ROUTE_STABILITY_TIMEOUT_MS || 7000);',
        '	let stableSince = 0;',
        '	let lastCurrent = "";',
        '	while (Date.now() < deadline) {',
        '		const current = canonicalizeRoutePath(await page.evaluate(() => location.href));',
        '		lastCurrent = current;',
        '		if (routesMatch(current, expected)) {',
        '			stableSince = stableSince || Date.now();',
        '			if (Date.now() - stableSince >= Number(process.env.RESOLVEIO_RUNNER_QA_ROUTE_STABLE_MS || process.env.RESOLVEIO_SUPPORT_QA_ROUTE_STABLE_MS || 2500)) {',
        '				return;',
        '			}',
        '		}',
        '		else {',
        '			stableSince = 0;',
        '		}',
        '		await delay(250);',
        '	}',
        '	const summary = await pageSummary(page).catch(() => ({}));',
        '	throw new Error(`QA auth bootstrap route was not stable on requested target ${expected}; last route was ${lastCurrent || "unknown"}. This is a route blocker; do not continue browser QA until the runner/app can remain on the requested screen. Page summary: ${JSON.stringify(summary).slice(0, 1200)}`);',
        '}',
        '',
        'async function dismissNavigationOverlays(page) {',
        '	try {',
        '		await page.keyboard.press("Escape");',
        '	} catch (error) {}',
        '	try {',
        '		await page.mouse.move(16, Math.max(120, viewportHeight - 24));',
        '	} catch (error) {}',
        '	try {',
        '		await page.evaluate(() => {',
        '			if (document.activeElement && typeof document.activeElement.blur === "function") document.activeElement.blur();',
        '			document.body && document.body.dispatchEvent(new MouseEvent("mousemove", { bubbles: true, clientX: 16, clientY: Math.max(120, window.innerHeight - 24) }));',
        '		});',
        '	} catch (error) {}',
        '	await delay(350);',
        '}',
        '',
        'async function assertSummaryOnTargetRoute(summary) {',
        '	const expected = canonicalizeRoutePath(targetRoute);',
        '	if (expected === "/") {',
        '		return;',
        '	}',
        '	const current = canonicalizeRoutePath(summary && summary.url || "");',
        '	if (!routesMatch(current, expected)) {',
        '		if (isGenericAuthTargetRoute(expected) && isAuthenticatedNonSetupRoute(summary)) {',
        '			return;',
        '		}',
        '		throw new Error(`QA auth bootstrap final summary is on ${current}, not requested target route ${expected}. This is a route blocker; refusing to write a false pass. Page summary: ${JSON.stringify(summary).slice(0, 1200)}`);',
        '	}',
        '}',
        '',
        'async function logoutExistingBrowserSession(page) {',
        '	try {',
        '		await page.goto(clientUrl, { waitUntil: "domcontentloaded", timeout: 45000 });',
        '		await delay(500);',
        '		await page.evaluate(() => {',
        '			const controls = Array.from(document.querySelectorAll("button, a, [role=\'button\']"));',
        '			const logoutControl = controls.find((control) => /(^|\\s)logout(\\s|$)/i.test((control.textContent || "").trim()));',
        '			if (logoutControl && typeof logoutControl.click === "function") {',
        '				logoutControl.click();',
        '				return true;',
        '			}',
        '			return false;',
        '		});',
        '		await delay(1000);',
        '	}',
        '	catch (error) {}',
        '}',
        '',
        'async function waitForAuthenticatedApp(page) {',
        '	const url = `${clientUrl}${targetRoute}`;',
        '	await page.goto(url, { waitUntil: "domcontentloaded", timeout: 60000 });',
        '	await page.waitForFunction(() => {',
        '		const text = (document.body && document.body.innerText || "").replace(/\\s+/g, " ").trim();',
        '		const hasTokens = !!localStorage.getItem("refreshToken") && !!localStorage.getItem("accessToken") && !!localStorage.getItem("user");',
        '		const hasLogin = /Employee\\/Customer Login|Employee Sign In|Customer Access|Unable to sign in/i.test(text);',
        '		const hasOffline = text.includes("*** OFFLINE MODE ***");',
        '		return hasTokens && !hasLogin && !hasOffline && text.length > 40;',
        '	}, { timeout: Number(process.env.RESOLVEIO_RUNNER_QA_AUTH_TIMEOUT_MS || process.env.RESOLVEIO_SUPPORT_QA_AUTH_TIMEOUT_MS || 60000) });',
        '	await delay(1000);',
        '	await assertTargetRoute(page);',
        '	await waitForStableTargetRoute(page);',
        '	await dismissNavigationOverlays(page);',
        '}',
        '',
        'async function pageSummary(page) {',
        '	return page.evaluate(() => {',
        '		const bodyText = (document.body && document.body.innerText || "").replace(/\\s+/g, " ").trim();',
        '		return {',
        '			url: location.href,',
        '			title: document.title,',
        '			hasAngularDebug: !!window.ng,',
        '			hasRefreshToken: !!localStorage.getItem("refreshToken"),',
        '			hasAccessToken: !!localStorage.getItem("accessToken"),',
        '			hasUser: !!localStorage.getItem("user"),',
        '			hasOfflineModeText: bodyText.includes("*** OFFLINE MODE ***"),',
        '			hasLoginText: /Employee\\/Customer Login|Employee Sign In|Customer Access|Unable to sign in/i.test(bodyText),',
        '			bodyTextSnippet: bodyText.slice(0, 800),',
        '			localStorageKeys: Object.keys(localStorage).sort()',
        '		};',
        '	});',
        '}',
        '',
        '(async () => {',
        '	fs.mkdirSync(artifactDir, { recursive: true });',
        '	await waitForHttpReady(clientUrl, "QA client");',
        '	await waitForHttpReady(serverUrl, "QA server");',
        '	const puppeteer = requirePuppeteer();',
        '	const browser = await launchBrowser(puppeteer);',
        '	let page;',
        '	try {',
        '		page = await browser.newPage();',
        '		await page.setViewport({ width: viewportWidth, height: viewportHeight });',
        '		page.on("console", (msg) => {',
        '			const text = msg.text();',
        '			if (["error", "warning"].includes(msg.type()) || /error/i.test(text)) {',
        '				console.log("[browser console]", msg.type(), text);',
        '			}',
        '		});',
        '		page.on("pageerror", (error) => console.log("[pageerror]", error.message));',
        '		await logoutExistingBrowserSession(page);',
        '		await resetBrowserState(page);',
        '		const auth = await login();',
        '		await seedAuth(page, auth);',
        '		await waitForAuthenticatedApp(page);',
        '		const finalPage = await pageSummary(page);',
        '		await assertSummaryOnTargetRoute(finalPage);',
        '		await page.screenshot({ path: readyScreenshotPath, type: "jpeg", quality: 82, fullPage: false });',
        '		const summary = {',
        '			status: "pass",',
        '			clientUrl,',
        '			serverUrl,',
        '			targetRoute,',
        '			screenshot: readyScreenshotPath,',
        '			storageState: storageStatePath,',
        '			user: { _id: auth.user && auth.user._id, username: auth.user && auth.user.username, fullname: auth.user && auth.user.fullname },',
        '			page: finalPage',
        '		};',
        '		writeResult(summary);',
        '		console.log(JSON.stringify(summary, null, 2));',
        '	}',
        '	catch (error) {',
        '		let summary = { status: "fail", clientUrl, serverUrl, targetRoute, screenshot: failureScreenshotPath, error: error && error.stack || String(error) };',
        '		try {',
        '			if (page) {',
        '				await page.screenshot({ path: failureScreenshotPath, type: "jpeg", quality: 82, fullPage: false });',
        '				summary.page = await pageSummary(page);',
        '			}',
        '		} catch (screenshotError) {',
        '			summary.screenshotError = screenshotError && screenshotError.stack || String(screenshotError);',
        '		}',
        '		writeResult(summary);',
        '		console.error(JSON.stringify(summary, null, 2));',
        '		process.exitCode = 1;',
        '	}',
        '	finally {',
        '		if (browser && typeof browser.close === "function") {',
        '			await browser.close().catch(() => undefined);',
        '		}',
        '		process.exit(process.exitCode || 0);',
        '	}',
        '})();',
        ''
    ].join('\n');
}

//# sourceMappingURL=ai-runner-qa-auth.js.map
