"""Nimbleway scraper provider implementation."""

from __future__ import annotations

import re
from typing import Any, Dict, List

from g_gremlin.nimbleway import NimblewayClient, extract_company_profile

from .scraper_provider import (
    SCRAPER_PROVIDER_REGISTRY,
    ScraperProvider,
    ToolCatalogEntry,
    ToolField,
    extract_inputs_from_row,
    filter_output_fields,
    run_test_generic,
)


def _extract_title_from_markdown(markdown: str) -> str:
    for line in markdown.splitlines():
        line = line.strip()
        if not line:
            continue
        if line.startswith("#"):
            return line.lstrip("#").strip()
        return line
    return ""


def _extract_title_from_html(html: str) -> str:
    match = re.search(r"<title>(.*?)</title>", html, flags=re.IGNORECASE | re.DOTALL)
    if not match:
        return ""
    return re.sub(r"\s+", " ", match.group(1)).strip()


def _html_to_text(html: str) -> str:
    text = re.sub(r"<[^>]+>", " ", html or "")
    return re.sub(r"\s+", " ", text).strip()


def _markdown_to_text(markdown: str) -> str:
    text = re.sub(r"!\[[^\]]*\]\([^)]*\)", " ", markdown or "")
    text = re.sub(r"\[([^\]]+)\]\([^)]*\)", r"\1", text)
    text = re.sub(r"`{1,3}", "", text)
    text = re.sub(r"^#+\s*", "", text, flags=re.MULTILINE)
    text = re.sub(r"[*_>~-]", " ", text)
    return re.sub(r"\s+", " ", text).strip()


def _extract_page_transform(response: Dict[str, Any], inputs: Dict[str, Any]) -> Dict[str, Any]:
    data = response.get("data") or {}
    markdown = str(data.get("markdown") or "")
    html = str(data.get("html") or "")
    title = _extract_title_from_markdown(markdown) or _extract_title_from_html(html) or str(inputs.get("url") or "")
    text = _markdown_to_text(markdown) or _html_to_text(html)
    return {
        "title": title,
        "text": text,
        "markdown": markdown,
    }


def _extract_company_site_transform(response: Dict[str, Any], inputs: Dict[str, Any]) -> Dict[str, Any]:
    profile = extract_company_profile(response, source_url=str(inputs.get("company_url") or ""))

    return {
        "company_name": str(profile.get("company_name") or ""),
        "description": str(profile.get("description") or ""),
        "category": str(profile.get("category") or ""),
    }


def _search_web_transform(response: Dict[str, Any], _inputs: Dict[str, Any]) -> Dict[str, Any]:
    results = response.get("results") or []
    first = results[0] if isinstance(results, list) and results else {}
    if not isinstance(first, dict):
        first = {}
    snippet = first.get("description") or first.get("content") or first.get("snippet") or ""
    return {
        "title": str(first.get("title") or ""),
        "url": str(first.get("url") or ""),
        "snippet": str(snippet or ""),
    }


NIMBLEWAY_TRANSFORMS = {
    "extract_page_transform": _extract_page_transform,
    "extract_company_site_transform": _extract_company_site_transform,
    "search_web_transform": _search_web_transform,
}


NIMBLEWAY_TOOL_CATALOG: Dict[str, ToolCatalogEntry] = {
    "extract_page": ToolCatalogEntry(
        id="extract_page",
        provider="nimbleway",
        display_name="Extract Page",
        description="Extract page title, clean text, and markdown from a URL.",
        provider_tool_id="extract",
        input_schema=[
            ToolField(key="url", label="URL", required=True, type="url"),
        ],
        output_schema=[
            ToolField(key="title", label="Title", default=True),
            ToolField(key="text", label="Text", default=True),
            ToolField(key="markdown", label="Markdown", default=False),
        ],
        output_column_prefix="Web_",
        transform="extract_page_transform",
    ),
    "extract_company_site": ToolCatalogEntry(
        id="extract_company_site",
        provider="nimbleway",
        display_name="Extract Company Site",
        description="Extract a structured company summary from a company website.",
        provider_tool_id="extract",
        input_schema=[
            ToolField(key="company_url", label="Company URL", required=True, type="url"),
        ],
        output_schema=[
            ToolField(key="company_name", label="Company Name", default=True),
            ToolField(key="description", label="Description", default=True),
            ToolField(key="category", label="Category", default=True),
        ],
        output_column_prefix="Company_",
        transform="extract_company_site_transform",
    ),
    "search_web": ToolCatalogEntry(
        id="search_web",
        provider="nimbleway",
        display_name="Search Web",
        description="Search the web and return the top result for each query.",
        provider_tool_id="search",
        input_schema=[
            ToolField(key="query", label="Query", required=True),
            ToolField(key="country", label="Country", required=False),
        ],
        output_schema=[
            ToolField(key="title", label="Title", default=True),
            ToolField(key="url", label="URL", default=True),
            ToolField(key="snippet", label="Snippet", default=True),
        ],
        output_column_prefix="Search_",
        transform="search_web_transform",
    ),
}


