"""Web search resource for CMDOP LLM service."""

from __future__ import annotations

from typing import TYPE_CHECKING, Optional, Union

import httpx
from openai._compat import cached_property
from openai._resource import AsyncAPIResource, SyncAPIResource
from openai._types import NOT_GIVEN, Body, Headers, NotGiven, Query

from ..types.search import UserLocation, WebSearchResponse

if TYPE_CHECKING:
    from .._client import AsyncCmdopLLM, CmdopLLM

__all__ = ["Search", "AsyncSearch"]


class Search(SyncAPIResource):
    """Web search resource.

    Provides web search capabilities using Anthropic's web_search tool.

    Example:
        ```python
        from cmdop_llm import CmdopLLM

        client = CmdopLLM()

        # Basic search
        result = client.search.web("What is Python?")
        print(result.content)
        for citation in result.citations:
            print(f"- {citation.title}: {citation.url}")

        # Search with options
        result = client.search.web(
            query="Latest news about AI",
            max_searches=5,
            allowed_domains=["bbc.com", "cnn.com"],
        )

        # Fetch and analyze URL
        result = client.search.fetch(
            url="https://example.com/article",
            prompt="Summarize the main points"
        )
        ```
    """

    _client: CmdopLLM

    @cached_property
    def with_raw_response(self) -> SearchWithRawResponse:
        """Access raw HTTP response data."""
        return SearchWithRawResponse(self)

    def web(
        self,
        query: str,
        *,
        model: str = "claude-3-5-haiku-20241022",
        max_tokens: int = 1024,
        max_searches: int = 5,
        allowed_domains: Optional[list[str]] = None,
        blocked_domains: Optional[list[str]] = None,
        user_location: Optional[UserLocation] = None,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN,
    ) -> WebSearchResponse:
        """Search the web and get AI-summarized results with citations.

        Args:
            query: Search query (1-2000 characters).
            model: Model to use. Defaults to claude-3-5-haiku-20241022.
            max_tokens: Maximum tokens in response (1-4096).
            max_searches: Maximum web searches to perform (1-10).
            allowed_domains: Restrict search to these domains only.
            blocked_domains: Exclude these domains from search.
            user_location: User location for localized results.
            extra_headers: Additional headers to send.
            extra_query: Additional query parameters.
            extra_body: Additional body parameters.
            timeout: Request timeout.

        Returns:
            WebSearchResponse with content, citations, and usage info.

        Raises:
            openai.APIError: If the request fails.
        """
        body: dict = {
            "query": query,
            "model": model,
            "max_tokens": max_tokens,
            "max_searches": max_searches,
        }

        if allowed_domains is not None:
            body["allowed_domains"] = allowed_domains
        if blocked_domains is not None:
            body["blocked_domains"] = blocked_domains
        if user_location is not None:
            body["user_location"] = user_location.model_dump(exclude_none=True)

        return self._post(
            "/search",
            body=body,
            options={
                "extra_headers": extra_headers,
                "extra_query": extra_query,
                "extra_body": extra_body,
                "timeout": timeout,
            },
            cast_to=WebSearchResponse,
        )

    def fetch(
        self,
        url: str,
        *,
        prompt: str = "Summarize this page",
        model: str = "claude-3-5-haiku-20241022",
        max_tokens: int = 1024,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN,
    ) -> WebSearchResponse:
        """Fetch and analyze content from a URL.

        Args:
            url: URL to fetch and analyze.
            prompt: Prompt for analyzing the content.
            model: Model to use. Defaults to claude-3-5-haiku-20241022.
            max_tokens: Maximum tokens in response (1-4096).
            extra_headers: Additional headers to send.
            extra_query: Additional query parameters.
            extra_body: Additional body parameters.
            timeout: Request timeout.

        Returns:
            WebSearchResponse with analyzed content.

        Raises:
            openai.APIError: If the request fails.
        """
        body = {
            "url": url,
            "prompt": prompt,
            "model": model,
            "max_tokens": max_tokens,
        }

        return self._post(
            "/search/fetch",
            body=body,
            options={
                "extra_headers": extra_headers,
                "extra_query": extra_query,
                "extra_body": extra_body,
                "timeout": timeout,
            },
            cast_to=WebSearchResponse,
        )


