from bitfield import BitField
from django.db import models

from orchestra.models.communication.mixins import CommunicationPreferenceMixin
from orchestra.models.communication.mixins import StaffBotRequestMixin
from orchestra.models.communication.mixins import StaffingRequestInquiryMixin
from orchestra.models.communication.mixins import StaffingResponseMixin
from orchestra.models.communication.managers \
    import CommunicationPreferenceManager
from orchestra.models.core.models import Task
from orchestra.models.core.models import Worker
from orchestra.utils.models import BaseModel
from orchestra.utils.models import ChoicesEnum


class CommunicationPreference(CommunicationPreferenceMixin, BaseModel):
    """
        A CommunicationPreference object defines how a Worker would like to
        contacted for a given CommunicationType.

        Attributes:
            worker (orchestra.models.Worker):
                Django user that the preference represents.
            methods (BitField):
                The ways in which the user would like to be contacted.
            type (CommunicationType):
                The type of communication to which this preference applies.
    """
    objects = CommunicationPreferenceManager()

    class CommunicationMethods:
        SLACK = 'slack'
        EMAIL = 'email'

    COMMUNICATION_METHODS = (
        (CommunicationMethods.SLACK, 'Slack'),
        (CommunicationMethods.EMAIL, 'Email'),
    )

    class CommunicationType(ChoicesEnum):
        TASK_STATUS_CHANGE = 'task_status_change'
        NEW_TASK_AVAILABLE = 'new_task_available'

    COMMUNICATION_TYPE_DESCRIPTIONS = {
        CommunicationType.TASK_STATUS_CHANGE:
        {
            'short_description': 'Task Status Changes',
            'long_description':
            """
            When a task status changes (e.g., when it moves from in
            review to returned from reviewer), you will automatically
            receive a notification in the project Slack group. Select
            whether you would like to receive an email notification as
            well.
            """
        },
        CommunicationType.NEW_TASK_AVAILABLE:
        {
            'short_description': 'New Tasks',
            'long_description':
            """
            Select your preferred channel for being notified when a
            new task is available to work on.
            """
        }
    }

    worker = models.ForeignKey(Worker)
    methods = BitField(flags=COMMUNICATION_METHODS,
                       blank=True, null=True, default=None)
    communication_type = models.IntegerField(
        choices=CommunicationType.choices())

    class Meta:
        app_label = 'orchestra'
        unique_together = (('worker', 'communication_type'),)


class StaffBotRequest(StaffBotRequestMixin, BaseModel):
    """
    A StaffBotRequest object defines a new task that needs
    a worker for a specific role.

    Attributes:
        task (orchestra.models.Task):
            The task that needs a worker assignment
        required_role_counter (int):
            Counter that tells which expert is a new worker
            going to be on a task
        request_cause (RequestCause):
            The cause for request
        project_description (str):
            Description of the project
        status (Status)
            Status of the request
        last_inquiry_sent (DateTimeField):
            The datetime the most recent inquiry was sent.
    """
    class RequestCause(ChoicesEnum):
        USER = 'user'
        AUTOSTAFF = 'autostaff'
        RESTAFF = 'restaff'

    class Status(ChoicesEnum):
        PROCESSING = 'processing'
        COMPLETE = 'complete'

    task = models.ForeignKey(Task, related_name='staffing_requests')
    required_role_counter = models.IntegerField()
    request_cause = models.IntegerField(choices=RequestCause.choices())
    project_description = models.TextField(null=True, blank=True)
    status = models.IntegerField(
        default=Status.PROCESSING.value,
        choices=Status.choices())
    last_inquiry_sent = models.DateTimeField(null=True)


class StaffingRequestInquiry(StaffingRequestInquiryMixin, BaseModel):
    """
    A StaffingRequestInquiry object defines how a Worker was contacted to
    work on a StaffBotRequest

    Attributes:
        communication_preference (orchestra.models.CommunicationPreference):
            Django user that the request is sent to
        request (orchestra.models.StaffBotRequest):
            Request object associated with inquiry
        communication_method (CommunicationMethod):
            Method by which a worker is going to be contacted
    """

    class CommunicationMethod(ChoicesEnum):
        SLACK = 'slack'
        EMAIL = 'email'

    request = models.ForeignKey(StaffBotRequest,
                                null=True,
                                related_name='inquiries')
    communication_preference = models.ForeignKey(CommunicationPreference)
    communication_method = models.IntegerField(
        choices=CommunicationMethod.choices())


class StaffingResponse(StaffingResponseMixin, BaseModel):
    """
    A StaffingResponse object stores a response from a worker and
    whether the worker got the task.

    Attributes:
        request_inquiry (orchestra.models.StaffingRequestInquiry):
            Request inquiry associated with a response
        response_text (str):
            Response text that a Worker provided
        is_available (bool):
            True if a Worker is ready to work on the Task
        is_winner (bool):
            True if a Worker was selected to work on the Task
    """
    request_inquiry = models.ForeignKey(StaffingRequestInquiry,
                                        related_name='responses')
    response_text = models.TextField(blank=True, null=True)
    is_available = models.BooleanField()
    is_winner = models.NullBooleanField()
