"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.pages = void 0;
exports.allocatePageId = allocatePageId;
exports.ensureBrowser = ensureBrowser;
exports.resolvePage = resolvePage;
exports.buildHandle = buildHandle;
exports.stopPlaywrightServer = stopPlaywrightServer;
const playwright_1 = require("playwright");
const logger_1 = require("../../../lib/logger");
const types_1 = require("./types");
// ── Browser lifecycle ────────────────────────────────────────────────────────
// Shared browser instance across tool calls within a single server lifetime.
// Lazy-launched on first use, cleaned up via stopPlaywrightServer().
let browser = null;
exports.pages = new Map();
let pageCounter = 0;
/** Remote browsers connected via CDP — cached to avoid reconnecting per call. */
const remoteBrowsers = new Map();
/** Allocate a unique page ID (shared across both playwright servers). */
function allocatePageId() {
    return `page_${++pageCounter}`;
}
async function ensureBrowser() {
    if (!browser || !browser.isConnected()) {
        browser = await playwright_1.chromium.launch({ headless: true });
        logger_1.loggerRegistry.info('[lt-mcp:playwright] browser launched');
    }
    return browser;
}
/**
 * Resolve a page from either a local `page_id`, a `_handle`, or "most recent".
 * Handles cross-container reconnection via CDP when needed.
 */
async function resolvePage(args) {
    // 1. Try local pages Map (fast path — same container)
    const localId = args._handle?.page_id || args.page_id;
    if (localId) {
        const local = exports.pages.get(localId);
        if (local)
            return { page: local, pageId: localId };
    }
    // 2. Try CDP reconnection via handle (cross-container)
    if (args._handle?.cdp_endpoint) {
        const cdp = args._handle.cdp_endpoint;
        let remoteBrowser = remoteBrowsers.get(cdp);
        if (!remoteBrowser || !remoteBrowser.isConnected()) {
            try {
                remoteBrowser = await playwright_1.chromium.connectOverCDP(cdp);
                remoteBrowsers.set(cdp, remoteBrowser);
                logger_1.loggerRegistry.info(`[lt-mcp:playwright] connected to remote browser: ${cdp}`);
            }
            catch (err) {
                throw Object.assign(new Error(`Cannot reach browser at ${cdp}`), { code: types_1.SESSION_UNREACHABLE, cdp_endpoint: cdp });
            }
        }
        // Find the page — for now, use the first page in the default context
        const contexts = remoteBrowser.contexts();
        const ctx = contexts[0] || await remoteBrowser.newContext();
        const remotePages = ctx.pages();
        if (remotePages.length > 0) {
            const page = remotePages[remotePages.length - 1];
            const id = args._handle.page_id || `remote_${++pageCounter}`;
            exports.pages.set(id, page);
            return { page, pageId: id };
        }
        throw Object.assign(new Error(`No pages found on remote browser at ${cdp}`), { code: types_1.SESSION_NOT_FOUND, cdp_endpoint: cdp });
    }
    // 3. If a specific page_id was requested but not found, throw structured error
    if (localId) {
        throw Object.assign(new Error(`Page ${localId} not found`), { code: types_1.SESSION_NOT_FOUND, page_id: localId });
    }
    // 4. Fall back to most recent local page
    const entries = Array.from(exports.pages.entries());
    if (entries.length === 0) {
        throw Object.assign(new Error('No pages open. Navigate to a URL first, or pass a _handle.'), { code: types_1.SESSION_NOT_FOUND });
    }
    return { page: entries[entries.length - 1][1], pageId: entries[entries.length - 1][0] };
}
/**
 * Build a _handle for a page. Includes CDP endpoint when available
 * (for cross-container access in distributed deployments).
 */
function buildHandle(pageId) {
    const handle = {
        type: 'playwright_page',
        page_id: pageId,
    };
    // Include CDP endpoint if browser exposes one (e.g., launchServer or remote)
    if (browser && typeof browser.wsEndpoint === 'function') {
        try {
            handle.cdp_endpoint = browser.wsEndpoint();
        }
        catch {
            // Not available — single-process mode, local pages Map suffices
        }
    }
    return handle;
}
/**
 * Shut down the shared browser and release all pages.
 */
async function stopPlaywrightServer() {
    for (const [id, page] of exports.pages) {
        try {
            await page.close();
        }
        catch { /* ignore */ }
        exports.pages.delete(id);
    }
    // Close remote browsers
    for (const [endpoint, rb] of remoteBrowsers) {
        try {
            await rb.close();
        }
        catch { /* ignore */ }
        remoteBrowsers.delete(endpoint);
    }
    if (browser) {
        await browser.close();
        browser = null;
    }
    pageCounter = 0;
    logger_1.loggerRegistry.info('[lt-mcp:playwright] browser closed');
}
