# 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 a :class:`DataVolatileBlobApi` class to perform API operations.

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

.. |volatile_blob_api_reference| raw:: html

   <a href="https://developer.here.com/documentation/data-api/api-reference-volatile-blob.html" target="_blank">Volatile Blob API Reference</a>  # noqa E501
"""

from typing import Optional

from here.platform.api.base_api import BaseApi
from here.platform.auth import Auth
from here.platform.config.application_config import ApplicationConfig
from here.platform.config.platform_config import PlatformConfig
from here.platform.exceptions import PlatformException


class DataVolatileBlobApi(BaseApi):
    """
    This class provides access to HERE platform Data Volatile Blob APIs.

    The volatile-blob service supports the upload and retrieval of volatile data from the storage
    of a catalog. Each discrete chunk of data is stored as a blob (Binary Large Object). Each blob
    has its own unique ID (data handle), which is stored as partition metadata. To get a
    partition's data, you first use metadata service to retrieve the partition's metadata
    (data handle) with the addresses of the relevant blobs. You then use those addresses to pull
    the data using the volatile-blob service. Unlike the blob service, data handles can be
    overwritten. Hence the volatile aspect.
    """

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

        :param base_url: base url
        :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.
        """
        super(DataVolatileBlobApi, self).__init__(
            platform_config=platform_config,
            application_config=application_config,
            auth=auth,
            proxies=proxies,
        )
        self.base_url = base_url

    def get_volatile_blob(
        self, layer_id: str, data_handle: str, billing_tag: Optional[str] = None
    ) -> bytes:
        """
        Retrieve a volatile data blob from storage.

        :param layer_id: The ID of the parent layer for this volatile data blob.
        :param data_handle: The data handle identifies a specific volatile data blob so that you
            can get that blob's contents.
        :param billing_tag: A string which is used for grouping billing records.
        :return: response from the API.
        :raises PlatformException: If platform responds with an HTTP error.
        """
        path = f"/layers/{layer_id}/data/{data_handle}"
        url = self.format_url(self.base_url, path)
        params = {"billingTag": billing_tag}
        resp = self.get(url, params=params)
        if resp.status_code in [200, 204]:
            return resp.content
        else:
            raise PlatformException(resp)

    def check_handle_exists(
        self, layer_id: str, data_handle: str, billing_tag: Optional[str] = None
    ) -> bool:
        """
        Check if a volatile blob exists for the requested data handle.

        :param layer_id: The ID of the layer that the volatile blob belongs to.
        :param data_handle: The data handle identifies a specific volatile blob so that you can
            get that blob's contents.
        :param billing_tag: A string which is used for grouping billing records.
        :return: response from the API.
        :raises PlatformException: If platform responds with an HTTP error.
        """
        path = f"/layers/{layer_id}/data/{data_handle}"
        url = self.format_url(self.base_url, path)
        params = {"billingTag": billing_tag}
        resp = self.head(url, params)
        if resp.status_code in [200, 204]:
            return True
        elif resp.status_code == 404:
            return False
        else:
            raise PlatformException(resp)

    def put_volatile_blob(
        self,
        layer_id: str,
        data_handle: str,
        body: bytes,
        billing_tag: Optional[str] = None,
    ):
        """
        Persist the volatile data blob in the underlying storage mechanism (volume).

        :param layer_id: The ID of the layer that the volatile blob belongs to.
        :param data_handle: The data handle (ID) represents an identifier for the volatile data
            blob.
        :param body: The data to upload as part of the blob. Size limit: 2 MB
        :param billing_tag: A string which is used for grouping billing records.
        :raises PlatformException: If platform responds with an HTTP error.
        """
        path = f"/layers/{layer_id}/data/{data_handle}"
        url = self.format_url(self.base_url, path)
        params = {"billingTag": billing_tag}
        resp = self.put(url, data=body, params=params)
        if resp.status_code in [200, 204]:
            return
        else:
            raise PlatformException(resp)

    def delete_volatile_blob(
        self,
        layer_id: str,
        data_handle: str,
        billing_tag: Optional[str] = None,
        strict: bool = False,
    ) -> bool:
        """
        Delete a volatile data blob from the underlying storage mechanism (volume).

        :param layer_id: The ID of the layer that the volatile data blob belongs to.
        :param data_handle: The data handle (ID) represents an identifier for the volatile data
            blob which contents will be deleted.
        :param strict: If the blob doesn't exist, strict=True will raise a PlatformException
            while strict=False will not.
        :param billing_tag: A string which is used for grouping billing records.
        :return: True if the blob exists and was deleted, False if it doesn't exist.
        :raises PlatformException: If platform responds with an HTTP error.
        """
        success_codes = [200, 204]
        # Note: 200 is returned only by LDS
        if not strict:
            success_codes.append(404)

        path = f"/layers/{layer_id}/data/{data_handle}"
        url = self.format_url(self.base_url, path)
        params = {"billingTag": billing_tag}
        resp = self.delete(url, params)
        if resp.status_code in success_codes:
            return resp.status_code != 404
        else:
            raise PlatformException(resp)
