"""Web tools for Crustdata MCP Server."""

import re
from typing import Optional

from mcp.server.fastmcp import Context
from pydantic import BaseModel, Field, field_validator, model_validator

from ..client import CrustdataAPIError, CrustdataAuthError
from ..client_factory import get_client_for_request
from ..models.common import WebSearchSource
from ..server import AppContext, mcp


class WebSearchInput(BaseModel):
    """Input parameters for web search."""

    query: str = Field(
        ...,
        max_length=1000,
        description="Search query string.",
    )
    limit: int = Field(
        10,
        ge=1,
        le=50,
        description="Maximum number of results to return (default: 10, max: 50).",
    )
    geolocation: Optional[str] = Field(
        None,
        description="Geographic location for search results (e.g., 'US', 'GB'). ISO 3166-1 alpha-2 country code.",
    )
    sources: Optional[list[str]] = Field(
        None,
        description="Specific sources to search. Valid values: news, web, scholar-articles, scholar-articles-enriched, scholar-author, ai, social",
    )
    site: Optional[str] = Field(
        None,
        description="Limit search to a specific site (e.g., 'linkedin.com').",
    )
    start_date: Optional[int] = Field(
        None,
        description="Start date for results (Unix timestamp).",
    )
    end_date: Optional[int] = Field(
        None,
        description="End date for results (Unix timestamp).",
    )
    fetch_content: bool = Field(
        False,
        description="Whether to fetch and include page content in results.",
    )

    @field_validator("geolocation")
    @classmethod
    def validate_geolocation(cls, v: Optional[str]) -> Optional[str]:
        """Validate geolocation is ISO 3166-1 alpha-2 country code."""
        if v is None:
            return v
        if not re.match(r"^[A-Z]{2}$", v):
            raise ValueError(
                f"Invalid geolocation: {v}. Must be ISO 3166-1 alpha-2 country code (2 uppercase letters, e.g., 'US', 'GB')"
            )
        return v

    @field_validator("sources")
    @classmethod
    def validate_sources(cls, v: Optional[list[str]]) -> Optional[list[str]]:
        """Validate sources are from allowed WebSearchSource enum."""
        if v is None:
            return v
        valid_sources = {source.value for source in WebSearchSource}
        for source in v:
            if source not in valid_sources:
                raise ValueError(
                    f"Invalid source: {source}. Must be one of: {', '.join(sorted(valid_sources))}"
                )
        return v

    @model_validator(mode="after")
    def validate_date_range(self) -> "WebSearchInput":
        """Validate start_date < end_date if both provided."""
        if self.start_date is not None and self.end_date is not None:
            if self.start_date >= self.end_date:
                raise ValueError(
                    f"start_date ({self.start_date}) must be less than end_date ({self.end_date})"
                )
        return self


@mcp.tool(
    name="crustdata_web_search",
    description="Search the web for information about companies, people, or topics. "
    "Returns search results with titles, URLs, and snippets. Costs 1 credit per search.",
)
async def crustdata_web_search(
    params: WebSearchInput,
    ctx: Context[None, AppContext, None],
) -> dict:
    """Search the web for information.

    Args:
        params: Web search parameters.
        ctx: MCP context for logging and client access.

    Returns:
        Dictionary with search results including:
        - results: List of search results with title, url, snippet
        - query: The original search query
        - metadata: Additional search metadata
    """
    client = get_client_for_request()

    data: dict = {"query": params.query, "limit": params.limit}
    if params.geolocation:
        data["geolocation"] = params.geolocation
    if params.sources:
        data["sources"] = params.sources
    if params.site:
        data["site"] = params.site
    if params.start_date:
        data["startDate"] = params.start_date
    if params.end_date:
        data["endDate"] = params.end_date
    if params.fetch_content:
        data["fetch_content"] = params.fetch_content

    try:
        ctx.info(f"Web search for: {params.query}")
        result = await client.post("/screener/web-search", data=data)

        results = result.get("results", [])
        ctx.info(f"Found {len(results)} search results")
        return {
            "results": results,
            "query": result.get("query"),
            "metadata": result.get("metadata"),
            "total_results": len(results),
        }

    except CrustdataAuthError as e:
        ctx.error(f"Authentication failed: {e}")
        raise
    except CrustdataAPIError as e:
        ctx.error(f"API error: {e}")
        raise


class WebFetchInput(BaseModel):
    """Input parameters for web fetch."""

    urls: list[str] = Field(
        ...,
        max_length=10,
        description="URLs to fetch content from (max 10).",
    )
    extract_text: bool = Field(
        True,
        description="Whether to extract and return text content. Default: true.",
    )

    @field_validator("urls")
    @classmethod
    def validate_urls(cls, v: list[str]) -> list[str]:
        """Validate URLs start with http:// or https://."""
        for url in v:
            if not url.startswith(("http://", "https://")):
                raise ValueError(
                    f"Invalid URL: {url}. URLs must start with http:// or https://"
                )
        return v


@mcp.tool(
    name="crustdata_web_fetch",
    description="Fetch and extract content from a web page URL. "
    "Useful for getting detailed information from specific pages.",
)
async def crustdata_web_fetch(
    params: WebFetchInput,
    ctx: Context[None, AppContext, None],
) -> dict:
    """Fetch content from web URLs.

    Args:
        params: Web fetch parameters.
        ctx: MCP context for logging and client access.

    Returns:
        Dictionary with:
        - content: Extracted text content (if extract_text=true)
        - html: Raw HTML content
        - metadata: Page metadata (title, description, etc.)
    """
    client = get_client_for_request()

    data = {"urls": params.urls, "extract_text": params.extract_text}

    try:
        ctx.info(f"Fetching content from {len(params.urls)} URL(s): {', '.join(params.urls)}")
        result = await client.post("/screener/web-fetch", data=data)
        ctx.info("Content fetched successfully")
        return result

    except CrustdataAuthError as e:
        ctx.error(f"Authentication failed: {e}")
        raise
    except CrustdataAPIError as e:
        ctx.error(f"API error: {e}")
        raise
