# __init__.py for gis

from .content_manager import ContentManager

import os
from urllib.parse import urljoin
import logging
import importlib.util
from kapipy.custom_errors import ServerError, BadRequest
import httpx

logger = logging.getLogger(__name__)

PORTAL_DETAILS = {
    "linz": {
        "name": "LINZ",
        "url": "https://data.linz.govt.nz/",
    },
    "statsnz": {"name": "Stats NZ", "url": "https://datafinder.stats.govt.nz/"},
    "lris": {
        "name": "LRIS",
        "url": "https://lris.scinfo.org.nz/",
    },
}
DEFAULT_API_VERSION = "v1.x"
SERVICES_PATH = "services/"
API_PATH = "api/"
WFS_PATH = "wfs/"

has_arcgis = importlib.util.find_spec("arcgis") is not None
has_geopandas = importlib.util.find_spec("geopandas") is not None
has_arcpy = importlib.util.find_spec("arcpy") is not None

logger.debug(f'{has_arcgis=}')
logger.debug(f'{has_geopandas=}')
logger.debug(f'{has_arcpy=}')


class GIS:
    """
    Client for connecting to a Koordinates server.

    Provides methods for authenticating, accessing content, and making HTTP requests to the Koordinates API.
    Used as the main entry point for interacting with Koordinates-hosted data.

    Attributes:
        name (str): The name of the Koordinates portal.
        url (str): The base URL of the Koordinates server.
        _api_version (str): The API version to use.
        _content_manager (ContentManager or None): Cached ContentManager instance.
        _wfs_manager (object or None): Cached WFS manager instance (if implemented).
        _api_key (str): The API key for authenticating requests.
    """

    def __init__(
        self,
        name=None,
        url=None,
        api_key=None,
        api_version=DEFAULT_API_VERSION,
    ) -> None:
        """
        Initializes the GIS instance with the base URL, API version, and API key.

        Parameters:
            name (str, optional): The name of the Koordinates portal (e.g., 'linz'). If provided, overrides url.
            url (str, optional): The base URL of the Koordinates server. Used if name is not provided.
            api_key (str): The API key for authenticating with the Koordinates server.
            api_version (str, optional): The API version to use. Defaults to 'v1.x'.

        Raises:
            ValueError: If the portal name is not recognized or if api_key is not provided.
        """

        if name and PORTAL_DETAILS.get(name.lower(), None) is None:
            raise ValueError("Supplied portal name is not included in default list.")
        if name:
            self.name = PORTAL_DETAILS.get(name.lower()).get("name")
            self.url = PORTAL_DETAILS.get(name.lower()).get("url")
        self.url = (
            self.url if self.url.endswith("/") else f"{self.url}/"
        )  # ensure trailing slash
        self._api_version = api_version
        self._content_manager = None
        self._wfs_manager = None
        self._api_key = api_key
        if not self._api_key:
            raise ValueError("API key must be provided.")
        logger.debug(f"GIS initialized with URL: {self.url}")

    @property
    def _service_url(self) -> str:
        """
        Returns the service URL for the Koordinates server.

        Returns:
            str: The full service URL, ending with a slash.
        """
        url = f"{self.url}{SERVICES_PATH}"
        return url if url.endswith("/") else f"{url}/"

    @property
    def _api_url(self) -> str:
        """
        Returns the API URL for the Koordinates server.

        Returns:
            str: The full API URL, ending with a slash.
        """
        url = f"{self._service_url}{API_PATH}{self._api_version}/"
        return url if url.endswith("/") else f"{url}/"

    @property
    def _wfs_url(self) -> str:
        """
        Returns the WFS URL for the Koordinates server.

        Returns:
            str: The full WFS URL, ending with a slash.
        """

        url = f"{self._service_url}{WFS_PATH}"
        return url if url.endswith("/") else f"{url}/"

    @property
    def content(self) -> "ContentManager":
        """
        Returns the ContentManager instance for this server.

        Returns:
            ContentManager: The content manager associated with this server.
        """

        if self._content_manager is None:
            from . import ContentManager

            self._content_manager = ContentManager(self)
        return self._content_manager

    def get(self, url: str, params: dict = None) -> dict:
        """
        Makes a synchronous GET request to the specified URL with the provided parameters.
        Injects the API key into the request headers.

        Parameters:
            url (str): The URL to send the GET request to.
            params (dict, optional): Query parameters to include in the request. Defaults to None.

        Returns:
            dict: The JSON-decoded response from the server.

        Raises:
            BadRequest: If the request fails with a 400 status code.
            ServerError: For other HTTP errors or request exceptions.
        """

        headers = {"Authorization": f"key {self._api_key}"}
        logger.debug(f"Making kserver GET request to {url} with params {params}")
        try:
            response = httpx.get(url, headers=headers, params=params, timeout=30)
        except httpx.RequestError as exc:
            logger.error(f"An error occurred while requesting {exc.request.url!r}.")
            raise ServerError(str(exc)) from exc

        if response.status_code == 400:
            raise BadRequest(response.text)
        response.raise_for_status()
        return response.json()

    def reset(self) -> None:
        """
        Resets the GIS instance, forcing the content manager and WFS manager
        to reinitialize the next time they are accessed. This is useful if the API key
        or other configurations change.

        Returns:
            None
        """

        self._content_manager = None
        self._wfs_manager = None
        logger.info("KServer instance reset.")

    def __repr__(self) -> str:
        """
        Returns an unambiguous string representation of the GIS instance.

        Returns:
            str: String representation of the GIS instance.
        """
        return (
            f"GIS(name={self.name!r}, url={self.url!r}, api_key={'***' if self._api_key else None}, "
            f"api_version={self._api_version!r})"
        )

    def __str__(self) -> str:
        """
        Returns a user-friendly string representation of the GIS instance.

        Returns:
            str: User-friendly string representation.
        """
        return f"GIS: {self.name or 'Custom'} at {self.url} (API {self._api_version})"
