"""FastAPI application for DDGS API."""

import asyncio
import logging
import os
from typing import Any

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field

from ddgs import DDGS
from ddgs.utils import _expand_proxy_tb_alias

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Create FastAPI app
app = FastAPI(
    title="DDGS API",
    description="A FastAPI wrapper for the DDGS (Dux Distributed Global Search) library",
    version="1.0.0",
    docs_url="/docs",
    redoc_url="/redoc",
)

# Add CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# Pydantic models for request/response
class TextSearchRequest(BaseModel):
    """Request model for search operations."""

    query: str = Field(..., description="Search query")
    region: str = Field("us-en", description="Region for search (e.g., us-en, uk-en, ru-ru)")
    safesearch: str = Field("moderate", description="Safe search setting (on, moderate, off)")
    timelimit: str | None = Field(None, description="Time limit (d, w, m, y) or custom date range")
    max_results: int | None = Field(10, description="Maximum number of results to return")
    page: int = Field(1, description="Page number of results")
    backend: str = Field("auto", description="Search backend (auto, or specific engine)")


class ImagesSearchRequest(BaseModel):
    """Request model for image search operations."""

    query: str = Field(..., description="Image search query")
    region: str = Field("us-en", description="Region for search (e.g., us-en, uk-en, ru-ru)")
    safesearch: str = Field("moderate", description="Safe search setting (on, moderate, off)")
    timelimit: str | None = Field(None, description="Time limit (d, w, m, y) or custom date range")
    max_results: int | None = Field(10, description="Maximum number of results to return")
    page: int = Field(1, description="Page number of results")
    backend: str = Field("auto", description="Search backend (auto, or specific engine)")
    size: str | None = Field(None, description="Image size (Small, Medium, Large, Wallpaper)")
    color: str | None = Field(
        None,
        description="Image color (Monochrome, Red, Orange, Yellow, Green, Blue, Purple, Pink, Brown, Black, Gray, Teal, White)",  # noqa: E501
    )
    type_image: str | None = Field(None, description="Image type (photo, clipart, gif, transparent, line)")
    layout: str | None = Field(None, description="Image layout (Square, Tall, Wide)")
    license_image: str | None = Field(
        None, description="Image license (any, Public, Share, ShareCommercially, Modify, ModifyCommercially)"
    )


class NewsSearchRequest(BaseModel):
    """Request model for search operations."""

    query: str = Field(..., description="Search query")
    region: str = Field("us-en", description="Region for search (e.g., us-en, uk-en, ru-ru)")
    safesearch: str = Field("moderate", description="Safe search setting (on, moderate, off)")
    timelimit: str | None = Field(None, description="Time limit (d, w, m, y) or custom date range")
    max_results: int | None = Field(10, description="Maximum number of results to return")
    page: int = Field(1, description="Page number of results")
    backend: str = Field("auto", description="Search backend (auto, or specific engine)")


class VideosSearchRequest(BaseModel):
    """Request model for video search operations."""

    query: str = Field(..., description="Video search query")
    region: str = Field("us-en", description="Region for search (e.g., us-en, uk-en, ru-ru)")
    safesearch: str = Field("moderate", description="Safe search setting (on, moderate, off)")
    timelimit: str | None = Field(None, description="Time limit (d, w, m) or custom date range")
    max_results: int | None = Field(10, description="Maximum number of results to return")
    page: int = Field(1, description="Page number of results")
    backend: str = Field("auto", description="Search backend (auto, or specific engine)")
    resolution: str | None = Field(None, description="Video resolution (high, standard)")
    duration: str | None = Field(None, description="Video duration (short, medium, long)")
    license_videos: str | None = Field(None, description="Video license (creativeCommon, youtube)")


class BooksSearchRequest(BaseModel):
    """Request model for book search operations."""

    query: str = Field(..., description="Books search query")
    max_results: int | None = Field(10, description="Maximum number of results to return")
    page: int = Field(1, description="Page number of results")
    backend: str = Field("auto", description="Search backend (auto, or specific engine)")


class SearchResponse(BaseModel):
    """Response model for search operations."""

    results: list[dict[str, Any]]


class HealthResponse(BaseModel):
    """Response model for health check."""

    status: str
    version: str
    service: str


@app.get("/", response_model=HealthResponse)
async def root() -> HealthResponse:
    """Root endpoint with basic service information."""
    return HealthResponse(status="healthy", version="1.0.0", service="DDGS API")


