# Copyright (C) 2020-2022 HERE Global B.V. and its affiliate(s).
# All rights reserved.
#
# This software and other materials contain proprietary information
# controlled by HERE and are protected by applicable copyright legislation.
# Any use and utilization of this software and other materials and
# disclosure to any third parties is conditional upon having a separate
# agreement with HERE for the access, use, utilization or disclosure of this
# software. In the absence of such agreement, the use of the software is not
# allowed.

"""
This module contains an :class:`LookupApi` class to perform API operations.

The HERE API reference documentation used in this module can be found here:
|lookup_api_reference|

.. |lookup_api_reference| raw:: html

   <a href="https://developer.here.com/documentation/api-lookup/api-reference-swagger.html">Lookup API Reference</a>  # noqa E501
"""

from typing import Optional
from urllib.parse import urlparse

from here.platform.api.base_api import BaseApi
from here.platform.api.registry import LookupApiRegistry
from here.platform.auth import Auth
from here.platform.config import ApplicationConfig, PlatformConfig
from here.platform.exceptions import PlatformException


class LookupApi(BaseApi):
    """
    This class provides access to HERE platform Lookup APIs.

    Instances can call only to those API endpoints relevant for accessing
    catalog and layer metadata, as well as those needed to access the data
    contained in different types of layers.
    """

    api_version_impl = {
        "lookup": ["v1"],
        "blob": ["v1", "v2"],
        "index": ["v1"],
        "ingest": ["v1"],
        "metadata": ["v1"],
        "notification": ["v2"],
        "publish": ["v2"],
        "query": ["v1"],
        "statistics": ["v1"],
        "stream": ["v2"],
        "volatile-blob": ["v1"],
        "interactive": ["v1"],
    }

    platform_api_version_impl = {
        "lookup": "v1",
        "config": "v1",
        "artifact": "v1",
        "location-service-registry": "v1",
    }

    def __init__(
        self,
        auth: Optional[Auth],
        platform_config: PlatformConfig,
        application_config: ApplicationConfig,
        lookup_api_registry: LookupApiRegistry,
        proxies: Optional[dict] = None,
    ):
        """
        Instantiate LookupApi object.

        :param platform_config: a mandatory :class:`PlatformConfig` object to provide
            configuration information for the API.
        :param application_config: a mandatory :class:`ApplicationConfig` object to provide
            configuration information for the API.
        :param auth: an Authentication instance.
        :param proxies: an optional proxy configuration. Defaults to the environment proxy
        configuration.
        :param lookup_api_registry: instance of LookupApiRegistry
        """
        super(LookupApi, self).__init__(
            platform_config=platform_config,
            application_config=application_config,
            auth=auth,
            proxies=proxies,
        )

        # temporary work-around for LDS support: If api url has scheme, use value unchanged
        # as scheme and netloc.
        parsed = urlparse(self.platform_config.api)
        if parsed.scheme:
            server = self.platform_config.api
        else:
            server = f"https://api-lookup.data.{self.platform_config.api}"
        base_path = "/lookup/" + self.api_version_impl["lookup"][0]
        self.base_url = f"{server}{base_path}"
        self.lookup_api_registry = lookup_api_registry

    def get_resource_api_list(self, hrn: str, region: Optional[str] = None) -> dict:
        """
        Lookup all available APIs for given HRN.

        :param hrn: a HERE Resource Name identifying the resource
        :param region: an Optional param to look up a specific region for a given resource
        :return: The list of APIs that can be used with the resource
        :raises PlatformException: If platform responds with an HTTP error.
        """
        existing_apis = self.lookup_api_registry.resources_apis(catalog_hrn=hrn)
        if existing_apis is not None:
            return existing_apis

        path = f"/resources/{hrn}/apis"
        url = self.format_url(self.base_url, path)
        params = dict(region=region)
        resp = self.get(url, params=params)
        if resp.status_code == 200:
            apis = {
                f"{el['api']}-{el['version']}": {k: v for (k, v) in el.items() if k != "api"}
                for el in resp.json()
                if el["api"] in self.api_version_impl
                and el["version"] in self.api_version_impl[el["api"]]
            }
            self.lookup_api_registry.register(catalog_hrn=hrn, resources_apis=apis)
            return apis
        else:
            raise PlatformException(resp)

    def get_resource_api(
        self, hrn: str, api: str, version: str, region: Optional[str] = None
    ) -> dict:
        """
        Return details of a single API for a given resource identified by hrn, api and version.

        :param hrn: a HERE Resource Name identifying the resource
        :param api: The identifier of the API
        :param version: The version of the API
        :param region: an Optional param to look up a specific region for a given resource
        :return: Details of the requested API for the resource
        :raises PlatformException: If platform responds with an HTTP error.
        """
        path = f"/resources/{hrn}/apis/{api}/{version}"
        url = self.format_url(self.base_url, path)
        params = dict(region=region)
        resp = self.get(url, params=params)
        if resp.status_code == 200:
            resp_json: dict = resp.json()[0] if resp.json() else dict()
            return resp_json
        else:
            raise PlatformException(resp)

    def get_platform_api_list(self) -> list:
        """
        Return the list of the platform APIs.

        :return: The list of APIs of the platform
        :raises PlatformException: If platform responds with an HTTP error.
        """
        path = "/platform/apis"
        url = self.format_url(self.base_url, path)
        resp = self.get(url)
        if resp.status_code == 200:
            resp_json: list = resp.json()
            return resp_json
        else:
            raise PlatformException(resp)

    def get_impl_platform_api_list(self) -> dict:
        """
        Lookup implemented platform APIs.
        """
        platform_api_resp = self.get_platform_api_list()
        platform_apis = {
            el["api"]: {k: v for (k, v) in el.items() if k != "api"}
            for el in platform_api_resp
            if el["api"] in self.platform_api_version_impl
            and el["version"] == self.platform_api_version_impl[el["api"]]
        }
        return platform_apis

    def get_platform_api(self, api: str, version: str) -> dict:
        """
        Return details of a single platform API.

        :param api: The identifier of the API
        :param version: The version of the API
        :return: Details of the requested API for the resource
        :raises PlatformException: If platform responds with an HTTP error.
        """
        path = f"/platform/apis/{api}/{version}"
        url = self.format_url(self.base_url, path)
        resp = self.get(url)
        if resp.status_code == 200:
            resp_json: dict = resp.json()[0] if resp.json() else dict()
            return resp_json
        else:
            raise PlatformException(resp)
