"""Structured data extraction tool for web pages."""

from __future__ import annotations

import json
from typing import Any

from madpod_agent.agent.tools.base import Tool
from madpod_agent.agent.tools.browser import BrowserBackend

# JavaScript executed in browser context to extract structured data
_EXTRACT_TABLES_JS = """
() => {
    const tables = [];
    for (const table of document.querySelectorAll('table')) {
        const headers = [];
        const rows = [];
        const headerCells = table.querySelectorAll('thead th, thead td, tr:first-child th');
        headerCells.forEach(cell => headers.push(cell.innerText.trim()));

        const bodyRows = table.querySelectorAll('tbody tr, tr');
        const startIdx = headers.length > 0 ? 0 : 0;
        bodyRows.forEach((tr, idx) => {
            // Skip header row if it was already captured
            if (idx === 0 && headers.length > 0 && tr.querySelector('th')) return;
            const cells = [];
            tr.querySelectorAll('td, th').forEach(cell => cells.push(cell.innerText.trim()));
            if (cells.length > 0) rows.push(cells);
        });

        if (rows.length > 0) {
            tables.push({ headers, rows, row_count: rows.length });
        }
    }
    return tables;
}
"""

_EXTRACT_LINKS_JS = """
(options) => {
    const { selector, limit } = options;
    const root = selector ? document.querySelector(selector) : document.body;
    if (!root) return [];
    const links = [];
    for (const a of root.querySelectorAll('a[href]')) {
        if (links.length >= limit) break;
        const href = a.href;
        if (!href || href.startsWith('javascript:') || href === '#') continue;
        links.push({
            text: (a.innerText || a.textContent || '').trim().slice(0, 200),
            href: href,
            title: a.getAttribute('title') || '',
        });
    }
    return links;
}
"""

_EXTRACT_FORMS_JS = """
() => {
    const forms = [];
    for (const form of document.querySelectorAll('form')) {
        const fields = [];
        for (const el of form.querySelectorAll('input, textarea, select')) {
            const field = {
                tag: el.tagName.toLowerCase(),
                type: el.getAttribute('type') || '',
                name: el.getAttribute('name') || '',
                id: el.getAttribute('id') || '',
                label: '',
                value: el.value || '',
                required: el.required,
                placeholder: el.getAttribute('placeholder') || '',
            };
            // Find associated label
            if (el.id) {
                const label = document.querySelector(`label[for="${el.id}"]`);
                if (label) field.label = label.innerText.trim();
            }
            if (!field.label) {
                const parent = el.closest('label');
                if (parent) field.label = parent.innerText.trim().slice(0, 100);
            }
            if (el.tagName === 'SELECT') {
                field.options = Array.from(el.options).map(o => ({
                    value: o.value,
                    text: o.text.trim(),
                    selected: o.selected,
                }));
            }
            fields.push(field);
        }
        forms.push({
            action: form.action || '',
            method: (form.method || 'GET').toUpperCase(),
            id: form.id || '',
            fields: fields,
        });
    }
    return forms;
}
"""

_EXTRACT_TEXT_CONTENT_JS = """
(options) => {
    const { selector, maxChars } = options;
    const root = selector ? document.querySelector(selector) : (document.querySelector('main') || document.querySelector('article') || document.body);
    if (!root) return { text: '', selector_found: false };
    const text = (root.innerText || root.textContent || '').trim();
    return {
        text: text.slice(0, maxChars),
        truncated: text.length > maxChars,
        chars: text.length,
        selector_found: true,
    };
}
"""

_EXTRACT_META_JS = """
() => {
    const meta = {};
    meta.title = document.title || '';
    meta.url = location.href;
    meta.description = (document.querySelector('meta[name="description"]') || {}).content || '';
    meta.og_title = (document.querySelector('meta[property="og:title"]') || {}).content || '';
    meta.og_description = (document.querySelector('meta[property="og:description"]') || {}).content || '';
    meta.og_image = (document.querySelector('meta[property="og:image"]') || {}).content || '';
    meta.canonical = (document.querySelector('link[rel="canonical"]') || {}).href || '';
    meta.language = document.documentElement.lang || '';
    return meta;
}
"""


class ExtractTablesTool(Tool):
    """Extract structured table data from the current browser page."""

    def __init__(self, backend: BrowserBackend):
        self._backend = backend

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

    @property
    def description(self) -> str:
        return (
            "Extract all HTML tables from the current browser page as structured JSON arrays. "
            "Each table includes headers and rows. Use this for scraping pricing tables, "
            "comparison charts, data listings, or any tabular content on a web page."
        )

    @property
    def parameters(self) -> dict[str, Any]:
        return {"type": "object", "properties": {}}

    async def execute(self, **kwargs: Any) -> str:
        result = await self._backend.execute(code=_EXTRACT_TABLES_JS)
        try:
            parsed = json.loads(result)
            tables = parsed.get("result", parsed) if isinstance(parsed, dict) else parsed
        except (json.JSONDecodeError, TypeError):
            tables = result
        return json.dumps({"ok": True, "tables": tables, "count": len(tables) if isinstance(tables, list) else 0})