@app.get("/health", response_model=HealthResponse)
async def health_check() -> HealthResponse:
    """Health check endpoint."""
    return HealthResponse(status="healthy", version="1.0.0", service="DDGS API")


@app.post("/search/text", response_model=SearchResponse)
async def search_text(request: TextSearchRequest) -> SearchResponse:
    """Perform a text search."""
    try:
        results = await asyncio.to_thread(
            lambda: DDGS(proxy=_expand_proxy_tb_alias(os.environ.get("DDGS_PROXY"))).text(
                query=request.query,
                region=request.region,
                safesearch=request.safesearch,
                timelimit=request.timelimit,
                max_results=request.max_results,
                page=request.page,
                backend=request.backend,
            )
        )

        return SearchResponse(results=results)
    except Exception as e:
        logger.warning("Error in text search: %s", e)
        raise HTTPException(status_code=500, detail=f"Search failed: {e!s}") from e


@app.get("/search/text", response_model=SearchResponse)
async def search_text_get(
    query: str,
    region: str = "us-en",
    safesearch: str = "moderate",
    timelimit: str | None = None,
    max_results: int = 10,
    page: int = 1,
    backend: str = "auto",
) -> SearchResponse:
    """Perform a text search via GET request."""
    try:
        results = await asyncio.to_thread(
            lambda: DDGS(proxy=_expand_proxy_tb_alias(os.environ.get("DDGS_PROXY"))).text(
                query=query,
                region=region,
                safesearch=safesearch,
                timelimit=timelimit,
                max_results=max_results,
                page=page,
                backend=backend,
            )
        )

        return SearchResponse(results=results)
    except Exception as e:
        logger.warning("Error in text search (GET): %s", e)
        raise HTTPException(status_code=500, detail=f"Search failed: {e!s}") from e


@app.post("/search/images", response_model=SearchResponse)
async def search_images(request: ImagesSearchRequest) -> SearchResponse:
    """Perform an image search."""
    try:
        results = await asyncio.to_thread(
            lambda: DDGS(proxy=_expand_proxy_tb_alias(os.environ.get("DDGS_PROXY"))).images(
                query=request.query,
                region=request.region,
                safesearch=request.safesearch,
                timelimit=request.timelimit,
                max_results=request.max_results,
                page=request.page,
                backend=request.backend,
                size=request.size,
                color=request.color,
                type_image=request.type_image,
                layout=request.layout,
                license_image=request.license_image,
            )
        )

        return SearchResponse(results=results)
    except Exception as e:
        logger.warning("Error in image search: %s", e)
        raise HTTPException(status_code=500, detail=f"Image search failed: {e!s}") from e


@app.get("/search/images", response_model=SearchResponse)
async def search_images_get(
    query: str,
    region: str = "us-en",
    safesearch: str = "moderate",
    timelimit: str | None = None,
    max_results: int = 10,
    page: int = 1,
    backend: str = "auto",
    size: str | None = None,
    color: str | None = None,
    type_image: str | None = None,
    layout: str | None = None,
    license_image: str | None = None,
) -> SearchResponse:
    """Perform an image search via GET request."""
    try:
        results = await asyncio.to_thread(
            lambda: DDGS(proxy=_expand_proxy_tb_alias(os.environ.get("DDGS_PROXY"))).images(
                query=query,
                region=region,
                safesearch=safesearch,
                timelimit=timelimit,
                max_results=max_results,
                page=page,
                backend=backend,
                size=size,
                color=color,
                type_image=type_image,
                layout=layout,
                license_image=license_image,
            )
        )

        return SearchResponse(results=results)
    except Exception as e:
        logger.warning("Error in image search (GET): %s", e)
        raise HTTPException(status_code=500, detail=f"Image search failed: {e!s}") from e


@app.post("/search/news", response_model=SearchResponse)
async def search_news(request: NewsSearchRequest) -> SearchResponse:
    """Perform a news search."""
    try:
        results = await asyncio.to_thread(
            lambda: DDGS(proxy=_expand_proxy_tb_alias(os.environ.get("DDGS_PROXY"))).news(
                query=request.query,
                region=request.region,
                safesearch=request.safesearch,
                timelimit=request.timelimit,
                max_results=request.max_results,
                page=request.page,
                backend=request.backend,
            )
        )

        return SearchResponse(results=results)
    except Exception as e:
        logger.warning("Error in news search: %s", e)
        raise HTTPException(status_code=500, detail=f"News search failed: {e!s}") from e


