"""Browser plugin — Playwright-based web browsing with content extraction."""

from __future__ import annotations

import logging
import re
from typing import Any
from urllib.parse import urlparse

import httpx

from agent_scout.plugins.base import Plugin, PluginConfig, PluginParam, ToolSpec

logger = logging.getLogger(__name__)

USER_AGENTS = [
    (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        " (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
    ),
    (
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
        " (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
    ),
    (
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
        " (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
    ),
]


async def _check_robots_txt(url: str) -> bool:
    """Check if the URL is allowed by robots.txt.

    Returns True if scraping is allowed, False if disallowed.
    Best-effort: if robots.txt can't be fetched, allow by default.
    """
    parsed = urlparse(url)
    robots_url = f"{parsed.scheme}://{parsed.netloc}/robots.txt"
    try:
        async with httpx.AsyncClient() as client:
            resp = await client.get(robots_url, timeout=5)
            if resp.status_code != 200:
                return True
            text = resp.text.lower()
            # Simple check: look for "disallow: /" under "user-agent: *"
            path = parsed.path or "/"
            in_wildcard_block = False
            for line in text.splitlines():
                line = line.strip()
                if line.startswith("user-agent:"):
                    agent = line.split(":", 1)[1].strip()
                    in_wildcard_block = agent == "*"
                elif in_wildcard_block and line.startswith("disallow:"):
                    disallowed = line.split(":", 1)[1].strip()
                    if disallowed and path.startswith(disallowed):
                        return False
    except Exception:
        pass  # Can't reach robots.txt — allow by default
    return True


def _extract_text(html: str) -> str:
    """Extract readable text from HTML, stripping tags and excess whitespace."""
    # Remove script and style blocks
    text = re.sub(r"<(script|style)[^>]*>.*?</\1>", "", html, flags=re.DOTALL | re.IGNORECASE)
    # Remove all tags
    text = re.sub(r"<[^>]+>", " ", text)
    # Decode common entities
    text = text.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">")
    text = text.replace("&nbsp;", " ").replace("&quot;", '"')
    # Collapse whitespace
    text = re.sub(r"\s+", " ", text).strip()
    return text


def _extract_links(html: str, base_url: str) -> list[dict[str, str]]:
    """Extract links from HTML."""
    links: list[dict[str, str]] = []
    parsed_base = urlparse(base_url)
    for match in re.finditer(r'<a[^>]+href=["\']([^"\']+)["\'][^>]*>(.*?)</a>', html, re.DOTALL):
        href, anchor = match.group(1), _extract_text(match.group(2))
        if href.startswith("/"):
            href = f"{parsed_base.scheme}://{parsed_base.netloc}{href}"
        if href.startswith("http"):
            links.append({"url": href, "text": anchor[:100]})
    return links[:50]  # Limit to prevent huge outputs


class BrowserEngine:
    """Wrapper around Playwright browser for page interaction."""

    def __init__(self, headless: bool = True, user_agent_idx: int = 0) -> None:
        self._headless = headless
        self._user_agent = USER_AGENTS[user_agent_idx % len(USER_AGENTS)]
        self._browser: Any = None
        self._context: Any = None

    async def start(self) -> None:
        from playwright.async_api import async_playwright

        self._pw = await async_playwright().start()
        self._browser = await self._pw.chromium.launch(headless=self._headless)
        self._context = await self._browser.new_context(user_agent=self._user_agent)

    async def stop(self) -> None:
        if self._context:
            await self._context.close()
        if self._browser:
            await self._browser.close()
        if hasattr(self, "_pw") and self._pw:
            await self._pw.stop()
        self._browser = None
        self._context = None

    async def get_page_content(self, url: str, timeout_ms: int = 15000) -> dict[str, Any]:
        """Navigate to URL and extract content."""
        page = await self._context.new_page()
        try:
            await page.goto(url, timeout=timeout_ms, wait_until="domcontentloaded")
            html = await page.content()
            title = await page.title()
            return {
                "url": url,
                "title": title,
                "text": _extract_text(html),
                "links": _extract_links(html, url),
                "html_length": len(html),
            }
        finally:
            await page.close()

    async def take_screenshot(self, url: str, path: str, timeout_ms: int = 15000) -> str:
        """Navigate to URL and take a screenshot."""
        page = await self._context.new_page()
        try:
            await page.goto(url, timeout=timeout_ms, wait_until="domcontentloaded")
            await page.screenshot(path=path, full_page=False)
            return path
        finally:
            await page.close()


