/**
 * BrowserActions — thin, typed wrapper over playwright-core Page actions.
 *
 * Every action accepts a `Target`:
 *   - `{ index: number }` -> resolved via `data-avis-idx="<n>"` (set by observePage)
 *   - `{ selector: string }` -> raw CSS selector
 *
 * Actions are intentionally dumb — no LLM, no planning. The agent layer (v2)
 * will plan actions on top of these primitives.
 */
function asLocator(page, target) {
    if ("index" in target) {
        return page.locator(`[data-avis-idx="${target.index}"]`);
    }
    return page.locator(target.selector);
}
function describeTarget(target) {
    return "index" in target ? `index ${target.index}` : `selector ${target.selector}`;
}
export class BrowserActions {
    constructor(getPage, getContext) {
        this.getPage = getPage;
        this.getContext = getContext;
    }
    async click(target, opts) {
        const page = this.getPage();
        const loc = asLocator(page, target).first();
        await loc.waitFor({ state: "visible", timeout: 5_000 });
        if (opts?.dblclick) {
            await loc.dblclick({ button: opts.button, timeout: 10_000 });
        }
        else {
            await loc.click({ button: opts?.button, timeout: 10_000 });
        }
        return `clicked ${describeTarget(target)}`;
    }
    async hover(target) {
        const page = this.getPage();
        const loc = asLocator(page, target).first();
        await loc.hover({ timeout: 5_000 });
        return `hovered ${describeTarget(target)}`;
    }
    async type(target, text, opts) {
        const page = this.getPage();
        const loc = asLocator(page, target).first();
        await loc.waitFor({ state: "visible", timeout: 5_000 });
        if (opts?.clear ?? true) {
            await loc.fill("");
        }
        // type() dispatches real keyboard events (triggers autocomplete, React onChange, etc.)
        await loc.type(text, { delay: 15 });
        if (opts?.submit) {
            await loc.press("Enter");
            return `typed and submitted in ${describeTarget(target)}: ${JSON.stringify(text)}`;
        }
        return `typed in ${describeTarget(target)}: ${JSON.stringify(text)}`;
    }
    async select(target, optionText) {
        const page = this.getPage();
        const loc = asLocator(page, target).first();
        await loc.waitFor({ state: "visible", timeout: 5_000 });
        // Try label first, then value.
        try {
            await loc.selectOption({ label: optionText }, { timeout: 3_000 });
        }
        catch {
            await loc.selectOption({ value: optionText }, { timeout: 3_000 });
        }
        return `selected "${optionText}" in ${describeTarget(target)}`;
    }
    async press(keys) {
        // e.g. "Enter", "Escape", "Control+K", "Meta+A"
        const page = this.getPage();
        await page.keyboard.press(keys);
        return `pressed ${keys}`;
    }
    async scroll(opts) {
        const page = this.getPage();
        const dir = opts.direction ?? "down";
        const script = (() => {
            if (dir === "top")
                return `window.scrollTo(0, 0)`;
            if (dir === "bottom")
                return `window.scrollTo(0, document.documentElement.scrollHeight)`;
            const px = Math.max(0, Math.floor(opts.pixels ?? 0)) || "window.innerHeight";
            const sign = dir === "up" ? "-" : "";
            return `window.scrollBy(0, ${sign}(${px}))`;
        })();
        await page.evaluate(script);
        return `scrolled ${dir}${opts.pixels ? ` ${opts.pixels}px` : ""}`;
    }
    async scrollIntoView(target) {
        const page = this.getPage();
        const loc = asLocator(page, target).first();
        await loc.scrollIntoViewIfNeeded({ timeout: 5_000 });
        return `scrolled ${describeTarget(target)} into view`;
    }
    // ─── Tabs ──────────────────────────────────────────────────────────────
    async listTabs() {
        const ctx = this.getContext();
        const pages = ctx.pages();
        const activePage = this.getPage();
        const out = await Promise.all(pages.map(async (p, idx) => ({
            id: idx,
            url: p.url(),
            title: await p.title().catch(() => ""),
            active: p === activePage,
        })));
        return out;
    }
    /** Returns the new Page; caller decides whether to promote it to active. */
    async openTab(url) {
        const ctx = this.getContext();
        const page = await ctx.newPage();
        if (url) {
            const normalized = /^[a-z]+:\/\//i.test(url) ? url : `https://${url}`;
            await page.goto(normalized, { waitUntil: "domcontentloaded", timeout: 30_000 });
        }
        return page;
    }
    async closeTab(id) {
        const ctx = this.getContext();
        const pages = ctx.pages();
        const target = pages[id];
        if (!target)
            throw new Error(`No tab with id ${id}`);
        await target.close();
        return `closed tab ${id}`;
    }
    async switchTab(id) {
        const ctx = this.getContext();
        const pages = ctx.pages();
        const target = pages[id];
        if (!target)
            throw new Error(`No tab with id ${id}`);
        await target.bringToFront();
        return target;
    }
}
//# sourceMappingURL=browser-actions.js.map