from __future__ import annotations

from typing import TYPE_CHECKING, Any, Dict, List, Optional

from cardog.types.listings import (
    Listing,
    ListingCountResponse,
    ListingFacetsResponse,
    ListingSearchResponse,
)

if TYPE_CHECKING:
    from cardog._base_client import AsyncAPIClient, SyncAPIClient


def _build_search_params(
    makes: Optional[List[str]] = None,
    models: Optional[Dict[str, List[str]]] = None,
    trims: Optional[List[str]] = None,
    year_min: Optional[int] = None,
    year_max: Optional[int] = None,
    price_min: Optional[float] = None,
    price_max: Optional[float] = None,
    odometer_min: Optional[int] = None,
    odometer_max: Optional[int] = None,
    body_styles: Optional[List[str]] = None,
    fuel_types: Optional[List[str]] = None,
    exterior_colors: Optional[List[str]] = None,
    interior_colors: Optional[List[str]] = None,
    sale_types: Optional[List[str]] = None,
    sellers: Optional[List[str]] = None,
    lat: Optional[float] = None,
    lng: Optional[float] = None,
    radius: Optional[float] = None,
    sort_field: Optional[str] = None,
    sort_direction: Optional[str] = None,
    page: Optional[int] = None,
    limit: Optional[int] = None,
) -> Dict[str, Any]:
    params: Dict[str, Any] = {}
    if makes:
        params["makes"] = ",".join(makes)
    if models:
        for make, model_list in models.items():
            params[f"models[{make}]"] = ",".join(model_list)
    if trims:
        params["trims"] = ",".join(trims)
    if year_min is not None:
        params["yearMin"] = year_min
    if year_max is not None:
        params["yearMax"] = year_max
    if price_min is not None:
        params["priceMin"] = price_min
    if price_max is not None:
        params["priceMax"] = price_max
    if odometer_min is not None:
        params["odometerMin"] = odometer_min
    if odometer_max is not None:
        params["odometerMax"] = odometer_max
    if body_styles:
        params["bodyStyles"] = ",".join(body_styles)
    if fuel_types:
        params["fuelTypes"] = ",".join(fuel_types)
    if exterior_colors:
        params["exteriorColors"] = ",".join(exterior_colors)
    if interior_colors:
        params["interiorColors"] = ",".join(interior_colors)
    if sale_types:
        params["saleTypes"] = ",".join(sale_types)
    if sellers:
        params["sellers"] = ",".join(sellers)
    if lat is not None:
        params["lat"] = lat
    if lng is not None:
        params["lng"] = lng
    if radius is not None:
        params["radius"] = radius
    if sort_field:
        params["sort"] = sort_field
    if sort_direction:
        params["order"] = sort_direction
    if page is not None:
        params["page"] = page
    if limit is not None:
        params["limit"] = limit
    return params


class SyncListingsResource:
    """Vehicle listings search and retrieval."""

    def __init__(self, client: SyncAPIClient) -> None:
        self._client = client

    def search(
        self,
        *,
        makes: Optional[List[str]] = None,
        models: Optional[Dict[str, List[str]]] = None,
        trims: Optional[List[str]] = None,
        year_min: Optional[int] = None,
        year_max: Optional[int] = None,
        price_min: Optional[float] = None,
        price_max: Optional[float] = None,
        odometer_min: Optional[int] = None,
        odometer_max: Optional[int] = None,
        body_styles: Optional[List[str]] = None,
        fuel_types: Optional[List[str]] = None,
        exterior_colors: Optional[List[str]] = None,
        interior_colors: Optional[List[str]] = None,
        sale_types: Optional[List[str]] = None,
        sellers: Optional[List[str]] = None,
        lat: Optional[float] = None,
        lng: Optional[float] = None,
        radius: Optional[float] = None,
        sort_field: Optional[str] = None,
        sort_direction: Optional[str] = None,
        page: Optional[int] = None,
        limit: Optional[int] = None,
    ) -> ListingSearchResponse:
        """Search vehicle listings with filters.

        Args:
            makes: Filter by vehicle makes (e.g., ["Toyota", "Honda"]).
            models: Filter by models per make (e.g., {"Toyota": ["Camry", "Corolla"]}).
            trims: Filter by trims.
            year_min: Minimum model year.
            year_max: Maximum model year.
            price_min: Minimum price.
            price_max: Maximum price.
            odometer_min: Minimum odometer reading.
            odometer_max: Maximum odometer reading.
            body_styles: Filter by body style (e.g., ["SUV", "Sedan"]).
            fuel_types: Filter by fuel type (e.g., ["Gasoline", "Electric"]).
            exterior_colors: Filter by exterior color.
            interior_colors: Filter by interior color.
            sale_types: Filter by sale type (e.g., ["new", "used"]).
            sellers: Filter by seller IDs.
            lat: Latitude for location-based search.
            lng: Longitude for location-based search.
            radius: Search radius in km.
            sort_field: Sort field (price, year, odometer, createdAt, random, score).
            sort_direction: Sort direction (asc, desc).
            page: Page number (starts at 1).
            limit: Results per page (max 250).
        """
        params = _build_search_params(**{k: v for k, v in locals().items() if k != "self"})
        return self._client._get("/listings/search", params=params, model=ListingSearchResponse)

    def count(
        self,
        *,
        makes: Optional[List[str]] = None,
        models: Optional[Dict[str, List[str]]] = None,
        year_min: Optional[int] = None,
        year_max: Optional[int] = None,
        price_min: Optional[float] = None,
        price_max: Optional[float] = None,
        body_styles: Optional[List[str]] = None,
        fuel_types: Optional[List[str]] = None,
    ) -> ListingCountResponse:
        """Count listings matching the given filters."""
        params = _build_search_params(
            makes=makes, models=models, year_min=year_min, year_max=year_max,
            price_min=price_min, price_max=price_max,
            body_styles=body_styles, fuel_types=fuel_types,
        )
        return self._client._get("/listings/count", params=params, model=ListingCountResponse)

    def get_by_id(self, listing_id: str) -> Listing:
        """Get a listing by its ID.

        Args:
            listing_id: The listing ID.
        """
        return self._client._get(f"/listings/id/{listing_id}", model=Listing)

    def get_by_vin(self, vin: str) -> Listing:
        """Get a listing by VIN.

        Args:
            vin: A 17-character VIN.
        """
        return self._client._get(f"/listings/vin/{vin}", model=Listing)

    def facets(
        self,
        *,
        makes: Optional[List[str]] = None,
        year_min: Optional[int] = None,
        year_max: Optional[int] = None,
        price_min: Optional[float] = None,
        price_max: Optional[float] = None,
    ) -> ListingFacetsResponse:
        """Get search facets (aggregated filter options)."""
        params = _build_search_params(
            makes=makes, year_min=year_min, year_max=year_max,
            price_min=price_min, price_max=price_max,
        )
        return self._client._get("/listings/facets", params=params, model=ListingFacetsResponse)


