from typing import Optional, Type

from django_idm_api.exceptions import RoleNotFound

from staff.groups.models import (
    GROUP_TYPE_CHOICES,
    Group,
    GroupMembership,
    GroupResponsible,
)

from .base import BaseActions, filter_q_factory


get_group_filter_q = filter_q_factory({
    'type': GROUP_TYPE_CHOICES.WIKI,
    'level': 1,
    'autogrouptemplate': None,
    'intranet_status': 1,
})


def get_group_qs():
    return Group.objects.filter(get_group_filter_q())


class GroupActions(BaseActions):
    @staticmethod
    def get_objects(fields):
        groups = get_group_qs().values(*fields)
        return groups


class GroupRoleActions(BaseActions):
    role_model_cls: Optional[Type] = None

    @staticmethod
    def _check_group(group_id):
        if not get_group_qs().filter(id=group_id).exists():
            raise RoleNotFound('Role "group:%s" not found' % group_id)

    def add_role(self, person_id, role):
        self._check_group(role['group'])
        created = self.role_model_cls.objects.get_or_create(
            group_id=int(role['group']),
            staff_id=int(person_id),
        )[1]
        return created

    def remove_role(self, person_id, role):
        self._check_group(role['group'])
        gms = (
            self.role_model_cls.objects
            .filter(
                group_id=int(role['group']),
                staff_id=int(person_id),
            )
        )
        for gm in gms:
            gm.delete()

    def get_all_roles(self):
        gms = (
            self.role_model_cls.objects
            .filter(get_group_filter_q(prefix='group'))
            .values_list('staff__login', 'group_id')
            .order_by('pk')
        )

        for person_login, group_id in gms:
            key_crumbs = dict(self.key_crumbs)
            key_crumbs['group'] = str(group_id)
            yield person_login, key_crumbs


class GroupMemberActions(GroupRoleActions):
    role_model_cls = GroupMembership


class GroupResponsibleActions(GroupRoleActions):
    role_model_cls = GroupResponsible
