import { createRequire } from 'node:module';
const INTERACTIVE_SELECTOR = [
    'a',
    'button',
    'input',
    'textarea',
    'select',
    'summary',
    '[role]',
    '[tabindex]',
].join(', ');
const nodeRequire = createRequire(import.meta.url);
/**
 * Playwright-backed browser driver adapter for the validation spike.
 */
export class PlaywrightBrowserDriverFactory {
    async launch(options) {
        const context = await loadPlaywrightChromium().launchPersistentContext(options.profile.userDataDir, {
            acceptDownloads: false,
            channel: options.profile.channel,
            headless: options.profile.headless ?? true,
            viewport: { width: 1280, height: 900 },
        });
        const page = context.pages()[0] ?? await context.newPage();
        return new PlaywrightBrowserDriver(context, page);
    }
}
function loadPlaywrightChromium() {
    try {
        return nodeRequire('playwright').chromium;
    }
    catch (error) {
        throw new Error('Browser automation requires the optional playwright package. Install playwright or pass a custom driverFactory to createBrowserResearchToolkit.', { cause: error });
    }
}
class PlaywrightBrowserDriver {
    context;
    page;
    refs = new Map();
    constructor(context, page) {
        this.context = context;
        this.page = page;
    }
    async open(url) {
        await this.page.goto(url, { waitUntil: 'domcontentloaded' });
        return this.page.url();
    }
    async snapshot(options) {
        const locator = this.page.locator(INTERACTIVE_SELECTOR);
        const count = await locator.count();
        const elements = [];
        this.refs = new Map();
        for (let index = 0; index < count && elements.length < options.maxElements; index += 1) {
            const item = locator.nth(index);
            const element = await this.snapshotElement(item, index);
            if (!element) {
                continue;
            }
            elements.push(element);
            this.refs.set(element.ref, item);
        }
        const body = this.page.locator('body');
        const ariaSnapshot = await body.ariaSnapshot().catch(() => '');
        return {
            url: this.page.url(),
            title: await this.page.title(),
            ariaSnapshot,
            elements,
        };
    }
    async click(ref, options = {}) {
        const locator = this.refs.get(ref);
        if (!locator) {
            throw new Error(`Unknown browser snapshot ref: ${ref}`);
        }
        const routeHandler = this.createNavigationGuard(options);
        await this.page.route('**/*', routeHandler);
        try {
            await locator.click();
            await this.page.waitForLoadState('domcontentloaded').catch(() => undefined);
        }
        finally {
            await this.page.unroute('**/*', routeHandler).catch(() => undefined);
        }
        return this.page.url();
    }
    async screenshot(path) {
        await this.page.screenshot({ path, fullPage: true });
    }
    async close() {
        await this.context.close();
    }
    currentUrl() {
        return this.page.url();
    }
    createNavigationGuard(options) {
        return async (route) => {
            const request = route.request();
            if (!request.isNavigationRequest() || !options.canNavigateTo) {
                await route.continue();
                return;
            }
            if (!options.canNavigateTo(request.url())) {
                await route.abort('blockedbyclient');
                return;
            }
            const finalUrl = await this.resolveFinalNavigationUrl(route);
            if (!options.canNavigateTo(finalUrl)) {
                await route.abort('blockedbyclient');
                return;
            }
            await route.continue();
        };
    }
    async resolveFinalNavigationUrl(route) {
        const response = await route.fetch({ maxRedirects: 20 });
        return response.url();
    }
    async snapshotElement(locator, index) {
        const visible = await locator.isVisible().catch(() => false);
        if (!visible) {
            return undefined;
        }
        const detail = await locator.evaluate((element) => {
            const htmlElement = element;
            const tagName = htmlElement.tagName.toLowerCase();
            const role = htmlElement.getAttribute('role') ?? inferRole(tagName, htmlElement);
            const href = element instanceof HTMLAnchorElement ? element.href : undefined;
            // Report only browser/DOM facts. Do not infer site-specific route variants
            // such as locale, tenant, or storefront prefixes from one observed page.
            const rawHref = element instanceof HTMLAnchorElement
                ? htmlElement.getAttribute('href') ?? undefined
                : undefined;
            const name = [
                htmlElement.getAttribute('aria-label'),
                htmlElement.getAttribute('title'),
                htmlElement.innerText,
                htmlElement.getAttribute('value'),
                href,
            ].find((value) => value && value.trim())?.trim() ?? tagName;
            return {
                role,
                name,
                text: htmlElement.innerText?.trim() || undefined,
                href,
                rawHref,
                tagName,
            };
            function inferRole(tag, node) {
                const roles = {
                    a: 'link',
                    button: 'button',
                    input: inputRole(node),
                    select: 'combobox',
                    summary: 'button',
                    textarea: 'textbox',
                };
                return roles[tag] ?? 'generic';
            }
            function inputRole(node) {
                const type = node.getAttribute('type') ?? 'text';
                const roles = {
                    button: 'button',
                    checkbox: 'checkbox',
                    radio: 'radio',
                    search: 'searchbox',
                    submit: 'button',
                };
                return roles[type] ?? 'textbox';
            }
        }).catch(() => undefined);
        if (!detail) {
            return undefined;
        }
        return {
            ref: `el_${index + 1}`,
            ...detail,
        };
    }
}
//# sourceMappingURL=driver.js.map