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

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

.. |publish_api_reference| raw:: html

   <a href="https://developer.here.com/documentation/data-api/api-reference-publish.html" target="_blank">Publish 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 import ApplicationConfig, PlatformConfig
from here.platform.exceptions import PlatformException


class DataPublishApi(BaseApi):
    """
    This class provides access to HERE platform Data Publish APIs.

    Manage the publishing of data to a catalog. Supports publish to versioned, volatile and stream
    layer types.
    """

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

    def init_publication(self, body: dict, billing_tag: Optional[str] = None) -> dict:
        """
        Initialize a new publication for publishing metadata.

        :param body: a dictionary with fields to initialize a publication.
        :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 = "/publications"
        url = self.format_url(self.base_url, path)
        params = {"billingTag": billing_tag}
        resp = self.post(url, data=body, params=params)
        if resp.status_code == 200:
            resp_json: dict = resp.json()
            return resp_json
        else:
            raise PlatformException(resp)

    def get_publication(self, publication_id: str, billing_tag: Optional[str] = None) -> dict:
        """
        Return the details of the specified publication.

        :param publication_id: The ID of the publication to retrieve.
        :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"/publications/{publication_id}"
        url = self.format_url(self.base_url, path)
        params = {"billingTag": billing_tag}
        resp = self.get(url, params)
        if resp.status_code == 200:
            resp_json: dict = resp.json()
            return resp_json
        else:
            raise PlatformException(resp)

    def submit_publication(self, publication_id: str, billing_tag: Optional[str] = None):
        """
        Submit the publication and initiates post processing if necessary.

        :param publication_id: The ID of the publication to submit.
        :param billing_tag: A string which is used for grouping billing records.
        :raises PlatformException: If platform responds with an HTTP error.
        """
        path = f"/publications/{publication_id}"
        url = self.format_url(self.base_url, path)
        params = {"billingTag": billing_tag}
        resp = self.put(url, params)
        if resp.status_code == 204:
            return
        else:
            raise PlatformException(resp)

    def cancel_publication(
        self, publication_id: str, billing_tag: Optional[str] = None, strict: bool = False
    ) -> bool:
        """
        Cancel a publication if it has not yet been submitted.

        :param publication_id: The ID of the publication to cancel.
        :param billing_tag: A string which is used for grouping billing records.
        :param strict: If the publication doesn't exist, strict=True will raise a
            PlatformException while strict=False will not.
        :return: True if the publication exists and was cancelled, False if it doesn't exist.
        :raises PlatformException: If platform responds with an HTTP error.
        """
        success_codes = [204]
        if not strict:
            success_codes.append(404)

        path = f"/publications/{publication_id}"
        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)

    def upload_partitions(
        self,
        layer_id: str,
        publication_id: str,
        body: dict,
        billing_tag: Optional[str] = None,
    ):
        """
        Upload partitions to the given layer. Depending on the publication type, post processing
        may be required before the partitions are published. For better performance batch your
        partitions (e.g. 1000 per request), rather than uploading them individually.

        :param layer_id: The ID of the layer to publish to.
        :param publication_id: The ID of publication to publish to.
        :param body: a dictionary with publication partitions. Data and DataHandle fields cannot
            be populated at the same time.
        :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}/publications/{publication_id}/partitions"
        url = self.format_url(self.base_url, path)
        params = {"billingTag": billing_tag}
        resp = self.post(url, data=body, params=params)
        if resp.status_code == 204:
            return
        else:
            raise PlatformException(resp)
