from openaq.shared.models import build_query_params
from openaq.shared.responses import (
    LatestResponse,
    LocationsResponse,
    SensorsResponse,
)
from openaq.shared.types import SortOrder
from openaq.shared.validators import (
    validate_geospatial_params,
    validate_integer_id,
    validate_integer_or_list_integer_params,
    validate_iso_param,
    validate_limit_param,
    validate_mobile,
    validate_monitor,
    validate_order_by,
    validate_page_param,
    validate_sort_order,
)

from .base import AsyncResourceBase


class Locations(AsyncResourceBase):
    """This provides methods to retrieve air monitor locations resource from the OpenAQ API."""

    async def get(self, locations_id: int) -> LocationsResponse:
        """Retrieve a specific location by its locations ID.

        Args:
            locations_id: The locations ID of the location to retrieve.

        Returns:
            LocationsResponse: An instance representing the retrieved location.

        Raises:
            BadRequestError: Raised for HTTP 400 error, indicating a client request error.
            NotAuthorizedError: Raised for HTTP 401 error, indicating the client is not authorized.
            ForbiddenError: Raised for HTTP 403 error, indicating the request is forbidden.
            NotFoundError: Raised for HTTP 404 error, indicating a resource is not found.
            TimeoutError: Raised for HTTP 408 error, indicating the request has timed out.
            ValidationError: Raised for HTTP 422 error, indicating invalid request parameters.
            RateLimitError: Raised for HTTP 429 error, indicating rate limit exceeded.
            ServerError: Raised for HTTP 500 error, indicating an internal server error or unexpected server-side issue.
            GatewayTimeoutError: Raised for HTTP 504 error, indicating a gateway timeout.
        """
        locations_id = validate_integer_id(locations_id)
        location = await self._client._get(f"/locations/{locations_id}")
        return LocationsResponse.read_response(location)

    async def latest(self, locations_id: int) -> LatestResponse:
        """Retrieve latest measurements from a location.

        Args:
            locations_id: The locations ID of the location to retrieve.

        Returns:
            LatestResponse: An instance representing the retrieved latest results.

        Raises:
            IdentifierOutOfBoundsError: Client validation error, identifier outside support int32 range.
            BadRequestError: Raised for HTTP 400 error, indicating a client request error.
            NotAuthorizedError: Raised for HTTP 401 error, indicating the client is not authorized.
            ForbiddenError: Raised for HTTP 403 error, indicating the request is forbidden.
            NotFoundError: Raised for HTTP 404 error, indicating a resource is not found.
            TimeoutError: Raised for HTTP 408 error, indicating the request has timed out.
            ValidationError: Raised for HTTP 422 error, indicating invalid request parameters.
            RateLimitError: Raised for HTTP 429 error, indicating rate limit exceeded.
            ServerError: Raised for HTTP 500 error, indicating an internal server error or unexpected server-side issue.
            GatewayTimeoutError: Raised for HTTP 504 error, indicating a gateway timeout.
        """
        locations_id = validate_integer_id(locations_id)
        latest = await self._client._get(f"/locations/{locations_id}/latest")
        return LatestResponse.read_response(latest)

    async def list(
        self,
        page: int = 1,
        limit: int = 100,
        radius: int | None = None,
        coordinates: tuple[float, float] | None = None,
        bbox: tuple[float, float] | None = None,
        providers_id: int | list[int] | None = None,
        countries_id: int | list[int] | None = None,
        parameters_id: int | list[int] | None = None,
        licenses_id: int | list[int] | None = None,
        iso: str | None = None,
        monitor: bool | None = None,
        mobile: bool | None = None,
        order_by: str | None = None,
        sort_order: SortOrder | None = None,
    ) -> LocationsResponse:
        """List locations based on provided filters.

        Args:
            page: The page number, must be greater than zero. Page count is locations found / limit.
            limit: The number of results returned per page. Must be between 1 and 1,000.
            radius: A distance value in meters to search around the given coordinates value, must be between 1 and 25,000 (25km).
            coordinates: WGS 84 coordinate pair in form latitude, longitude (y,x).
            bbox: Geospatial bounding box of min X, min Y, max X, max Y in WGS 84 coordinates. Limited to four decimals precision.
            providers_id: Single providers ID or an array of IDs.
            countries_id: Single countries ID or an array of IDs.
            parameters_id: Single parameters ID or an array of IDs.
            licenses_id: Single licenses ID or an array of IDs.
            iso: 2 letter ISO 3166-alpha-2 country code.
            monitor: Boolean for reference grade monitors (true) or air sensors (false)
            mobile: Boolean mobile locations (true) or not mobile locations (false).
            order_by: Order by operators for results.
            sort_order: Order for sorting results (asc/desc).

        Returns:
            LocationsResponse: An instance representing the list of retrieved locations.

        Raises:
            InvalidParameterError: Client validation error, query parameter is not correct type or value.
            IdentifierOutOfBoundsError: Client validation error, identifier outside support int32 range.
            ApiKeyMissingError: Authentication error, missing API Key credentials.
            BadRequestError: Raised for HTTP 400 error, indicating a client request error.
            NotAuthorizedError: Raised for HTTP 401 error, indicating the client is not authorized.
            ForbiddenError: Raised for HTTP 403 error, indicating the request is forbidden.
            NotFoundError: Raised for HTTP 404 error, indicating a resource is not found.
            TimeoutError: Raised for HTTP 408 error, indicating the request has timed out.
            ValidationError: Raised for HTTP 422 error, indicating invalid request parameters.
            RateLimitError: Raised when managed client exceeds rate limit.
            HTTPRateLimitError: Raised for HTTP 429 error, indicating rate limit exceeded.
            ServerError: Raised for HTTP 500 error, indicating an internal server error or unexpected server-side issue.
            BadGatewayError: Raised for HTTP 502, indicating that the gateway or proxy received an invalid response from the upstream server.
            ServiceUnavailableError: Raised for HTTP 503, indicating that the server is not ready to handle the request.
            GatewayTimeoutError: Raised for HTTP 504 error, indicating a gateway timeout.
        """
        page = validate_page_param(page)
        limit = validate_limit_param(limit)
        validate_geospatial_params(coordinates, radius, bbox)
        if providers_id:
            providers_id = validate_integer_or_list_integer_params(
                'providers_id', providers_id
            )
        if countries_id:
            countries_id = validate_integer_or_list_integer_params(
                'countries_id', countries_id
            )
        if parameters_id:
            parameters_id = validate_integer_or_list_integer_params(
                'parameters_id', parameters_id
            )
        if licenses_id:
            licenses_id = validate_integer_or_list_integer_params(
                'licenses_id', licenses_id
            )
        if iso:
            iso = validate_iso_param(iso)
        if monitor is not None:
            monitor = validate_monitor(monitor)
        if mobile is not None:
            mobile = validate_mobile(mobile)
        if sort_order is not None:
            sort_order = validate_sort_order(sort_order)
        if order_by is not None:
            order_by = validate_order_by(order_by)
        params = build_query_params(
            page=page,
            limit=limit,
            radius=radius,
            coordinates=coordinates,
            bbox=bbox,
            providers_id=providers_id,
            countries_id=countries_id,
            parameters_id=parameters_id,
            licenses_id=licenses_id,
            iso=iso,
            monitor=monitor,
            mobile=mobile,
            order_by=order_by,
            sort_order=sort_order,
        )

        locations = await self._client._get("/locations", params=params)
        return LocationsResponse.read_response(locations)

    async def sensors(self, locations_id: int) -> SensorsResponse:
        """Retrieve sensors from a location.

        Args:
            locations_id: The locations ID of the location to retrieve.

        Returns:
            SensorsResponse: An instance representing the retrieved latest results.

        Raises:
            IdentifierOutOfBoundsError: Client validation error, identifier outside support int32 range.
            ApiKeyMissingError: Authentication error, missing API Key credentials.
            BadRequestError: Raised for HTTP 400 error, indicating a client request error.
            NotAuthorizedError: Raised for HTTP 401 error, indicating the client is not authorized.
            ForbiddenError: Raised for HTTP 403 error, indicating the request is forbidden.
            NotFoundError: Raised for HTTP 404 error, indicating a resource is not found.
            TimeoutError: Raised for HTTP 408 error, indicating the request has timed out.
            ValidationError: Raised for HTTP 422 error, indicating invalid request parameters.
            RateLimitError: Raised when managed client exceeds rate limit.
            HTTPRateLimitError: Raised for HTTP 429 error, indicating rate limit exceeded.
            ServerError: Raised for HTTP 500 error, indicating an internal server error or unexpected server-side issue.
            BadGatewayError: Raised for HTTP 502, indicating that the gateway or proxy received an invalid response from the upstream server.
            ServiceUnavailableError: Raised for HTTP 503, indicating that the server is not ready to handle the request.
            GatewayTimeoutError: Raised for HTTP 504 error, indicating a gateway timeout.
        """
        locations_id = validate_integer_id(locations_id)
        sensors = await self._client._get(f"/locations/{locations_id}/sensors")
        return SensorsResponse.read_response(sensors)
