"""Browser automation tool for Promethian.

Uses Playwright for headless browser control.
This is a built-in tool that's always available to the agent.
"""

from __future__ import annotations

import asyncio
import base64
from dataclasses import dataclass
from typing import Any, Optional
from pathlib import Path


@dataclass
class BrowserResult:
    """Result from a browser action."""
    success: bool
    data: Any = None
    error: str | None = None
    screenshot: bytes | None = None


class BrowserTool:
    """
    Headless browser automation tool.

    Provides:
    - navigate(url) - Go to a URL
    - screenshot() - Take a screenshot
    - click(selector) - Click an element
    - fill(selector, text) - Fill a form field
    - get_text(selector) - Get text content
    - get_html() - Get page HTML
    - evaluate(js) - Run JavaScript
    """

    def __init__(self):
        self._browser = None
        self._context = None
        self._page = None
        self._playwright = None

    async def _ensure_browser(self):
        """Ensure browser is launched."""
        if self._browser is not None:
            return

        try:
            from playwright.async_api import async_playwright
        except ImportError:
            raise RuntimeError(
                "Playwright not installed. Run: pip install playwright && playwright install chromium"
            )

        self._playwright = await async_playwright().start()
        self._browser = await self._playwright.chromium.launch(headless=True)
        self._context = await self._browser.new_context(
            viewport={"width": 1280, "height": 720},
            user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
        )
        self._page = await self._context.new_page()

    async def close(self):
        """Close the browser."""
        if self._browser:
            await self._browser.close()
            self._browser = None
        if self._playwright:
            await self._playwright.stop()
            self._playwright = None

    async def navigate(self, url: str, wait_for: str = "load") -> BrowserResult:
        """
        Navigate to a URL.

        Args:
            url: The URL to navigate to
            wait_for: When to consider navigation done ("load", "domcontentloaded", "networkidle")

        Returns:
            BrowserResult with page title
        """
        try:
            await self._ensure_browser()
            await self._page.goto(url, wait_until=wait_for)
            title = await self._page.title()
            return BrowserResult(success=True, data={"title": title, "url": self._page.url})
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def screenshot(self, full_page: bool = False) -> BrowserResult:
        """
        Take a screenshot of the current page.

        Args:
            full_page: If True, capture the full scrollable page

        Returns:
            BrowserResult with base64-encoded screenshot
        """
        try:
            await self._ensure_browser()
            screenshot_bytes = await self._page.screenshot(full_page=full_page)
            screenshot_b64 = base64.b64encode(screenshot_bytes).decode()
            return BrowserResult(
                success=True,
                data={"screenshot_base64": screenshot_b64},
                screenshot=screenshot_bytes
            )
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def click(self, selector: str) -> BrowserResult:
        """
        Click an element on the page.

        Args:
            selector: CSS selector or text selector

        Returns:
            BrowserResult indicating success
        """
        try:
            await self._ensure_browser()
            await self._page.click(selector)
            return BrowserResult(success=True)
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def fill(self, selector: str, text: str) -> BrowserResult:
        """
        Fill a form field with text.

        Args:
            selector: CSS selector for the input field
            text: Text to enter

        Returns:
            BrowserResult indicating success
        """
        try:
            await self._ensure_browser()
            await self._page.fill(selector, text)
            return BrowserResult(success=True)
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def type(self, selector: str, text: str, delay: int = 50) -> BrowserResult:
        """
        Type text character by character (for inputs that need keystrokes).

        Args:
            selector: CSS selector for the input field
            text: Text to type
            delay: Delay between keystrokes in ms

        Returns:
            BrowserResult indicating success
        """
        try:
            await self._ensure_browser()
            await self._page.type(selector, text, delay=delay)
            return BrowserResult(success=True)
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def get_text(self, selector: str) -> BrowserResult:
        """
        Get the text content of an element.

        Args:
            selector: CSS selector

        Returns:
            BrowserResult with text content
        """
        try:
            await self._ensure_browser()
            text = await self._page.text_content(selector)
            return BrowserResult(success=True, data={"text": text})
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def get_html(self, selector: str = "body") -> BrowserResult:
        """
        Get the HTML content of an element.

        Args:
            selector: CSS selector (default: body)

        Returns:
            BrowserResult with HTML content
        """
        try:
            await self._ensure_browser()
            html = await self._page.inner_html(selector)
            return BrowserResult(success=True, data={"html": html})
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def evaluate(self, js: str) -> BrowserResult:
        """
        Execute JavaScript in the page context.

        Args:
            js: JavaScript code to execute

        Returns:
            BrowserResult with the return value
        """
        try:
            await self._ensure_browser()
            result = await self._page.evaluate(js)
            return BrowserResult(success=True, data={"result": result})
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def wait_for_selector(self, selector: str, timeout: int = 30000) -> BrowserResult:
        """
        Wait for an element to appear.

        Args:
            selector: CSS selector
            timeout: Max wait time in ms

        Returns:
            BrowserResult indicating if element was found
        """
        try:
            await self._ensure_browser()
            await self._page.wait_for_selector(selector, timeout=timeout)
            return BrowserResult(success=True)
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def select(self, selector: str, value: str) -> BrowserResult:
        """
        Select an option from a dropdown.

        Args:
            selector: CSS selector for the select element
            value: Value to select

        Returns:
            BrowserResult indicating success
        """
        try:
            await self._ensure_browser()
            await self._page.select_option(selector, value)
            return BrowserResult(success=True)
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def get_cookies(self) -> BrowserResult:
        """Get all cookies for the current page."""
        try:
            await self._ensure_browser()
            cookies = await self._context.cookies()
            return BrowserResult(success=True, data={"cookies": cookies})
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    async def set_cookies(self, cookies: list[dict]) -> BrowserResult:
        """Set cookies for the browser context."""
        try:
            await self._ensure_browser()
            await self._context.add_cookies(cookies)
            return BrowserResult(success=True)
        except Exception as e:
            return BrowserResult(success=False, error=str(e))

    def get_tool_schemas(self) -> list[dict]:
        """Get Claude tool schemas for all browser actions."""
        return [
            {
                "name": "browser_navigate",
                "description": "Navigate to a URL in the browser",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "url": {"type": "string", "description": "The URL to navigate to"},
                        "wait_for": {
                            "type": "string",
                            "enum": ["load", "domcontentloaded", "networkidle"],
                            "description": "When to consider navigation done",
                            "default": "load"
                        }
                    },
                    "required": ["url"]
                }
            },
            {
                "name": "browser_screenshot",
                "description": "Take a screenshot of the current page",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "full_page": {
                            "type": "boolean",
                            "description": "Capture the full scrollable page",
                            "default": False
                        }
                    }
                }
            },
            {
                "name": "browser_click",
                "description": "Click an element on the page",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "selector": {"type": "string", "description": "CSS selector for the element"}
                    },
                    "required": ["selector"]
                }
            },
            {
                "name": "browser_fill",
                "description": "Fill a form field with text",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "selector": {"type": "string", "description": "CSS selector for the input"},
                        "text": {"type": "string", "description": "Text to enter"}
                    },
                    "required": ["selector", "text"]
                }
            },
            {
                "name": "browser_get_text",
                "description": "Get the text content of an element",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "selector": {"type": "string", "description": "CSS selector"}
                    },
                    "required": ["selector"]
                }
            },
            {
                "name": "browser_evaluate",
                "description": "Execute JavaScript in the page context",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "js": {"type": "string", "description": "JavaScript code to execute"}
                    },
                    "required": ["js"]
                }
            },
        ]

    async def execute_tool(self, name: str, params: dict) -> BrowserResult:
        """Execute a browser tool by name."""
        method_map = {
            "browser_navigate": lambda p: self.navigate(p["url"], p.get("wait_for", "load")),
            "browser_screenshot": lambda p: self.screenshot(p.get("full_page", False)),
            "browser_click": lambda p: self.click(p["selector"]),
            "browser_fill": lambda p: self.fill(p["selector"], p["text"]),
            "browser_type": lambda p: self.type(p["selector"], p["text"], p.get("delay", 50)),
            "browser_get_text": lambda p: self.get_text(p["selector"]),
            "browser_get_html": lambda p: self.get_html(p.get("selector", "body")),
            "browser_evaluate": lambda p: self.evaluate(p["js"]),
            "browser_wait": lambda p: self.wait_for_selector(p["selector"], p.get("timeout", 30000)),
            "browser_select": lambda p: self.select(p["selector"], p["value"]),
        }

        if name not in method_map:
            return BrowserResult(success=False, error=f"Unknown browser tool: {name}")

        return await method_map[name](params)


# Singleton browser instance
_browser: BrowserTool | None = None


async def get_browser() -> BrowserTool:
    """Get the global browser instance."""
    global _browser
    if _browser is None:
        _browser = BrowserTool()
    return _browser


async def close_browser() -> None:
    """Close the global browser instance."""
    global _browser
    if _browser is not None:
        await _browser.close()
        _browser = None
