import json
import logging
from datetime import datetime

from django.db.models import Q
from staff.lib.db import atomic


from staff.person.models import Staff
from staff.groups.models import Group, GroupMembership
from .models import AutoGroupTemplate


logger = logging.getLogger('staff.autogroup')


def load_query(condition):
    if condition:
        return json.loads(condition)
    else:
        return []


class AutogroupUpdater:

    def __init__(self, template, verbosity=0):
        self.template = template
        self.verbosity = verbosity

    def load_query(self, condition_type):
        condition = getattr(self.template, condition_type).strip()
        condition = load_query(condition)
        return condition

    def get_query(self, condition_type):
        query = self.load_query(condition_type)
        qq = Q()
        for q in query:
            qq |= Q(**q)
        return qq

    def get_necessary(self):
        filter_query = self.get_query('condition')
        exclude_query = self.get_query('exclusion')

        queryset = (
            Staff.objects
            .filter(filter_query)
            .exclude(exclude_query)
            .values_list('id', flat=True)
        )
        return queryset

    def get_available(self):
        queryset = (
            GroupMembership.objects
            .filter(group=self.group)
            .values_list('staff_id', flat=True)
        )
        return queryset

    @atomic
    def init_group(self):
        if self.template.group is None:
            # TODO: через контроллер
            now = datetime.now()
            self.template.group = Group.objects.create(
                url=self.template.code,
                code=self.template.code,
                name=self.template.name,
                created_at=now,
                modified_at=now,
                parent=Group.objects.get(url='__wiki__'),
            )
            self.template.save()
            self.log_info_message(
                'New autogroup "%s" created',
                self.template.group.url
            )

        self.group = self.template.group

    def update(self):
        self.init_group()
        self.update_group_members()

    def update_group_members(self):
        necessary = set(self.get_necessary())
        available = set(self.get_available())

        to_create = necessary - available
        to_delete = available - necessary

        for person_id in to_create:
            GroupMembership.objects.create(
                group=self.group, staff_id=person_id
            )

        to_delete_members = (
            GroupMembership.objects
            .filter(group=self.group, staff__in=to_delete)
        )
        for member in to_delete_members:
            member.delete()

        msg = (
            'Autogroup template "%s" (%s) processed.'
            ' Autogroup\'s "%s" (%s) membership updated'
            ' new: %d, deleted: %d, total: %d'
        )
        self.log_info_message(
            msg,
            self.template.name,
            self.template.id,
            self.group.url,
            self.group.id,
            len(to_create),
            len(to_delete),
            len(available) + len(to_create) - len(to_delete),
        )

    def log_info_message(self, message, *args):
        if self.verbosity:
            print(message % args)
        logger.info(message, *args)


def updated_autogroups(template_ids=None, verbosity=1):

    templates = AutoGroupTemplate.objects.all()
    if template_ids is not None:
        templates = templates.filter(id__in=template_ids)

    for template in templates.order_by('id'):
        try:
            AutogroupUpdater(template, verbosity).update()
        except Exception:
            msg = 'Exception when process autogroup template "%s" (%s)'
            if verbosity:
                import traceback
                print(msg % (template.name, template.id))
                traceback.print_exc()
            logger.exception(msg, template.name, template.id)
