from typing import Any, Literal

import httpx
from pydantic import BaseModel, Field

from app.core.config import settings


Freshness = Literal["noLimit", "oneDay", "oneWeek", "oneMonth", "oneYear"]


class WebSearchRequest(BaseModel):
    query: str = Field(..., min_length=1, description="搜索关键词")
    freshness: Freshness | str = Field(
        default="noLimit",
        description="时间范围：noLimit、oneDay、oneWeek、oneMonth、oneYear 或日期范围",
    )
    summary: bool = Field(default=True, description="是否返回网页摘要")
    count: int = Field(default=10, ge=1, le=50, description="返回结果数量")
    include: list[str] | None = Field(default=None, description="限定搜索域名")
    exclude: list[str] | None = Field(default=None, description="排除搜索域名")


class BochaSearchConfigError(RuntimeError):
    pass


class BochaSearchAPIError(RuntimeError):
    def __init__(self, status_code: int, detail: Any):
        super().__init__("Bocha Web Search API request failed")
        self.status_code = status_code
        self.detail = detail


def _build_payload(request: WebSearchRequest) -> dict[str, Any]:
    payload = request.model_dump(exclude_none=True)
    if request.include:
        payload["include"] = ",".join(request.include)
    if request.exclude:
        payload["exclude"] = ",".join(request.exclude)
    return payload


async def search_bocha_web(
    request: WebSearchRequest,
    *,
    client: httpx.AsyncClient | None = None,
) -> dict[str, Any]:
    if not settings.BOCHA_API_KEY:
        raise BochaSearchConfigError("BOCHA_API_KEY is not configured")

    headers = {
        "Authorization": f"Bearer {settings.BOCHA_API_KEY}",
        "Content-Type": "application/json",
    }
    payload = _build_payload(request)

    if client is not None:
        response = await client.post(
            settings.BOCHA_WEB_SEARCH_URL,
            headers=headers,
            json=payload,
        )
        return _parse_response(response)

    async with httpx.AsyncClient(
        timeout=settings.BOCHA_WEB_SEARCH_TIMEOUT_SECONDS
    ) as managed_client:
        response = await managed_client.post(
            settings.BOCHA_WEB_SEARCH_URL,
            headers=headers,
            json=payload,
        )
        return _parse_response(response)


def _parse_response(response: httpx.Response) -> dict[str, Any]:
    try:
        response.raise_for_status()
    except httpx.HTTPStatusError as exc:
        raise BochaSearchAPIError(
            exc.response.status_code,
            _response_detail(exc.response),
        ) from exc

    return response.json()


def _response_detail(response: httpx.Response) -> Any:
    try:
        return response.json()
    except ValueError:
        return response.text
