# Copyright (C) 2019-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.

"""HERE Platform, utilities package
"""
from typing import Any, Dict, List


class JsonDictDocument:
    """
    A JSON document used in APIs loaded into a ``Dict``. Used as base class.

    Calling code can inspect the document via ``to_dict``. Subclasses typically add
    also read-only properties via the ``@property`` annotation to expose some
    of the most significant fields in a developer-friendly, typed, controlled way.

    Constructors provided by subclasses help the creation of complex JSON documents
    from significant fields, providing names, types and sensible defaults.

    Subclasses can also be created and initialized using ``from_dict``.
    """

    def __init__(self, json_dict: Dict[str, Any]):
        """
        Create a document from a JSON structure in a ``Dict``.

        This saves the document as-is so that subclasses
        can inspect it on the fly and extract the fields.

        :param json_dict: the JSON structure loaded into a ``Dict``
        """
        self._json = json_dict

    @property
    def json(self) -> Dict[str, Any]:
        """The JSON representation of the document in a python dictionary"""  # noqa
        return self._json

    def to_dict(self) -> Dict[str, Any]:
        """
        Expose the document as python dictionary.

        :return: the unchanged document as python dictionary
        """
        return self._json

    @classmethod
    def from_dict(cls, json_dict: Dict[str, Any]):
        """
        Create a subclass of ``JsonDictDocument`` and initialize its content
        using a JSON structure in a ``Dict`` already available.

        This method is used by the SDK to load JSON API responses into a document.

        Use this on subclasses of ``JsonDictDocument`` but not directly on ``JsonDictDocument``.
        Initialization of subclasses is skipped. Subclasses should not define
        any field or behavior (apart from calling the parent ``__init__``) in their constructor
        as these won't be defined when the subclass is created with this class method.

        :param json_dict: the JSON document loaded into a ``Dict``
        :return: A new object of the class specified, initialized using ``json_dict``
        """
        # It must be a proper subclass of this base class
        assert issubclass(cls, JsonDictDocument) and not issubclass(JsonDictDocument, cls)
        # create the subclass without initializing it, skipping its __init__
        new_document = cls.__new__(cls)
        # Set the JSON in the base class, calling __init__ of this class
        JsonDictDocument.__init__(new_document, json_dict)
        # return the newly-created and initialized subclass
        return new_document

    @property
    def fields(self) -> List:
        """
        Returns all available fields in the service

        :return: ``List`` of fields
        """
        return list(self._json.keys())

    def get_field(self, field_name: str) -> Any:
        """
        Get value of a field in service

        :param field_name: name of the field to be read
        :return: value of the field
        """
        return self._json[field_name]

    def has_field(self, field_name: str) -> bool:
        """
        Checks whether the field is present

        :param field_name: name of the field to be checked
        :return: ``True`` if field is present else ``False``
        """
        return field_name in self._json
