"""
browser_controller.py
=====================
Módulo de Controle de Navegador Web (Browser Automation)
Parte do sistema de Agente AI Desktop

O agente consegue:
- Abrir qualquer URL
- Ler TUDO da página (texto, links, imagens, formulários)
- Clicar em qualquer elemento (por texto, seletor, posição)
- Rolar a página
- Preencher formulários
- Tirar screenshot da página
- Extrair dados estruturados
- Mandar conteúdo para o Grok analisar

Dependências:
    pip install playwright
    playwright install chromium
"""

import asyncio
import base64
import json
import time
from pathlib import Path
from typing import Optional

from playwright.async_api import async_playwright, Browser, BrowserContext, Page, Playwright

_AGENT_CACHE_BROWSER = Path(__file__).resolve().parent / ".ad06_cache" / "browser"


def default_browser_screenshot_dir() -> str:
    _AGENT_CACHE_BROWSER.mkdir(parents=True, exist_ok=True)
    return str(_AGENT_CACHE_BROWSER)


# ==============================================================================
# CLASSE PRINCIPAL
# ==============================================================================

class BrowserController:
    """
    Controlador completo de navegador para o agente AI.
    Usa Playwright com Chromium para controle total de páginas web.
    """

    def __init__(
        self,
        screenshot_dir: str | None = None,
        headless: bool = False,
        slow_mo: int = 100,
    ):
        """
        Args:
            screenshot_dir : Pasta para salvar screenshots
            headless       : True = navegador invisível | False = navegador visível
            slow_mo        : Delay em ms entre ações (útil para debug)
        """
        self.screenshot_dir = Path(screenshot_dir or default_browser_screenshot_dir())
        self.screenshot_dir.mkdir(parents=True, exist_ok=True)
        self.headless = headless
        self.slow_mo = slow_mo

        # Instâncias internas (inicializadas ao conectar)
        self._playwright: Optional[Playwright] = None
        self._browser: Optional[Browser] = None
        self._context: Optional[BrowserContext] = None
        self._page: Optional[Page] = None

        # Cache da página atual — usado pelo agente para responder sem re-scraping
        self._page_cache: dict = {}

    # ==========================================================================
    # CONEXÃO E CICLO DE VIDA
    # ==========================================================================

    async def start(self) -> None:
        """
        Inicia o Playwright e abre o navegador Chromium.
        Deve ser chamado antes de qualquer outra função.

        Exemplo:
            await browser.start()
            await browser.goto("https://google.com")
        """
        self._playwright = await async_playwright().start()
        self._browser = await self._playwright.chromium.launch(
            headless=self.headless,
            slow_mo=self.slow_mo,
            args=[
                "--no-sandbox",
                "--disable-blink-features=AutomationControlled",  # evita detecção de bot
            ],
        )
        self._context = await self._browser.new_context(
            viewport={"width": 1280, "height": 800},
            user_agent=(
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                "AppleWebKit/537.36 (KHTML, like Gecko) "
                "Chrome/120.0.0.0 Safari/537.36"
            ),
        )
        self._page = await self._context.new_page()

    async def stop(self) -> None:
        """Fecha o navegador e libera recursos."""
        if self._browser:
            await self._browser.close()
        if self._playwright:
            await self._playwright.stop()
        self._browser = None
        self._context = None
        self._page = None
        self._playwright = None

    @property
    def page(self) -> Page:
        """Retorna a página atual (garante que está iniciado)."""
        if not self._page:
            raise RuntimeError("Navegador não iniciado. Chame await browser.start() primeiro.")
        return self._page

    # ==========================================================================
    # NAVEGAÇÃO
    # ==========================================================================

    async def goto(self, url: str, wait_until: str = "domcontentloaded") -> None:
        """
        Navega para uma URL.

        Args:
            url        : URL completa (ex: "https://google.com")
            wait_until : Quando considerar carregado:
                         'domcontentloaded' (rápido) | 'networkidle' (completo)

        Exemplo:
            await browser.goto("https://google.com")
        """
        if not url.startswith("http"):
            url = "https://" + url
        await self.page.goto(url, wait_until=wait_until, timeout=30000)
        await self._atualizar_cache()

    async def get_current_url(self) -> str:
        """Retorna a URL atual da página."""
        return self.page.url

    async def get_title(self) -> str:
        """Retorna o título da página atual."""
        return await self.page.title()

    async def go_back(self) -> None:
        """Volta para a página anterior (equivalente ao botão Voltar)."""
        await self.page.go_back()
        await self._atualizar_cache()

    async def go_forward(self) -> None:
        """Avança para a próxima página (equivalente ao botão Avançar)."""
        await self.page.go_forward()
        await self._atualizar_cache()

    async def reload(self) -> None:
        """Recarrega a página atual."""
        await self.page.reload()
        await self._atualizar_cache()

    # ==========================================================================
    # LER CONTEÚDO DA PÁGINA — O AGENTE "VÊ" A PÁGINA
    # ==========================================================================

    async def get_page_text(self) -> str:
        """
        Retorna TODO o texto visível da página (sem tags HTML).
        O agente usa isso para ler e entender o conteúdo da página.

        Returns:
            Texto limpo da página

        Exemplo:
            texto = await browser.get_page_text()
            # Manda 'texto' para o Grok responder perguntas sobre a página
        """
        return await self.page.inner_text("body")

    async def get_page_html(self) -> str:
        """
        Retorna o HTML completo da página.
        Útil para análise estrutural profunda.
        """
        return await self.page.content()

    async def get_all_links(self) -> list[dict]:
        """
        Retorna todos os links da página com texto e URL.

        Returns:
            Lista de dicts: [{"text": "Google", "href": "https://google.com", "x": 100, "y": 200}]

        Exemplo:
            links = await browser.get_all_links()
            for link in links:
                print(f"{link['text']} → {link['href']}")
        """
        links = await self.page.eval_on_selector_all(
            "a[href]",
            """els => els.map(e => {
                const rect = e.getBoundingClientRect();
                return {
                    text: e.innerText.trim(),
                    href: e.href,
                    x: Math.round(rect.x + rect.width / 2),
                    y: Math.round(rect.y + rect.height / 2)
                };
            })"""
        )
        # Filtra links vazios
        return [l for l in links if l["text"] or l["href"]]

    async def get_all_buttons(self) -> list[dict]:
        """
        Retorna todos os botões da página com texto e posição.

        Returns:
            Lista de dicts: [{"text": "Enviar", "x": 300, "y": 450}]
        """
        return await self.page.eval_on_selector_all(
            "button, input[type='button'], input[type='submit'], [role='button']",
            """els => els.map(e => {
                const rect = e.getBoundingClientRect();
                return {
                    text: (e.innerText || e.value || e.getAttribute('aria-label') || '').trim(),
                    x: Math.round(rect.x + rect.width / 2),
                    y: Math.round(rect.y + rect.height / 2),
                    visible: rect.width > 0 && rect.height > 0
                };
            }).filter(e => e.visible)"""
        )

    async def get_all_inputs(self) -> list[dict]:
        """
        Retorna todos os campos de input da página.

        Returns:
            Lista de dicts com tipo, placeholder, nome e posição
        """
        return await self.page.eval_on_selector_all(
            "input, textarea, select",
            """els => els.map(e => {
                const rect = e.getBoundingClientRect();
                return {
                    type: e.type || e.tagName.toLowerCase(),
                    name: e.name || '',
                    placeholder: e.placeholder || '',
                    value: e.value || '',
                    x: Math.round(rect.x + rect.width / 2),
                    y: Math.round(rect.y + rect.height / 2),
                    visible: rect.width > 0 && rect.height > 0
                };
            }).filter(e => e.visible)"""
        )

    async def get_all_images(self) -> list[dict]:
        """
        Retorna todas as imagens da página com src e alt.
        """
        return await self.page.eval_on_selector_all(
            "img",
            "els => els.map(e => ({src: e.src, alt: e.alt, width: e.naturalWidth, height: e.naturalHeight}))"
        )

    async def get_page_summary(self) -> dict:
        """
        Retorna um resumo completo da página para o agente usar.
        Combina: título, URL, texto, links, botões e inputs.

        Returns:
            Dict com todas as informações da página

        Exemplo:
            info = await browser.get_page_summary()
            # Manda 'info' para o Grok: "O que você vê nessa página?"
        """
        titulo = await self.get_title()
        url = await self.get_current_url()
        texto = await self.get_page_text()
        links = await self.get_all_links()
        botoes = await self.get_all_buttons()
        inputs = await self.get_all_inputs()

        summary = {
            "titulo": titulo,
            "url": url,
            "texto_pagina": texto[:5000],  # Limita para não explodir o contexto do LLM
            "total_links": len(links),
            "links": links[:30],           # Os 30 primeiros links
            "botoes": botoes[:20],
            "inputs": inputs[:10],
        }

        # Salva no cache para o agente usar depois
        self._page_cache = summary
        return summary

    async def get_cached_page_info(self) -> dict:
        """
        Retorna as informações da página do cache (sem re-escanear).
        O agente usa isso para responder perguntas sem precisar re-scraping.
        """
        if not self._page_cache:
            return await self.get_page_summary()
        return self._page_cache

    # ==========================================================================
    # CLICAR EM ELEMENTOS
    # ==========================================================================

    async def click_text(self, text: str, exact: bool = False) -> bool:
        """
        Clica em um elemento pelo texto visível.

        Args:
            text  : Texto do elemento a clicar
            exact : True = correspondência exata | False = contém o texto

        Returns:
            True se clicou com sucesso

        Exemplo:
            await browser.click_text("Entrar")
            await browser.click_text("Saiba mais")
        """
        try:
            if exact:
                await self.page.click(f"text='{text}'", timeout=5000)
            else:
                await self.page.click(f"text={text}", timeout=5000)
            await self._atualizar_cache()
            return True
        except Exception:
            return False

    async def click_link(self, link_text: str) -> bool:
        """
        Clica em um link pelo texto.
        Usa o cache de links para encontrar a posição exata.

        Args:
            link_text : Texto do link (ex: "Política de Privacidade")

        Returns:
            True se encontrou e clicou

        Exemplo:
            await browser.click_link("Fazer login")
        """
        # Tenta pelo cache primeiro
        links = self._page_cache.get("links", [])
        for link in links:
            if link_text.lower() in link.get("text", "").lower():
                await self.page.mouse.click(link["x"], link["y"])
                await self._atualizar_cache()
                return True

        # Fallback: tenta pelo seletor Playwright
        return await self.click_text(link_text)

    async def click_button(self, button_text: str) -> bool:
        """
        Clica em um botão pelo texto.

        Args:
            button_text : Texto do botão (ex: "Enviar", "OK", "Cancelar")

        Returns:
            True se encontrou e clicou
        """
        try:
            await self.page.click(f"button:has-text('{button_text}')", timeout=5000)
            return True
        except Exception:
            return await self.click_text(button_text)

    async def click_selector(self, selector: str) -> bool:
        """
        Clica em um elemento por seletor CSS ou XPath.

        Args:
            selector : Seletor CSS (ex: "#btn-login", ".menu-item")

        Returns:
            True se clicou com sucesso

        Exemplo:
            await browser.click_selector("#submit-button")
            await browser.click_selector(".nav-link:first-child")
        """
        try:
            await self.page.click(selector, timeout=5000)
            return True
        except Exception:
            return False

    async def click_coordinates(self, x: int, y: int) -> None:
        """
        Clica em coordenadas absolutas da janela do navegador.

        Args:
            x, y : Coordenadas do clique
        """
        await self.page.mouse.click(x, y)

    async def click_link_by_href(self, href_contains: str) -> bool:
        """
        Clica em um link que contém parte da URL.

        Args:
            href_contains : Parte da URL do link (ex: "/login", "youtube.com")

        Returns:
            True se encontrou e clicou
        """
        links = await self.get_all_links()
        for link in links:
            if href_contains.lower() in link.get("href", "").lower():
                await self.page.mouse.click(link["x"], link["y"])
                await self._atualizar_cache()
                return True
        return False

    # ==========================================================================
    # DIGITAR E PREENCHER FORMULÁRIOS
    # ==========================================================================

    async def type_in(self, selector: str, text: str, clear_first: bool = True) -> bool:
        """
        Digita texto em um campo de input.

        Args:
            selector    : Seletor CSS do campo (ex: "input[name='email']")
            text        : Texto a digitar
            clear_first : Limpa o campo antes de digitar

        Returns:
            True se digitou com sucesso

        Exemplo:
            await browser.type_in("input[name='q']", "python tutorial")
        """
        try:
            if clear_first:
                await self.page.fill(selector, "")
            await self.page.fill(selector, text)
            return True
        except Exception:
            try:
                await self.page.click(selector)
                if clear_first:
                    await self.page.keyboard.press("Control+a")
                await self.page.keyboard.type(text)
                return True
            except Exception:
                return False

    async def type_in_focused(self, text: str) -> None:
        """
        Digita texto no elemento atualmente em foco.

        Args:
            text : Texto a digitar
        """
        await self.page.keyboard.type(text)

    async def press_key(self, key: str) -> None:
        """
        Pressiona uma tecla no navegador.

        Args:
            key : Tecla (ex: "Enter", "Escape", "Tab", "ArrowDown")

        Exemplo:
            await browser.press_key("Enter")
            await browser.press_key("Tab")
        """
        await self.page.keyboard.press(key)

    async def search_google(self, query: str) -> None:
        """
        Faz uma pesquisa no Google.

        Args:
            query : Termo de pesquisa

        Exemplo:
            await browser.search_google("clima em São Paulo hoje")
        """
        await self.goto("https://www.google.com")
        typed = False
        for sel in ("textarea[name='q']", "textarea[name=\"q\"]", "input[name='q']", "textarea#APjFqb"):
            if await self.type_in(sel, query):
                typed = True
                break
        if not typed:
            try:
                await self.page.locator("textarea").first.fill(query, timeout=8000)
            except Exception:
                await self.page.get_by_role("combobox", name="Search").fill(query)
        await self.press_key("Enter")
        await self.page.wait_for_load_state("domcontentloaded")
        await self._atualizar_cache()

    # ==========================================================================
    # SCROLL
    # ==========================================================================

    async def scroll_down(self, pixels: int = 500) -> None:
        """
        Rola a página para baixo.

        Args:
            pixels : Quantidade de pixels para rolar

        Exemplo:
            await browser.scroll_down(300)
            await browser.scroll_down(1000)  # rola bastante
        """
        await self.page.evaluate(f"window.scrollBy(0, {pixels})")

    async def scroll_up(self, pixels: int = 500) -> None:
        """Rola a página para cima."""
        await self.page.evaluate(f"window.scrollBy(0, -{pixels})")

    async def scroll_to_bottom(self) -> None:
        """Rola até o final da página."""
        await self.page.evaluate("window.scrollTo(0, document.body.scrollHeight)")

    async def scroll_to_top(self) -> None:
        """Rola até o topo da página."""
        await self.page.evaluate("window.scrollTo(0, 0)")

    async def scroll_to_element(self, selector: str) -> bool:
        """
        Rola a página até um elemento específico ficar visível.

        Args:
            selector : Seletor CSS do elemento

        Returns:
            True se encontrou e rolou até o elemento
        """
        try:
            await self.page.eval_on_selector(
                selector,
                "el => el.scrollIntoView({behavior: 'smooth', block: 'center'})"
            )
            return True
        except Exception:
            return False

    async def scroll_to_text(self, text: str) -> bool:
        """
        Rola a página até encontrar um texto específico.

        Args:
            text : Texto a procurar

        Returns:
            True se encontrou e rolou até ele
        """
        try:
            await self.page.eval_on_selector(
                f"text={text}",
                "el => el.scrollIntoView({behavior: 'smooth', block: 'center'})"
            )
            return True
        except Exception:
            return False

    # ==========================================================================
    # SCREENSHOT DO NAVEGADOR
    # ==========================================================================

    async def screenshot(self, save_path: Optional[str] = None, full_page: bool = False) -> str:
        """
        Tira screenshot da página atual.

        Args:
            save_path : Caminho para salvar (gera automaticamente se None)
            full_page : True = captura página inteira (com scroll)

        Returns:
            Caminho do arquivo salvo

        Exemplo:
            path = await browser.screenshot()
            path = await browser.screenshot(full_page=True)
        """
        if not save_path:
            ts = int(time.time())
            save_path = str(self.screenshot_dir / f"browser_{ts}.png")

        await self.page.screenshot(path=save_path, full_page=full_page)
        return save_path

    async def screenshot_as_base64(self, full_page: bool = False) -> str:
        """
        Retorna screenshot da página como string base64.
        Pronto para enviar direto ao Grok Vision.

        Returns:
            String base64 da imagem

        Exemplo:
            b64 = await browser.screenshot_as_base64()
            # Usa direto na chamada do Grok Vision
        """
        img_bytes = await self.page.screenshot(full_page=full_page)
        return base64.b64encode(img_bytes).decode()

    async def screenshot_element(self, selector: str, save_path: Optional[str] = None) -> Optional[str]:
        """
        Tira screenshot de um elemento específico da página.

        Args:
            selector  : Seletor CSS do elemento
            save_path : Caminho para salvar

        Returns:
            Caminho do arquivo ou None se não encontrou
        """
        try:
            if not save_path:
                ts = int(time.time())
                save_path = str(self.screenshot_dir / f"element_{ts}.png")
            element = await self.page.query_selector(selector)
            if element:
                await element.screenshot(path=save_path)
                return save_path
        except Exception:
            pass
        return None

    # ==========================================================================
    # ESPERAR ELEMENTOS
    # ==========================================================================

    async def wait_for_text(self, text: str, timeout: float = 10.0) -> bool:
        """
        Aguarda até um texto aparecer na página.

        Args:
            text    : Texto a aguardar
            timeout : Tempo máximo em segundos

        Returns:
            True se apareceu, False se deu timeout
        """
        try:
            await self.page.wait_for_selector(
                f"text={text}",
                timeout=timeout * 1000
            )
            return True
        except Exception:
            return False

    async def wait_for_selector(self, selector: str, timeout: float = 10.0) -> bool:
        """
        Aguarda até um seletor CSS aparecer na página.

        Args:
            selector : Seletor CSS
            timeout  : Tempo máximo em segundos
        """
        try:
            await self.page.wait_for_selector(selector, timeout=timeout * 1000)
            return True
        except Exception:
            return False

    async def wait_for_navigation(self, timeout: float = 15.0) -> None:
        """Aguarda a navegação completar após um clique."""
        try:
            await self.page.wait_for_load_state("domcontentloaded", timeout=timeout * 1000)
        except Exception:
            pass

    # ==========================================================================
    # ABAS (TABS)
    # ==========================================================================

    async def new_tab(self, url: Optional[str] = None) -> None:
        """
        Abre uma nova aba, opcionalmente navegando para uma URL.

        Args:
            url : URL para abrir na nova aba (opcional)
        """
        self._page = await self._context.new_page()
        if url:
            await self.goto(url)

    async def get_all_tabs(self) -> list[str]:
        """
        Retorna os títulos de todas as abas abertas.

        Returns:
            Lista de títulos
        """
        titles = []
        for page in self._context.pages:
            titles.append(await page.title())
        return titles

    async def switch_to_tab(self, index: int) -> None:
        """
        Troca para uma aba pelo índice.

        Args:
            index : Índice da aba (começa em 0)
        """
        pages = self._context.pages
        if 0 <= index < len(pages):
            self._page = pages[index]
            await self._page.bring_to_front()

    async def close_tab(self) -> None:
        """Fecha a aba atual."""
        await self.page.close()
        if self._context.pages:
            self._page = self._context.pages[-1]

    # ==========================================================================
    # EXTRAÇÃO DE DADOS
    # ==========================================================================

    async def get_element_text(self, selector: str) -> Optional[str]:
        """
        Retorna o texto de um elemento específico.

        Args:
            selector : Seletor CSS

        Returns:
            Texto do elemento ou None
        """
        try:
            return await self.page.inner_text(selector)
        except Exception:
            return None

    async def get_element_attribute(self, selector: str, attribute: str) -> Optional[str]:
        """
        Retorna o valor de um atributo de um elemento.

        Args:
            selector  : Seletor CSS
            attribute : Nome do atributo (ex: "href", "src", "value")
        """
        try:
            return await self.page.get_attribute(selector, attribute)
        except Exception:
            return None

    async def extract_table(self, selector: str = "table") -> list[list[str]]:
        """
        Extrai dados de uma tabela HTML.

        Args:
            selector : Seletor da tabela

        Returns:
            Lista de listas com os dados da tabela
        """
        try:
            return await self.page.eval_on_selector(
                selector,
                """table => {
                    const rows = Array.from(table.querySelectorAll('tr'));
                    return rows.map(row =>
                        Array.from(row.querySelectorAll('th, td')).map(cell => cell.innerText.trim())
                    );
                }"""
            )
        except Exception:
            return []

    async def run_javascript(self, script: str):
        """
        Executa JavaScript diretamente na página.

        Args:
            script : Código JavaScript

        Returns:
            Resultado da execução

        Exemplo:
            result = await browser.run_javascript("document.title")
            await browser.run_javascript("document.querySelector('#btn').click()")
        """
        return await self.page.evaluate(script)

    # ==========================================================================
    # DOWNLOAD
    # ==========================================================================

    async def download_file(self, url: str, save_path: str) -> bool:
        """
        Faz download de um arquivo.

        Args:
            url       : URL do arquivo
            save_path : Caminho local para salvar

        Returns:
            True se baixou com sucesso
        """
        try:
            async with self.page.expect_download() as download_info:
                await self.page.goto(url)
            download = await download_info.value
            await download.save_as(save_path)
            return True
        except Exception:
            return False

    # ==========================================================================
    # INTEGRAÇÃO COM GROK VISION
    # ==========================================================================

    async def get_page_context_for_grok(self) -> dict:
        """
        Prepara um pacote completo de contexto da página para enviar ao Grok.
        Inclui screenshot base64 + dados estruturados da página.

        Returns:
            Dict pronto para usar na chamada da API do Grok

        Exemplo:
            ctx = await browser.get_page_context_for_grok()
            # Usar ctx['screenshot_b64'] e ctx['page_info'] na chamada do Grok
        """
        screenshot_b64 = await self.screenshot_as_base64()
        page_info = await self.get_page_summary()

        return {
            "screenshot_b64": screenshot_b64,
            "page_info": page_info,
            "prompt_context": f"""
Você está vendo a seguinte página web:
- Título: {page_info['titulo']}
- URL: {page_info['url']}
- Links disponíveis: {json.dumps(page_info['links'][:10], ensure_ascii=False)}
- Botões disponíveis: {json.dumps(page_info['botoes'][:10], ensure_ascii=False)}
- Conteúdo da página: {page_info['texto_pagina'][:3000]}
            """.strip()
        }

    # ==========================================================================
    # MÉTODOS INTERNOS
    # ==========================================================================

    async def _atualizar_cache(self) -> None:
        """Atualiza o cache da página após navegação ou clique."""
        try:
            await self.page.wait_for_load_state("domcontentloaded", timeout=5000)
            await self.get_page_summary()
        except Exception:
            pass


