# coding: utf-8

from collections import OrderedDict, defaultdict

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.core.cache import cache
from django.core.exceptions import ImproperlyConfigured
from django.db.models import F
from django_idm_api.exceptions import RoleNotFound, UserNotFound
from django_idm_api.hooks import AuthHooks

from procu.api import models
from .pagination import RolesPagination

User = get_user_model()


class Hooks(AuthHooks):
    def _get_user(self, login):
        try:
            return User.objects.get(
                username=login, is_staff=True, is_deleted=False
            )

        except User.DoesNotExist:
            raise UserNotFound('User does not exist: %s' % login)

    def _get_group(self, group_name):
        try:
            return self.get_group_queryset().get(name=group_name)

        except Group.DoesNotExist:
            raise RoleNotFound('Group does not exist: %s' % group_name)

    def info_impl(self, **kwargs):
        roles = OrderedDict()

        for group in self.get_group_queryset():
            name = group.name

            try:
                roles[name] = settings.ROLES[name]['meta']

            except KeyError:
                # Group may be unused
                pass

        cfos = models.OracleCFO.objects.values('key', 'name')

        roles['cfos'] = {
            'name': {'ru': 'Роли на ЦФО', 'en': 'CFO roles'},
            'roles': {
                'slug': 'cfo',
                'name': {'ru': 'ЦФО', 'en': 'CFO'},
                'values': {
                    cfo['key']: {
                        'name': f"{cfo['key']}: {cfo['name']}",
                        'roles': {
                            'slug': 'cfo-role',
                            'name': {'ru': 'роль', 'en': 'role'},
                            'values': {
                                'access': {
                                    'name': {'ru': 'Доступ', 'en': 'Access'}
                                }
                            },
                        },
                    }
                    for cfo in cfos
                },
            },
        }

        return roles

    def add_role_impl(self, login, role, fields, **kwargs):

        role_name = role.get('role')
        if not role_name:
            raise RoleNotFound('Role is not provided')

        if role_name == 'cfos' and role.get('cfo-role') == 'access':
            # CFO roles
            key = role.get('cfo', None)

            try:
                cfo = models.OracleCFO.objects.get(key=key)
                user = User.objects.get_or_create_by_login(login)
                cfo.users.add(user)

            except models.OracleCFO.DoesNotExist:
                raise RoleNotFound('CFO does not exist: %s' % key)

        else:
            # User roles
            group = self._get_group(role_name)
            models.UserGroup.objects.get_or_create(username=login, group=group)
            cache.delete(('perms', login))

    def remove_role_impl(self, login, role, data, is_fired, **kwargs):
        role_name = role.get('role')
        if not role_name:
            raise RoleNotFound('Role is not provided')

        if role_name == 'cfos' and role.get('cfo-role') == 'access':
            # CFO roles
            key = role.get('cfo', None)

            models.OracleCFO.users.through.objects.filter(
                user__username=login, oraclecfo__key=key
            ).delete()

        else:
            group = self._get_group(role_name)
            models.UserGroup.objects.filter(
                username=login, group=group
            ).delete()
            cache.delete(('perms', login))

    def get_user_roles_impl(self, login, **kwargs):
        raise ImproperlyConfigured("``get_user_roles_impl' is outdated")

    def get_all_roles_impl(self, **kwargs):

        user_groups = models.UserGroup.objects.values_list(
            'username', 'group__name'
        ).order_by('username')

        roles = defaultdict(list)

        for username, group in user_groups:
            roles[username].append({'role': group})

        cfos = models.OracleCFO.users.through.objects.values_list(
            'user__username', 'oraclecfo__key'
        )

        for username, cfo in cfos:
            roles[username].append(
                {'role': 'cfos', 'cfo': cfo, 'cfo-role': 'access'}
            )

        return roles.items()

    def get_roles(self, request):

        from django.db.models import Value, CharField

        user_qs = models.UserGroup.objects.order_by('pk').values(
            login=F('username'),
            role=F('group__name'),
            type=Value('user', output_field=CharField()),
        )

        cfo_qs = models.OracleCFO.users.through.objects.order_by('pk').values(
            login=F('user__username'),
            role=F('oraclecfo__key'),
            type=Value('cfo', output_field=CharField()),
        )

        qs = cfo_qs.union(user_qs).order_by('login')

        paginator = RolesPagination()

        request.query_params = request.GET

        paginated = paginator.paginate_queryset(qs, request)

        roles = []

        tpls = {
            'user': '/role/%s/',
            'cfo': '/role/cfos/cfo/%s/cfo-role/access/',
        }

        for role in paginated:
            roles.append(
                {
                    'login': role['login'],
                    'role': tpls[role['type']] % role['role'],
                }
            )

        page = OrderedDict([('code', 0), ('roles', roles)])

        next_url = paginator.get_next_link()
        if next_url:
            page['next-url'] = next_url

        return page