class ExtractLinksTool(Tool):
    """Extract all links from the current browser page."""

    def __init__(self, backend: BrowserBackend):
        self._backend = backend

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

    @property
    def description(self) -> str:
        return (
            "Extract links from the current browser page. "
            "Optionally scope extraction to a CSS selector. "
            "Use for crawling, research, and finding resources on a page."
        )

    @property
    def parameters(self) -> dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "selector": {
                    "type": "string",
                    "description": "Optional CSS selector to scope link extraction (e.g. 'nav', '.sidebar')",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max links to extract (default 100)",
                    "minimum": 1,
                    "maximum": 500,
                },
            },
        }

    async def execute(self, selector: str | None = None, limit: int = 100, **kwargs: Any) -> str:
        code = f"({_EXTRACT_LINKS_JS})({{selector: {json.dumps(selector)}, limit: {limit}}})"
        result = await self._backend.execute(code=code)
        try:
            parsed = json.loads(result)
            links = parsed.get("result", parsed) if isinstance(parsed, dict) else parsed
        except (json.JSONDecodeError, TypeError):
            links = result
        return json.dumps({"ok": True, "links": links, "count": len(links) if isinstance(links, list) else 0})


class ExtractFormsTool(Tool):
    """Extract form structure from the current browser page."""

    def __init__(self, backend: BrowserBackend):
        self._backend = backend

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

    @property
    def description(self) -> str:
        return (
            "Extract all forms and their fields from the current browser page. "
            "Returns form actions, methods, and field details (name, type, label, options for selects). "
            "Use this before filling forms to understand the field structure."
        )

    @property
    def parameters(self) -> dict[str, Any]:
        return {"type": "object", "properties": {}}

    async def execute(self, **kwargs: Any) -> str:
        result = await self._backend.execute(code=_EXTRACT_FORMS_JS)
        try:
            parsed = json.loads(result)
            forms = parsed.get("result", parsed) if isinstance(parsed, dict) else parsed
        except (json.JSONDecodeError, TypeError):
            forms = result
        return json.dumps({"ok": True, "forms": forms, "count": len(forms) if isinstance(forms, list) else 0})


class ExtractTextTool(Tool):
    """Extract text content from a specific area of the page."""

    def __init__(self, backend: BrowserBackend):
        self._backend = backend

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

    @property
    def description(self) -> str:
        return (
            "Extract the visible text content from the current browser page or a specific element. "
            "More thorough than browser_snapshot's text excerpt — returns full text from main/article/body. "
            "Use when you need the complete text of an article, product description, or page section."
        )

    @property
    def parameters(self) -> dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "selector": {
                    "type": "string",
                    "description": "Optional CSS selector to scope text extraction (e.g. 'article', '.content')",
                },
                "max_chars": {
                    "type": "integer",
                    "description": "Max characters (default 50000)",
                    "minimum": 100,
                    "maximum": 200000,
                },
            },
        }

    async def execute(self, selector: str | None = None, max_chars: int = 50000, **kwargs: Any) -> str:
        code = f"({_EXTRACT_TEXT_CONTENT_JS})({{selector: {json.dumps(selector)}, maxChars: {max_chars}}})"
        result = await self._backend.execute(code=code)
        try:
            parsed = json.loads(result)
            data = parsed.get("result", parsed) if isinstance(parsed, dict) else parsed
        except (json.JSONDecodeError, TypeError):
            data = {"text": str(result)}
        return json.dumps({"ok": True, **data} if isinstance(data, dict) else {"ok": True, "text": str(data)})


class ExtractMetaTool(Tool):
    """Extract page metadata (title, description, OG tags)."""

    def __init__(self, backend: BrowserBackend):
        self._backend = backend

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

    @property
    def description(self) -> str:
        return (
            "Extract metadata from the current browser page: title, description, Open Graph tags, "
            "canonical URL, and language. Useful for SEO analysis, content indexing, and page identification."
        )

    @property
    def parameters(self) -> dict[str, Any]:
        return {"type": "object", "properties": {}}

    async def execute(self, **kwargs: Any) -> str:
        result = await self._backend.execute(code=_EXTRACT_META_JS)
        try:
            parsed = json.loads(result)
            meta = parsed.get("result", parsed) if isinstance(parsed, dict) else parsed
        except (json.JSONDecodeError, TypeError):
            meta = result
        return json.dumps({"ok": True, "meta": meta})


def register_extract_tools(
    registry: "ToolRegistry",
    backend: BrowserBackend,
) -> None:
    """Register all extraction tools."""
    for tool_cls in (
        ExtractTablesTool,
        ExtractLinksTool,
        ExtractFormsTool,
        ExtractTextTool,
        ExtractMetaTool,
    ):
        tool = tool_cls(backend)
        registry.register(tool)
