import datetime
import json

from ..compat import basestring, integer_types


class BaseResource(object):
    """
    Data structure based on a becket resource
        https://github.com/phalt/beckett
    """

    class Meta:
        identifier = 'id'  # The key with which you uniquely identify this resource.
        attributes = {'id': 'id'}  # Acceptable attributes that you want to display in this resource.
        sub_resources = {}  # sub resources are complex attributes within a resource
        datetime_attributes = ()  # Attributes to be converted to datetime

    def __init__(self, **kwargs):
        self._datetime_sent = kwargs.pop('date_time_sent', None)
        self.streaming_unique_id = kwargs.pop('streaming_unique_id', None)
        self.publish_time = kwargs.pop('publish_time', None)

        now = datetime.datetime.utcnow()
        self.datetime_created = now
        self._datetime_updated = now
        self._sub_resource_map = getattr(self.Meta, 'sub_resources', {})
        self._data = kwargs
        self.set_attributes(**kwargs)
        self._data['Latency'] = self.elapsed_time

    def set_sub_resources(self, **kwargs):
        """
        For each sub resource assigned to this resource, generate the
        sub resource instance and set it as an attribute on this instance.
        """
        for attribute_name, resource in self._sub_resource_map.items():
            sub_attr = kwargs.get(attribute_name)
            if sub_attr:
                if isinstance(sub_attr, list):
                    value = [resource(**x) for x in sub_attr]  # A list of sub resources is supported
                else:
                    value = resource(**sub_attr)  # So is a single resource
                setattr(self, resource.Meta.identifier, value)
            else:
                setattr(self, resource.Meta.identifier, [])  # [] = Empty resource

    def set_attributes(self, **kwargs):
        """
        Set the resource attributes from the kwargs.
        Only sets items in the `self.Meta.attributes` white list.
        Subclass this method to customise attributes.
        """
        if self._sub_resource_map:
            self.set_sub_resources(**kwargs)
            for key in self._sub_resource_map.keys():
                kwargs.pop(key, None)  # Don't let these attributes be overridden later
        for field, value in kwargs.items():
            if field in self.Meta.attributes:
                if field in self.Meta.datetime_attributes:
                    value = self.strip_datetime(value) or value
                setattr(self, self.Meta.attributes[field], value)

    def json(self):
        return self._data

    def message(self):
        return json.dumps(self._data)

    @staticmethod
    def strip_datetime(value):
        """
        Converts value to datetime if string or int.
        """
        if isinstance(value, basestring):
            try:
                return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%fZ")
            except ValueError:
                return
        elif isinstance(value, integer_types):
            try:
                return datetime.datetime.utcfromtimestamp(value / 1e3)
            except (ValueError, OverflowError, OSError):
                return

    @property
    def elapsed_time(self):
        """
        Elapsed time between datetime sent and datetime created
        """
        if self._datetime_sent:
            return (self.datetime_created-self._datetime_sent).total_seconds()

    def __getattr__(self, item):
        """
        If item is an expected attribute in Meta
        return None, if not raise Attribute error.
        """
        if item in self.Meta.attributes.values():
            return
        else:
            return self.__getattribute__(item)

    def __repr__(self):
        return '<%s>' % self.__class__.__name__

    def __str__(self):
        return self.__class__.__name__
