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

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

.. |ingest_api_reference| raw:: html

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

from typing import Any, Dict, Optional

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


class DataIngestApi(BaseApi):
    """
    This class provides access to HERE platform Data Ingest APIs.

    The ingestion service provides the way to submit data to stream layers using REST API.
    """

    def __init__(
        self,
        base_url: str,
        platform_config: PlatformConfig,
        application_config: ApplicationConfig,
        auth: Optional[Auth],
        proxies: Optional[dict] = None,
    ):
        """
        Instantiate DataIngestApi 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(DataIngestApi, self).__init__(
            platform_config=platform_config,
            application_config=application_config,
            auth=auth,
            proxies=proxies,
        )
        self.base_url = base_url

    def ingest_data(
        self,
        layer_id: str,
        body: bytes,
        checksum_header: Optional[str] = None,
        traceid_header: Optional[str] = None,
        message_key_header: Optional[str] = None,
        billing_tag: Optional[str] = None,
    ) -> dict:
        """
        Send streaming data to a specified layer in the Platform.

        :param layer_id: The ID of the layer where you want the data ingested.
        :param body: data to be uploaded in bytes.
        :param checksum_header: A base64 encoded SHA-256 hash you can provide for validation
            against the calculated value on the request body hash. This verifies the integrity of
            your request and prevents modification by a third party.It will be created by the
            service if not provided. A SHA-256 hash consists of 256 bits or 64 chars.
        :param traceid_header: A unique message ID, such as a UUID. This can be included in the
            request if you want to use an ID that you define. If you do not include an ID, one
            will be generated during ingestion and included in the response. You can use this ID
            to track your request and identify the message in the catalog.
        :param message_key_header: A unique message ID, such as a UUID. This can be included in
            the request if you want to use an ID that you define. If you do not include an ID,
            X-HERE-TraceId will be used instead. You can use this ID to specify the partition for
            the message in the data catalog.
        :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}"
        url = self.format_url(self.base_url, path)
        headers = self.headers
        headers["X-HERE-Checksum"] = checksum_header
        headers["X-HERE-TraceId"] = traceid_header
        headers["X-HERE-Message-Key"] = message_key_header
        params = {"billingTag": billing_tag}
        resp = self.post(url, data=body, params=params, headers=headers)
        if resp.status_code == 200:
            resp_json: dict = resp.json()
            return resp_json
        else:
            raise PlatformException(resp)

    def ingest_sdii(
        self,
        layer_id: str,
        body: bytes,
        checksum_header: Optional[str] = None,
        traceid_header: Optional[str] = None,
        message_key_header: Optional[str] = None,
        billing_tag: Optional[str] = None,
    ) -> dict:
        """
        Consume individual SDII messages and SDII MessageLists. The endpoint automatically breaks
        SDII MessageLists down and stores the data as individual SDII messages. Note that this
        endpoint does not support compression. If you are sending data to stream layer that has
        compression enabled, you must use the /layers/<layerID> endpoint and all compression and
        decompression must be handled by your application.

        :param layer_id: The ID of the publication to retrieve.
        :param body: data to be uploaded in bytes.
        :param checksum_header: A base64 encoded SHA-256 hash you can provide for validation
            against the calculated value on the request body hash. This verifies the integrity of
            your request and prevents modification by a third party.It will be created by the
            service if not provided. A SHA-256 hash consists of 256 bits or 64 chars.
        :param traceid_header: A unique message ID, such as a UUID. This can be included in the
            request if you want to use an ID that you define. If you do not include an ID, one
            will be generated during ingestion and included in the response. You can use this ID
            to track your request and identify the message in the catalog.
        :param message_key_header: A unique message ID, such as a UUID. This can be included in
            the request if you want to use an ID that you define. If you do not include an ID,
            X-HERE-TraceId will be used instead. You can use this ID to specify the partition for
            the message in the data catalog.
        :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}/sdiiMessageList"
        url = self.format_url(self.base_url, path)
        params = {"billingTag": billing_tag}
        headers = self.headers
        headers["X-HERE-Checksum"] = checksum_header
        headers["X-HERE-TraceId"] = traceid_header
        headers["X-HERE-Message-Key"] = message_key_header
        headers["Content-Type"] = "application/x-protobuf"
        resp = self.post(url, data=body, params=params, headers=headers)
        if resp.status_code == 200:
            resp_json: dict = resp.json()
            return resp_json
        else:
            raise PlatformException(resp)

    def ingest_partitions(
        self,
        layer_id: str,
        partitions: Dict[str, Any],
        traceid_header: Optional[str] = None,
        billing_tag: Optional[str] = None,
    ) -> dict:
        """
        Upload partitions data to a specified layer in the Platform.

        :param layer_id: The ID of the layer where you want the data ingested.
        :param partitions: partitions to be uploaded .
        :param traceid_header: A unique message ID, such as a UUID. This can be included in the
            request if you want to use an ID that you define. If you do not include an ID, one
            will be generated during ingestion and included in the response. You can use this ID
            to track your request and identify the message in the catalog.
        :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}/partitions"
        url = self.format_url(self.base_url, path)
        headers = self.headers
        headers["X-HERE-TraceId"] = traceid_header
        params = {"billingTag": billing_tag}
        resp = self.post(url, data=partitions, params=params, headers=headers)
        if resp.status_code == 200:
            resp_json: dict = resp.json()
            return resp_json
        else:
            raise PlatformException(resp)