class AsyncSearch(AsyncAPIResource):
    """Async web search resource."""

    _client: AsyncCmdopLLM

    @cached_property
    def with_raw_response(self) -> AsyncSearchWithRawResponse:
        """Access raw HTTP response data."""
        return AsyncSearchWithRawResponse(self)

    async def web(
        self,
        query: str,
        *,
        model: str = "claude-3-5-haiku-20241022",
        max_tokens: int = 1024,
        max_searches: int = 5,
        allowed_domains: Optional[list[str]] = None,
        blocked_domains: Optional[list[str]] = None,
        user_location: Optional[UserLocation] = None,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN,
    ) -> WebSearchResponse:
        """Search the web and get AI-summarized results with citations (async).

        See Search.web for full documentation.
        """
        body: dict = {
            "query": query,
            "model": model,
            "max_tokens": max_tokens,
            "max_searches": max_searches,
        }

        if allowed_domains is not None:
            body["allowed_domains"] = allowed_domains
        if blocked_domains is not None:
            body["blocked_domains"] = blocked_domains
        if user_location is not None:
            body["user_location"] = user_location.model_dump(exclude_none=True)

        return await self._post(
            "/search",
            body=body,
            options={
                "extra_headers": extra_headers,
                "extra_query": extra_query,
                "extra_body": extra_body,
                "timeout": timeout,
            },
            cast_to=WebSearchResponse,
        )

    async def fetch(
        self,
        url: str,
        *,
        prompt: str = "Summarize this page",
        model: str = "claude-3-5-haiku-20241022",
        max_tokens: int = 1024,
        extra_headers: Headers | None = None,
        extra_query: Query | None = None,
        extra_body: Body | None = None,
        timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN,
    ) -> WebSearchResponse:
        """Fetch and analyze content from a URL (async).

        See Search.fetch for full documentation.
        """
        body = {
            "url": url,
            "prompt": prompt,
            "model": model,
            "max_tokens": max_tokens,
        }

        return await self._post(
            "/search/fetch",
            body=body,
            options={
                "extra_headers": extra_headers,
                "extra_query": extra_query,
                "extra_body": extra_body,
                "timeout": timeout,
            },
            cast_to=WebSearchResponse,
        )


class SearchWithRawResponse:
    """Raw response wrapper for Search resource."""

    def __init__(self, search: Search) -> None:
        self._search = search

    def web(self, query: str, **kwargs) -> httpx.Response:  # type: ignore
        """Get raw response from web search."""
        kwargs["query"] = query
        return self._search._client.with_raw_response._post(  # type: ignore
            "/search",
            body=kwargs,
        )

    def fetch(self, url: str, **kwargs) -> httpx.Response:  # type: ignore
        """Get raw response from fetch."""
        kwargs["url"] = url
        return self._search._client.with_raw_response._post(  # type: ignore
            "/search/fetch",
            body=kwargs,
        )


class AsyncSearchWithRawResponse:
    """Raw response wrapper for AsyncSearch resource."""

    def __init__(self, search: AsyncSearch) -> None:
        self._search = search

    async def web(self, query: str, **kwargs) -> httpx.Response:  # type: ignore
        """Get raw response from web search."""
        kwargs["query"] = query
        return await self._search._client.with_raw_response._post(  # type: ignore
            "/search",
            body=kwargs,
        )

    async def fetch(self, url: str, **kwargs) -> httpx.Response:  # type: ignore
        """Get raw response from fetch."""
        kwargs["url"] = url
        return await self._search._client.with_raw_response._post(  # type: ignore
            "/search/fetch",
            body=kwargs,
        )