# ==============================================================================
# WRAPPER SÍNCRONO — Para integrar em código não-async (loop principal)
# ==============================================================================

class BrowserControllerSync:
    """
    Versão síncrona do BrowserController.
    Use esta classe se o seu loop principal NÃO usa async/await.

    Exemplo de uso:
        browser = BrowserControllerSync()
        browser.start()
        browser.goto("https://google.com")
        info = browser.get_page_summary()
        browser.stop()
    """

    def __init__(self, **kwargs):
        self._async = BrowserController(**kwargs)
        self._loop = asyncio.new_event_loop()

    def _run(self, coro):
        return self._loop.run_until_complete(coro)

    def start(self):
        self._run(self._async.start())

    def stop(self):
        self._run(self._async.stop())
        self._loop.close()

    def goto(self, url: str):
        self._run(self._async.goto(url))

    def get_page_summary(self) -> dict:
        return self._run(self._async.get_page_summary())

    def get_page_text(self) -> str:
        return self._run(self._async.get_page_text())

    def get_all_links(self) -> list:
        return self._run(self._async.get_all_links())

    def click_text(self, text: str) -> bool:
        return self._run(self._async.click_text(text))

    def click_link(self, link_text: str) -> bool:
        return self._run(self._async.click_link(link_text))

    def click_button(self, text: str) -> bool:
        return self._run(self._async.click_button(text))

    def scroll_down(self, pixels: int = 500):
        self._run(self._async.scroll_down(pixels))

    def scroll_up(self, pixels: int = 500):
        self._run(self._async.scroll_up(pixels))

    def scroll_to_bottom(self):
        self._run(self._async.scroll_to_bottom())

    def search_google(self, query: str):
        self._run(self._async.search_google(query))

    def screenshot(self, save_path: Optional[str] = None, full_page: bool = False) -> str:
        return self._run(self._async.screenshot(save_path, full_page))

    def screenshot_as_base64(self) -> str:
        return self._run(self._async.screenshot_as_base64())

    def get_page_context_for_grok(self) -> dict:
        return self._run(self._async.get_page_context_for_grok())

    def type_in(self, selector: str, text: str) -> bool:
        return self._run(self._async.type_in(selector, text))

    def press_key(self, key: str):
        self._run(self._async.press_key(key))

    def wait_for_text(self, text: str, timeout: float = 10.0) -> bool:
        return self._run(self._async.wait_for_text(text, timeout))

    def get_current_url(self) -> str:
        return self._run(self._async.get_current_url())

    def get_title(self) -> str:
        return self._run(self._async.get_title())

    def run_javascript(self, script: str):
        return self._run(self._async.run_javascript(script))


# ==============================================================================
# EXEMPLO DE USO
# ==============================================================================

if __name__ == "__main__":

    # --- Uso síncrono (mais fácil de integrar) ---
    browser = BrowserControllerSync(headless=False)
    browser.start()

    print("Abrindo Google...")
    browser.search_google("python playwright tutorial")

    print("Lendo página...")
    info = browser.get_page_summary()
    print(f"Título: {info['titulo']}")
    print(f"Links encontrados: {len(info['links'])}")
    for link in info['links'][:5]:
        print(f"  - {link['text']} → {link['href']}")

    print("Rolando a página...")
    browser.scroll_down(500)

    print("Tirando screenshot...")
    path = browser.screenshot()
    print(f"Screenshot salvo: {path}")

    browser.stop()