class BrowserPlugin(Plugin):
    """Playwright-based web browsing plugin.

    Provides tools for navigating web pages, extracting content,
    and taking screenshots. Respects robots.txt before every request.
    """

    def __init__(self, config: PluginConfig | None = None) -> None:
        super().__init__(config)
        self._engine: BrowserEngine | None = None

    @property
    def name(self) -> str:
        return "browser"

    @property
    def description(self) -> str:
        return "Browse web pages, extract content, and take screenshots"

    def get_tools(self) -> list[ToolSpec]:
        return [
            ToolSpec(
                name="browse",
                description=(
                    "Navigate to a web page and extract its text content and links. "
                    "Use this to read articles, product pages, or any web content."
                ),
                parameters=[
                    PluginParam(name="url", description="The URL to browse", required=True),
                ],
            ),
            ToolSpec(
                name="screenshot",
                description=(
                    "Take a screenshot of a web page. Returns the file path of the saved image."
                ),
                parameters=[
                    PluginParam(name="url", description="The URL to screenshot", required=True),
                    PluginParam(
                        name="save_path",
                        description="File path to save the screenshot",
                        default="screenshot.png",
                    ),
                ],
            ),
        ]

    async def on_load(self) -> None:
        headless = self._config.settings.get("headless", True)
        self._engine = BrowserEngine(headless=headless)
        try:
            await self._engine.start()
        except Exception as e:
            logger.warning("Browser engine failed to start: %s", e)
            self._engine = None

    async def on_unload(self) -> None:
        if self._engine:
            await self._engine.stop()
            self._engine = None

    def set_engine(self, engine: BrowserEngine | None) -> None:
        """Override the browser engine (useful for testing)."""
        self._engine = engine

    async def execute(self, tool_name: str, tool_input: dict[str, Any]) -> str:
        if tool_name == "browse":
            return await self._browse(tool_input)
        elif tool_name == "screenshot":
            return await self._screenshot(tool_input)
        return f"Unknown tool: {tool_name}"

    async def _browse(self, tool_input: dict[str, Any]) -> str:
        url = tool_input.get("url", "")
        if not url:
            return "Error: No URL provided"

        # Check robots.txt
        if not await _check_robots_txt(url):
            return f"Blocked by robots.txt: {url}"

        if not self._engine:
            return "Error: Browser engine not available"

        try:
            content = await self._engine.get_page_content(url)
            text = content["text"]
            if len(text) > 5000:
                text = text[:5000] + "... [truncated]"

            lines = [
                f"Title: {content['title']}",
                f"URL: {content['url']}",
                "",
                text,
            ]

            if content["links"]:
                lines.append("")
                lines.append("Key links:")
                for link in content["links"][:10]:
                    lines.append(f"  - {link['text']}: {link['url']}")

            return "\n".join(lines)
        except Exception as e:
            return f"Error browsing {url}: {e}"

    async def _screenshot(self, tool_input: dict[str, Any]) -> str:
        url = tool_input.get("url", "")
        save_path = tool_input.get("save_path", "screenshot.png")

        if not url:
            return "Error: No URL provided"

        if not await _check_robots_txt(url):
            return f"Blocked by robots.txt: {url}"

        if not self._engine:
            return "Error: Browser engine not available"

        try:
            path = await self._engine.take_screenshot(url, save_path)
            return f"Screenshot saved to: {path}"
        except Exception as e:
            return f"Error taking screenshot of {url}: {e}"

    async def health_check(self) -> bool:
        return self.enabled and self._engine is not None
