# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
#     http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
"""Placeholder docstring"""

from smexperiments import _boto_functions, _utils


class ApiObject(object):
    """
    A Python class representation of a boto API object. Converts boto dicts of 'UpperCamelCase' names
    to dicts into/from a Python object with standard python members. Clients invoke to_boto on an instance
     of ApiObject to transform the ApiObject into a boto representation. Clients invoke from_boto on a sub-class of
     ApiObject to instantiate an instance of that class from a boto representation.
    """

    # A map from boto 'UpperCamelCase' name to member name. If a boto name does not appear in this dict then
    # it is converted to lower_snake_case.
    _custom_boto_names = {}

    # A map from name to an ApiObject subclass. Allows ApiObjects to contain ApiObject members.
    _custom_boto_types = {}

    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    @classmethod
    def _boto_ignore(cls):
        return ["ResponseMetadata"]

    @classmethod
    def from_boto(cls, boto_dict, **kwargs):
        """Construct an instance of this ApiObject from a boto response.

        Args:
            boto_dict (dict): A dictionary of a boto response.
            **kwargs: Arbitrary keyword arguments
        """
        boto_dict = {k: v for k, v in boto_dict.items() if k not in cls._boto_ignore()}
        custom_boto_names_to_member_names = {a: b for b, a in cls._custom_boto_names.items()}
        cls_kwargs = _boto_functions.from_boto(boto_dict, custom_boto_names_to_member_names, cls._custom_boto_types)
        cls_kwargs.update(kwargs)
        return cls(**cls_kwargs)

    @classmethod
    def to_boto(cls, obj):
        """Convert an object to a boto representation.

        Args:
            obj (dict): The object to convert to boto.
        """
        if not isinstance(obj, dict):
            var_dict = vars(obj)
        else:
            var_dict = obj
        return _boto_functions.to_boto(var_dict, cls._custom_boto_names, cls._custom_boto_types)

    def __eq__(self, other):
        """Returns true if this ApiObject equals other."""
        if isinstance(other, self.__class__):
            return self.__dict__ == other.__dict__
        return False

    def __ne__(self, other):
        """Returns true if this ApiObject does not equal other."""
        return not self.__eq__(other)

    def __hash__(self):
        """Returns a hashcode for this ApiObject."""
        return hash(tuple(sorted(self.__dict__.items())))

    def __repr__(self):
        """Returns a string representation of this ApiObject."""
        return "{}({})".format(
            type(self).__name__,
            ",".join(["{}={}".format(k, repr(v)) for k, v in vars(self).items()]),
        )


class Record(ApiObject):
    """A boto based Active Record class based on convention over Create/Read/Update/Delete operations."""

    # update / delete / list method names
    _boto_update_method = None
    _boto_delete_method = None
    _boto_list_method = None

    # List of member names to convert to boto representations and pass to the update method.
    _boto_update_members = []

    # List of member names to convert to boto representations and pass to the delete method.
    _boto_delete_members = []

    def __init__(self, sagemaker_boto_client, **kwargs):
        self.sagemaker_boto_client = sagemaker_boto_client
        super(Record, self).__init__(**kwargs)

    @classmethod
    def _list(
        cls,
        boto_list_method,
        list_item_factory,
        boto_list_items_name,
        boto_next_token_name="NextToken",
        sagemaker_boto_client=None,
        **kwargs
    ):
        sagemaker_boto_client = sagemaker_boto_client or _utils.sagemaker_client()
        next_token = None
        try:
            while True:
                list_request_kwargs = _boto_functions.to_boto(kwargs, cls._custom_boto_names, cls._custom_boto_types)
                if next_token:
                    list_request_kwargs[boto_next_token_name] = next_token
                list_method = getattr(sagemaker_boto_client, boto_list_method)
                list_method_response = list_method(**list_request_kwargs)
                list_items = list_method_response.get(boto_list_items_name, [])
                next_token = list_method_response.get(boto_next_token_name)
                for item in list_items:
                    yield list_item_factory(item)
                if not next_token:
                    break
        except StopIteration:
            return

    @classmethod
    def _search(
        cls,
        search_resource,
        search_item_factory,
        boto_next_token_name="NextToken",
        sagemaker_boto_client=None,
        **kwargs
    ):
        sagemaker_boto_client = sagemaker_boto_client or _utils.sagemaker_client()
        next_token = None
        try:
            while True:
                search_request_kwargs = _boto_functions.to_boto(kwargs, cls._custom_boto_names, cls._custom_boto_types)
                search_request_kwargs["Resource"] = search_resource
                if next_token:
                    search_request_kwargs[boto_next_token_name] = next_token
                search_method = getattr(sagemaker_boto_client, "search")
                search_method_response = search_method(**search_request_kwargs)
                search_items = search_method_response.get("Results", [])
                next_token = search_method_response.get(boto_next_token_name)
                for item in search_items:
                    if cls.__name__ in item:
                        yield search_item_factory(item[cls.__name__])
                if not next_token:
                    break
        except StopIteration:
            return

    @classmethod
    def _construct(cls, boto_method_name, sagemaker_boto_client=None, **kwargs):
        sagemaker_boto_client = sagemaker_boto_client or _utils.sagemaker_client()
        instance = cls(sagemaker_boto_client, **kwargs)
        return instance._invoke_api(boto_method_name, kwargs)

    def with_boto(self, boto_dict):
        """Update this ApiObject with a boto response.

        Args:
            boto_dict (dict): A dictionary of a boto response.
        """
        custom_boto_names_to_member_names = {a: b for b, a in self._custom_boto_names.items()}
        self.__dict__.update(
            **_boto_functions.from_boto(boto_dict, custom_boto_names_to_member_names, self._custom_boto_types)
        )
        return self

    def _invoke_api(self, boto_method, boto_method_members):
        api_values = {k: v for k, v in vars(self).items() if k in boto_method_members}
        api_kwargs = self.to_boto(api_values)
        api_method = getattr(self.sagemaker_boto_client, boto_method)
        api_boto_response = api_method(**api_kwargs)
        return self.with_boto(api_boto_response)