import logging

LOGGER = logging.getLogger('lightblue')


class LightBlueEntity(object):
    """
    Lightblue generic entity
     - all common action for all entities are specified here
     - for specific entity create new class with this class as parent
    """
    def __init__(self,
                 lightblue_service,
                 entity_name,
                 version=None,
                 *args,
                 **kwargs):

        super(LightBlueEntity, self).__init__(*args, **kwargs)
        self.service = lightblue_service
        self.entity_name = entity_name
        self.version = version

    @staticmethod
    def check_response(response):
        """
        Check LightBlue response. It always has to contain
            {
                'status': 'COMPLETE'
                ..
            }
        Args:
            response (dict): Lightblue API response

        Returns:
             bool - True if response is with no errors / False

        """
        if response is not None and \
           'status' in response and \
           response['status'] == 'COMPLETE':
            return True
        LOGGER.error('Received invalid response from Lightblue: %s', response)
        return False

    def get_schema(self, version=None):
        """
        Get schema for generic entity
        Args:
            version (str): entity version (if missing, default is used)

        Returns:
            - dict - json schema for entity

        """
        if version is None:
            if self.version is None:
                raise ValueError("No version was provided")
            return self.service.get_schema(self.entity_name, self.version)
        else:
            return self.service.get_schema(self.entity_name, version)

    def insert_data(self, data):
        """
        Insert data to generic entity (default projection is used)
        Args:
            data (dict/list): json objects for entity

        Returns:
            - dict - lightblue response

        """
        lightblue_data = {
            'objectType': self.entity_name,
            'data': data,
            'projection': {
                'field': '*',
                'include': True,
                'recursive': True

            }
        }
        if self.version is not None:
            lightblue_data['version'] = self.version
        return self.service.insert_data(self.entity_name, self.version,
                                        lightblue_data)

    def delete_all(self):
        """
        Delete all data for generic entity
        Returns:
            - dict - lightblue response

        """
        lightblue_data = {
            'objectType': self.entity_name,
            'query': {
                'field': 'objectType',
                'op': '=',
                'rvalue': self.entity_name
            }
        }
        if self.version is not None:
            lightblue_data['version'] = self.version
        return self.service.delete_data(self.entity_name, self.version,
                                        lightblue_data)

    def delete_item(self, query):
        """
        Delete specific object according to query
        Args:
            query (dict): query for delete

        Returns:
            - dict - lightblue response

        """
        lightblue_data = {
            'objectType': self.entity_name,
            'query': query
        }
        if self.version is not None:
            lightblue_data['version'] = self.version
        return self.service.delete_data(self.entity_name, self.version,
                                        lightblue_data)

    def update_item(self, query, update):
        """
        Update specific object according to query and update field
        Args:
            query (dict): query for searching data
            update (dict): update field (new values for given fields)

        Returns:
            - dict - lightblue response

        """
        lightblue_data = {
            'objectType': self.entity_name,
            'query': query,
            'update': update
        }
        if self.version is not None:
            lightblue_data['version'] = self.version
        return self.service.update_data(self.entity_name, self.version,
                                        lightblue_data)

    def find_item(self, query, projection=None, from_=None, max_results=None):
        """
        Find specific object according to query and projection field
        Args:
            query (dict): search query
            projection (list): specify field which will be returned
                               (default - return all)
            from_: from item in query
            max_results: limit results count in response

        Returns:
            - dict - result of search query

        """
        lightblue_data = {
            'objectType': self.entity_name,
            'query': query
        }
        if self.version is not None:
            lightblue_data['version'] = self.version
        if projection is None:
            projection = {
                'field': '*',
                'include': True,
                'recursive': True
            }
        if from_ is not None:
            lightblue_data['from'] = from_
        if max_results is not None:
            lightblue_data['maxResults'] = max_results
        lightblue_data['projection'] = projection
        return self.service.find_data(self.entity_name, self.version,
                                      lightblue_data)

    def find_all(self, projection=None, from_=None, max_results=None):
        """
        Find all objects of given entity
        Args:
            projection (list): custom projection (default return all items)
            from_: from item in query
            max_results: limit results count in response

        Returns:
            - dict - result of search query

        """
        lightblue_data = {
            'objectType': self.entity_name,
            'query': {
                'field': 'objectType',
                'op': '=',
                'rvalue': self.entity_name
            }
        }
        if self.version is not None:
            lightblue_data['version'] = self.version
        if projection is None:
            projection = {
                'field': '*',
                'include': True,
                'recursive': True
            }
        if from_ is not None:
            lightblue_data['from'] = from_
        if max_results is not None:
            lightblue_data['maxResults'] = max_results
        lightblue_data['projection'] = projection
        return self.service.find_data(self.entity_name, self.version,
                                      lightblue_data)

    def find_paginated(self, page_size, find, *args, **kwargs):
        """
        Get joined 'processed' key from paginated find calls

        Args:
            page_size (int): max results per LightBlue call
            find (Callable): find function (find_item / find_all)

        Returns:
            - list of processed items from multiple find calls

        """
        kwargs.update({
            "from_": 0,
            "max_results": page_size
        })

        processed = []
        while True:
            response = find(*args, **kwargs)
            if not self.check_response(response):
                return None
            elif len(response['processed']) == 0:
                return processed

            processed.extend(response['processed'])
            kwargs['from_'] += page_size
