import datetime
import pprint
from collections.abc import MutableMapping
from typing import Iterator, Any

from bson.objectid import ObjectId

STATUS_NEW = 'new'
STATUS_PENDING = 'pending'
STATUS_FAILED = 'failed'
STATUS_SUCCESSFUL = 'successful'


class Task(MutableMapping):
    """
    Task objects are building blocks of their corresponding TaskQueue.

    Task is a mapping class which encapsulates attributes and data
    pertaining to a certain job.
    """

    def __init__(self,
                 _id: ObjectId = None,
                 assignedTo: str = None,
                 createdAt: float = None,
                 modifiedAt: float = None,
                 status: str = None,
                 errorMessage: str = None,
                 retries: int = 0,
                 priority: int = 0,
                 payload: Any = None):
        """
        Instantiates a Task instance.

        :param _id: the Task ObjectId generated by MongoDB
        :param assignedTo: the consumer assigned to the Task
        :param createdAt: the timestamp when the Task object was first created
        :param modifiedAt: the timestamp of the most recent change to the Task
        :param status: the Task status. It can take values from the following
                       list: ['new', 'pending', 'failed', 'successful']
        :param errorMessage: last error message occurred while
                               processing the Task
        :param retries: number of attempts to process the Task
        :param priority: Task priority based on which Tasks are pulled
                         from the TaskQueue
        :param payload: data payload which can be used to store data
                        with the Task
        """
        datetime_now = datetime.datetime.now()
        self._id = _id or ObjectId()
        self.assignedTo = assignedTo
        self.createdAt = createdAt or datetime_now.timestamp()
        self.modifiedAt = modifiedAt or datetime_now.timestamp()
        self.status = status or STATUS_NEW
        self.retries = retries
        self.errorMessage = errorMessage
        self.priority = priority
        self.payload = payload

    @property
    def object_id_(self):
        return self._id

    @property
    def assigned_to_(self):
        return self.assignedTo

    @property
    def created_at_(self):
        return self.createdAt

    @property
    def modified_at_(self):
        return self.modifiedAt

    @property
    def status_(self):
        return self.status

    @property
    def retries_(self):
        return self.retries

    @property
    def error_message_(self):
        return self.errorMessage

    @property
    def priority_(self):
        return self.priority

    @property
    def payload_(self):
        return self.payload

    def __setitem__(self, key, value) -> None:
        self.__setattr__(key, value)

    def __getitem__(self, key) -> Any:
        return self.__getattribute__(key)

    def __delitem__(self, key: str) -> None:
        self.__delattr__(key)

    def __len__(self) -> int:
        return len(self.__dict__)

    def __iter__(self) -> Iterator[str]:
        return iter(self.__dict__)

    def __repr__(self):
        attrs_ = dict(self.__dict__)
        attrs_['createdAt'] = datetime.datetime.fromtimestamp(
            attrs_['createdAt'])
        attrs_['modifiedAt'] = datetime.datetime.fromtimestamp(
            attrs_['modifiedAt'])
        return pprint.pformat(attrs_)