class NimblewayScraperProvider(ScraperProvider):
    provider_name = "nimbleway"

    def __init__(self, api_key: str | None = None) -> None:
        self.api_key = api_key or ""
        self._client = NimblewayClient(api_key=self.api_key) if self.api_key else None

    def list_tools(self) -> List[ToolCatalogEntry]:
        return list(NIMBLEWAY_TOOL_CATALOG.values())

    def get_tool(self, tool_id: str) -> ToolCatalogEntry:
        tool = NIMBLEWAY_TOOL_CATALOG.get(tool_id)
        if tool is None:
            raise ValueError(f"Unknown scraper tool: {tool_id}")
        return tool

    def validate_rows(
        self,
        rows: List[Dict[str, Any]],
        input_mapping: Dict[str, str],
        tool_id: str,
    ) -> Dict[str, int]:
        tool = self.get_tool(tool_id)
        valid = 0
        skipped = 0
        for row in rows:
            inputs = self.extract_inputs(row, input_mapping, tool)
            if all(inputs.get(key) for key in tool.required_keys()):
                valid += 1
            else:
                skipped += 1
        return {
            "rows_with_valid_inputs": valid,
            "rows_skipped": skipped,
            "estimated_credits": valid,
        }

    def extract_inputs(
        self,
        row: Dict[str, Any],
        input_mapping: Dict[str, str],
        tool: ToolCatalogEntry,
    ) -> Dict[str, Any]:
        return extract_inputs_from_row(row, input_mapping, tool.input_schema)

    def run_single(
        self,
        inputs: Dict[str, Any],
        tool: ToolCatalogEntry,
        output_fields: List[str],
    ) -> Dict[str, Any]:
        if self._client is None:
            raise ValueError("Nimbleway not connected")

        if tool.id == "extract_page":
            response = self._client.extract(
                url=str(inputs["url"]),
                formats=["html", "markdown"],
            )
        elif tool.id == "extract_company_site":
            company_url = str(inputs["company_url"])
            is_linkedin = "linkedin.com" in company_url.lower()
            response = self._client.extract(
                url=company_url,
                render=is_linkedin,
                formats=["html", "markdown"],
                request_timeout_ms=90000 if is_linkedin else 45000,
            )
        elif tool.id == "search_web":
            response = self._client.search(
                query=str(inputs["query"]),
                country=str(inputs["country"]).upper() if inputs.get("country") else None,
                max_results=1,
                deep_search=False,
                output_format="markdown",
            )
        else:
            raise ValueError(f"Unsupported Nimbleway tool: {tool.id}")

        transform = NIMBLEWAY_TRANSFORMS.get(tool.transform)
        if transform is None:
            raise ValueError(f"Missing transform {tool.transform}")
        result = transform(response, inputs)
        return filter_output_fields(result, output_fields)

    def run_test(
        self,
        rows: List[Dict[str, Any]],
        input_mapping: Dict[str, str],
        output_fields: List[str],
        tool_id: str,
        max_rows: int = 5,
    ) -> List[Dict[str, Any]]:
        return run_test_generic(
            self,
            rows=rows,
            input_mapping=input_mapping,
            output_fields=output_fields,
            tool_id=tool_id,
            max_rows=max_rows,
        )


SCRAPER_PROVIDER_REGISTRY.register("nimbleway", NimblewayScraperProvider)