class AsyncListingsResource:
    """Vehicle listings search and retrieval (async)."""

    def __init__(self, client: AsyncAPIClient) -> None:
        self._client = client

    async def search(
        self,
        *,
        makes: Optional[List[str]] = None,
        models: Optional[Dict[str, List[str]]] = None,
        trims: Optional[List[str]] = None,
        year_min: Optional[int] = None,
        year_max: Optional[int] = None,
        price_min: Optional[float] = None,
        price_max: Optional[float] = None,
        odometer_min: Optional[int] = None,
        odometer_max: Optional[int] = None,
        body_styles: Optional[List[str]] = None,
        fuel_types: Optional[List[str]] = None,
        exterior_colors: Optional[List[str]] = None,
        interior_colors: Optional[List[str]] = None,
        sale_types: Optional[List[str]] = None,
        sellers: Optional[List[str]] = None,
        lat: Optional[float] = None,
        lng: Optional[float] = None,
        radius: Optional[float] = None,
        sort_field: Optional[str] = None,
        sort_direction: Optional[str] = None,
        page: Optional[int] = None,
        limit: Optional[int] = None,
    ) -> ListingSearchResponse:
        """Search vehicle listings with filters."""
        params = _build_search_params(**{k: v for k, v in locals().items() if k != "self"})
        return await self._client._get(
            "/listings/search", params=params, model=ListingSearchResponse
        )

    async def count(
        self,
        *,
        makes: Optional[List[str]] = None,
        models: Optional[Dict[str, List[str]]] = None,
        year_min: Optional[int] = None,
        year_max: Optional[int] = None,
        price_min: Optional[float] = None,
        price_max: Optional[float] = None,
        body_styles: Optional[List[str]] = None,
        fuel_types: Optional[List[str]] = None,
    ) -> ListingCountResponse:
        """Count listings matching the given filters."""
        params = _build_search_params(
            makes=makes, models=models, year_min=year_min, year_max=year_max,
            price_min=price_min, price_max=price_max,
            body_styles=body_styles, fuel_types=fuel_types,
        )
        return await self._client._get(
            "/listings/count", params=params, model=ListingCountResponse
        )

    async def get_by_id(self, listing_id: str) -> Listing:
        """Get a listing by its ID."""
        return await self._client._get(f"/listings/id/{listing_id}", model=Listing)

    async def get_by_vin(self, vin: str) -> Listing:
        """Get a listing by VIN."""
        return await self._client._get(f"/listings/vin/{vin}", model=Listing)

    async def facets(
        self,
        *,
        makes: Optional[List[str]] = None,
        year_min: Optional[int] = None,
        year_max: Optional[int] = None,
        price_min: Optional[float] = None,
        price_max: Optional[float] = None,
    ) -> ListingFacetsResponse:
        """Get search facets (aggregated filter options)."""
        params = _build_search_params(
            makes=makes, year_min=year_min, year_max=year_max,
            price_min=price_min, price_max=price_max,
        )
        return await self._client._get(
            "/listings/facets", params=params, model=ListingFacetsResponse
        )
