# -*- coding: utf-8 -*-

from django.db import models

from polynum.base.models import (Request, Transition, RoleAssociation,
        Entity, User, Role)

from .. import utils

def role_associations(user):
    return RoleAssociation.objects.filter(models.Q(user=user) |
            models.Q(user__delegations_given__user_delegated=user))

def get_entity_filter(user):
    '''Get entity filter for user

       - If the user is associated with a role, she can see requests for
         this associations, i.e. whose entity is child of the entity in
         her association,
       - If the user has done request, he can see parent entities for the
         entity its requests are linked to,
       - else she sees all entities in the listing.
    '''
    q = []
    query = models.Q(request__user=user)|models.Q(roleassociation__user=user)
    for entity in Entity.objects.filter(query).distinct():
        q.append(models.Q(left_bound__gte=entity.left_bound,
            left_bound__lt=entity.right_bound))
        q.append(models.Q(left_bound__lte=entity.left_bound,
            right_bound__gte=entity.right_bound))
    if not q:
        return models.Q()
    return reduce(models.Q.__or__, q)

def get_workable_requests(user):
    '''Retrieve requests and their actions:
       - whose entity the user has a role over or is a child of an entity the
         user has a role over,
       - whose status the user has power to do some action upon;

       The return value is a queryset, with each request present as many times
       as their is actions that the user can do upon them. The action id and
       name are attached to the request object.

        >>> requests = get_workable_requests(user)
        >>> for request in requests:
                print request.action_id
                print request.action_name
    '''
    ras =  role_associations(user).select_related('entity', 'role')
    filters = [models.Q(status__visible_by=ra.role,
            entity__left_bound__gte=ra.entity.left_bound,
            entity__left_bound__lt=ra.entity.right_bound) for ra in ras]
    filters.append(models.Q(user=user))
    q = reduce(models.Q.__or__, filters)
    return Request.objects.filter(q).distinct()

def get_workflows(user, request):
    return get_workflows_by_entity_status_owner(
            user, request.entity, request.status, request.user == user)


def get_actors(status, entity):
    '''Retrieve people able to operate on a request in a given status:
       - firs retrieve transition coming from this status goring to a different one,
       - then retrieve roles linked to those transitions,
       - then retrieve parents of this entity,
       - finally retrieve role associations on one of these entities and for one of those roles.
    '''
    transitions = Transition.objects.filter(source=status).exclude(destination=status)
    roles = Role.objects.filter(transition__in=transitions)
    entities = entity.parents(included=True)
    role_associations = RoleAssociation.objects.filter(role__in=roles,
            entity__in=entities)
    return User.objects.filter(roleassociation__in=role_associations).distinct()


@utils.cache_to_django(timeout=30)
def get_workflows_by_entity_status_owner(user, entity, status, owner=False):
    filters = []
    if entity is not None:
        filters.append(models.Q(source=status,
                role__roleassociation__user=user,
                role__roleassociation__entity__left_bound__lte=entity.left_bound,
                role__roleassociation__entity__right_bound__gte=entity.right_bound))
        filters.append(models.Q(source=status,
                role__roleassociation__user__delegations_given__user_delegated=user,
                role__roleassociation__entity__left_bound__lte=entity.left_bound,
                role__roleassociation__entity__right_bound__gte=entity.right_bound))
    if owner:
       filters.append(models.Q(role__name='Demandeur', source=status))
    if filters:
        return Transition.objects.filter(reduce(models.Q.__or__, filters)).distinct()
    else:
        return Transition.objects.none()
