"""Modeled groundwater level client endpoints."""

import logging
from typing import Any

from sgu_client.client.base import BaseClient
from sgu_client.models.modeled import (
    ModeledArea,
    ModeledAreaCollection,
    ModeledGroundwaterLevel,
    ModeledGroundwaterLevelCollection,
)


class ModeledGroundwaterLevelClient:
    """Client for modeled groundwater level-related SGU API endpoints."""

    BASE_PATH = "collections"
    MODELED_BASE_URL = "https://api.sgu.se/oppnadata/grundvattennivaer-sgu-hype-omraden/ogc/features/v1/"

    def __init__(self, base_client: BaseClient):
        """Initialize modeled groundwater level client.

        Args:
            base_client: Base HTTP client instance
        """
        self._client = base_client

    def get_areas(
        self,
        bbox: list[float] | None = None,
        limit: int | None = None,
        filter_expr: str | None = None,
        sortby: list[str] | None = None,
        **kwargs: Any,
    ) -> ModeledAreaCollection:
        """Get modeled groundwater areas.

        Args:
            bbox: Bounding box as [min_lon, min_lat, max_lon, max_lat]
            limit: Maximum number of features to return (automatically paginated if needed)
            filter_expr: CQL filter expression
            sortby: List of sort expressions (e.g., ['+omrade_id'] - use Swedish field names for API)
            **kwargs: Additional query parameters

        Returns:
            Typed collection of modeled groundwater areas
        """
        endpoint = f"{self.BASE_PATH}/omraden/items"
        params = self._build_query_params(
            bbox=bbox,
            limit=limit,
            filter=filter_expr,
            sortby=sortby,
            **kwargs,
        )
        response = self._make_request(endpoint, params)
        return ModeledAreaCollection(**response)

    def get_area(self, area_id: str) -> ModeledArea:
        """Get a specific modeled groundwater area by ID.

        Args:
            area_id: Area identifier

        Returns:
            Typed modeled groundwater area

        Raises:
            ValueError: If area not found or multiple areas returned
        """
        endpoint = f"{self.BASE_PATH}/omraden/items/{area_id}"
        response = self._make_request(endpoint, {})

        # SGU API returns a FeatureCollection even for single items
        collection = ModeledAreaCollection(**response)
        if not collection.features:
            raise ValueError(f"Area {area_id} not found")
        if len(collection.features) > 1:
            raise ValueError(f"Multiple areas returned for ID {area_id}")

        return collection.features[0]

    def get_levels(
        self,
        bbox: list[float] | None = None,
        datetime: str | None = None,
        limit: int | None = None,
        filter_expr: str | None = None,
        sortby: list[str] | None = None,
        **kwargs: Any,
    ) -> ModeledGroundwaterLevelCollection:
        """Get modeled groundwater levels.

        Args:
            bbox: Bounding box as [min_lon, min_lat, max_lon, max_lat]
            datetime: Date/time filter (RFC 3339 format or interval)
            limit: Maximum number of features to return (automatically paginated if needed)
            filter_expr: CQL filter expression
            sortby: List of sort expressions (e.g., ['+datum', '-omrade_id'] - use Swedish field names for API)
            **kwargs: Additional query parameters

        Returns:
            Typed collection of modeled groundwater levels
        """
        endpoint = f"{self.BASE_PATH}/grundvattennivaer-tidigare/items"
        params = self._build_query_params(
            bbox=bbox,
            datetime=datetime,
            limit=limit,
            filter=filter_expr,
            sortby=sortby,
            **kwargs,
        )
        response = self._make_request(endpoint, params)
        return ModeledGroundwaterLevelCollection(**response)

    def get_level(self, level_id: str) -> ModeledGroundwaterLevel:
        """Get a specific modeled groundwater level by ID.

        Args:
            level_id: Level identifier

        Returns:
            Typed modeled groundwater level

        Raises:
            ValueError: If level not found or multiple levels returned
        """
        endpoint = f"{self.BASE_PATH}/grundvattennivaer-tidigare/items/{level_id}"
        response = self._make_request(endpoint, {})

        # SGU API returns a FeatureCollection even for single items
        collection = ModeledGroundwaterLevelCollection(**response)
        if not collection.features:
            raise ValueError(f"Level {level_id} not found")
        if len(collection.features) > 1:
            raise ValueError(f"Multiple levels returned for ID {level_id}")

        return collection.features[0]

    def get_levels_by_area(
        self, area_id: int, **kwargs: Any
    ) -> ModeledGroundwaterLevelCollection:
        """Get modeled groundwater levels for a specific area.

        Args:
            area_id: Area ID to filter by
            **kwargs: Additional query parameters (limit, datetime, bbox, etc.)

        Returns:
            Typed collection of modeled groundwater levels for the specified area
        """
        filter_expr = f"omrade_id = {area_id}"
        return self.get_levels(filter_expr=filter_expr, **kwargs)

    def get_levels_by_areas(
        self, area_ids: list[int], **kwargs: Any
    ) -> ModeledGroundwaterLevelCollection:
        """Get modeled groundwater levels for multiple areas.

        Args:
            area_ids: List of area IDs to filter by
            **kwargs: Additional query parameters (limit, datetime, bbox, etc.)

        Returns:
            Typed collection of modeled groundwater levels for the specified areas
        """
        if not area_ids:
            raise ValueError("At least one area ID must be provided")

        # Create CQL filter expression for multiple area IDs
        if len(area_ids) == 1:
            filter_expr = f"omrade_id = {area_ids[0]}"
        else:
            area_ids_str = ", ".join(str(area_id) for area_id in area_ids)
            filter_expr = f"omrade_id IN ({area_ids_str})"

        return self.get_levels(filter_expr=filter_expr, **kwargs)

    def get_levels_by_coords(
        self,
        lat: float,
        lon: float,
        buffer: float = 0.01,
        **kwargs: Any,
    ) -> ModeledGroundwaterLevelCollection:
        """Get modeled groundwater levels for a specific coordinate.

        This convenience function first finds relevant areas containing or near
        the specified coordinates, then retrieves modeled levels for those areas.

        Args:
            lat: Latitude coordinate (WGS84)
            lon: Longitude coordinate (WGS84)
            buffer: Buffer distance in degrees around the point (default 0.01 ≈ 1km)
            **kwargs: Additional query parameters (limit, datetime, etc.)

        Returns:
            Typed collection of modeled groundwater levels for areas near the coordinates

        Raises:
            ValueError: If no areas found near the specified coordinates
        """
        logger = logging.getLogger(__name__)

        # Create bounding box around the point
        bbox = [
            lon - buffer,  # min_lon
            lat - buffer,  # min_lat
            lon + buffer,  # max_lon
            lat + buffer,  # max_lat
        ]

        # Find areas within the bounding box
        areas = self.get_areas(bbox=bbox, limit=1000)

        if not areas.features:
            raise ValueError(
                f"No modeled groundwater areas found near coordinates "
                f"({lat}, {lon}) within {buffer}° buffer"
            )

        # Extract area IDs
        area_ids = [int(area.properties.area_id) for area in areas.features]

        # Log warning if multiple areas found (near boundary)
        if len(area_ids) > 1:
            logger.warning(
                f"Found {len(area_ids)} modeled areas near coordinates "
                f"({lat}, {lon}). This suggests the point is close to an area boundary. "
                f"Area IDs: {area_ids}. All areas will be included in the results."
            )

        # Get levels for all found areas
        return self.get_levels_by_areas(area_ids, **kwargs)

    def _build_query_params(self, **params: Any) -> dict[str, Any]:
        """Build query parameters for API requests.

        Args:
            **params: Raw parameter values

        Returns:
            Cleaned dictionary of query parameters
        """
        query_params = {}

        for key, value in params.items():
            if value is None:
                continue

            if key == "bbox" and isinstance(value, list):
                query_params[key] = ",".join(map(str, value))
            elif key == "sortby" and isinstance(value, list):
                query_params[key] = ",".join(value)
            else:
                query_params[key] = value

        return query_params

    def _make_request(self, endpoint: str, params: dict[str, Any]) -> dict[str, Any]:
        """Make HTTP request to SGU API.

        Args:
            endpoint: API endpoint path
            params: Query parameters

        Returns:
            JSON response data

        Raises:
            Various HTTP and API exceptions via base client
        """
        return self._client.get(endpoint, params=params, base_url=self.MODELED_BASE_URL)