@app.get("/search/news", response_model=SearchResponse)
async def search_news_get(
    query: str,
    region: str = "us-en",
    safesearch: str = "moderate",
    timelimit: str | None = None,
    max_results: int = 10,
    page: int = 1,
    backend: str = "auto",
) -> SearchResponse:
    """Perform a news search via GET request."""
    try:
        results = await asyncio.to_thread(
            lambda: DDGS(proxy=_expand_proxy_tb_alias(os.environ.get("DDGS_PROXY"))).news(
                query=query,
                region=region,
                safesearch=safesearch,
                timelimit=timelimit,
                max_results=max_results,
                page=page,
                backend=backend,
            )
        )

        return SearchResponse(results=results)
    except Exception as e:
        logger.warning("Error in news search (GET): %s", e)
        raise HTTPException(status_code=500, detail=f"News search failed: {e!s}") from e


@app.post("/search/videos", response_model=SearchResponse)
async def search_videos(request: VideosSearchRequest) -> SearchResponse:
    """Perform a video search."""
    try:
        results = await asyncio.to_thread(
            lambda: DDGS(proxy=_expand_proxy_tb_alias(os.environ.get("DDGS_PROXY"))).videos(
                query=request.query,
                region=request.region,
                safesearch=request.safesearch,
                timelimit=request.timelimit,
                max_results=request.max_results,
                page=request.page,
                backend=request.backend,
                resolution=request.resolution,
                duration=request.duration,
                license_videos=request.license_videos,
            )
        )

        return SearchResponse(results=results)
    except Exception as e:
        logger.warning("Error in video search: %s", e)
        raise HTTPException(status_code=500, detail=f"Video search failed: {e!s}") from e


@app.get("/search/videos", response_model=SearchResponse)
async def search_videos_get(
    query: str,
    region: str = "us-en",
    safesearch: str = "moderate",
    timelimit: str | None = None,
    max_results: int = 10,
    page: int = 1,
    backend: str = "auto",
    resolution: str | None = None,
    duration: str | None = None,
    license_videos: str | None = None,
) -> SearchResponse:
    """Perform a video search via GET request."""
    try:
        results = await asyncio.to_thread(
            lambda: DDGS(proxy=_expand_proxy_tb_alias(os.environ.get("DDGS_PROXY"))).videos(
                query=query,
                region=region,
                safesearch=safesearch,
                timelimit=timelimit,
                max_results=max_results,
                page=page,
                backend=backend,
                resolution=resolution,
                duration=duration,
                license_videos=license_videos,
            )
        )

        return SearchResponse(results=results)
    except Exception as e:
        logger.warning("Error in video search (GET): %s", e)
        raise HTTPException(status_code=500, detail=f"Video search failed: {e!s}") from e


@app.post("/search/books", response_model=SearchResponse)
async def search_books(request: BooksSearchRequest) -> SearchResponse:
    """Perform a book search."""
    try:
        results = await asyncio.to_thread(
            lambda: DDGS(proxy=_expand_proxy_tb_alias(os.environ.get("DDGS_PROXY"))).books(
                query=request.query,
                max_results=request.max_results,
                page=request.page,
                backend=request.backend,
            )
        )

        return SearchResponse(results=results)
    except Exception as e:
        logger.warning("Error in book search: %s", e)
        raise HTTPException(status_code=500, detail=f"Book search failed: {e!s}") from e


@app.get("/search/books", response_model=SearchResponse)
async def search_books_get(
    query: str,
    max_results: int = 10,
    page: int = 1,
    backend: str = "auto",
) -> SearchResponse:
    """Perform a book search via GET request."""
    try:
        results = await asyncio.to_thread(
            lambda: DDGS(proxy=_expand_proxy_tb_alias(os.environ.get("DDGS_PROXY"))).books(
                query=query,
                max_results=max_results,
                page=page,
                backend=backend,
            )
        )

        return SearchResponse(results=results)
    except Exception as e:
        logger.warning("Error in book search (GET): %s", e)
        raise HTTPException(status_code=500, detail=f"Book search failed: {e!s}") from e


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8000)  # noqa: S104
