# -*- coding: utf-8 -*-
from collections import defaultdict
from django.db.models import F
from django.conf import settings
from django.contrib.auth.models import Permission, Group
from django.contrib.contenttypes.models import ContentType
from guardian.models import UserObjectPermission, GroupObjectPermission
from ninja import Schema

from events.accounts.models import User
from events.common_app.models import PermissionLog
from events.idm import utils


FORM_ACCESS_TYPE_COMMON = 'common'
FORM_ACCESS_TYPE_OWNER = 'owner'
FORM_ACCESS_TYPE_RESTRICTED = 'restricted'
DEFAULT_ACTION_TYPE = 'change'


def get_change_permission(obj, action_type=None):
    action_type = action_type or DEFAULT_ACTION_TYPE
    if isinstance(obj, ContentType):
        app_label = obj.app_label
        model = obj.model
    else:
        app_label = obj._meta.app_label
        model = obj._meta.model_name
    return Permission.objects.get(
        content_type__app_label=app_label,
        codename=f'{action_type}_{model}',
    )


def get_object_permissions(obj):
    if isinstance(obj, ContentType):
        app_label = obj.app_label
        model = obj.model
    else:
        app_label = obj._meta.app_label
        model = obj._meta.model_name
    return Permission.objects.filter(
        content_type__app_label=app_label,
        codename__in=(f'change_{model}', f'viewfile_{model}'),
    )


def get_common_group(org=None):
    """
    В б2б возвращаем группу 'Все сотрудники', а для остальных инстансов группу Яндекс
    """
    if settings.IS_BUSINESS_SITE:
        if org and org.o2g:
            return org.o2g.group
    else:
        return Group.objects.get(pk=settings.GROUP_ALLSTAFF_PK)


def get_groups_from_db(group_ids):
    from events.staff.models import StaffGroup
    return list(
        StaffGroup.objects.filter(staff_id__in=group_ids)
        .annotate(id=F('staff_id'))
    )


def get_staff_groups(group_names):
    groups = []
    for group_name in group_names:
        group_id = utils.get_group_id(group_name)
        if group_id:
            groups.append(group_id)
    if groups:
        return get_groups_from_db(groups)


def get_survey_access(obj, optimize=True):
    ct = ContentType.objects.get_for_model(obj)
    perms = get_object_permissions(ct)

    group_qs = (
        GroupObjectPermission.objects.filter(
            content_type=ct, object_pk=str(obj.pk),
            permission__in=perms,
        )
        .select_related('group', 'permission')
    )
    groups = defaultdict(list)
    for it in group_qs:
        groups[it.permission.codename].append(it.group)

    user_qs = (
        UserObjectPermission.objects.filter(
            content_type=ct, object_pk=str(obj.pk),
            permission__in=perms,
        )
        .select_related('user', 'permission')
    )
    users = defaultdict(list)
    for it in user_qs:
        users[it.permission.codename].append(it.user)

    result = {}
    for perm in perms:
        _users = users.get(perm.codename, [])
        _groups = groups.get(perm.codename, [])

        if not _users and not _groups:
            continue

        if len(_users) == 1 and not _groups:
            if _users[0] == obj.user:
                result[perm.codename] = {'type': FORM_ACCESS_TYPE_OWNER}
                continue

        common_group = get_common_group(obj.org)
        if len(_groups) == 1 and _groups[0] == common_group:
            result[perm.codename] = {'type': FORM_ACCESS_TYPE_COMMON}
            continue

        staff_groups = get_staff_groups(it.name for it in _groups)
        result[perm.codename] = {
            'type': FORM_ACCESS_TYPE_RESTRICTED,
            'users': [it for it in _users if it != obj.user],
            'groups': staff_groups,
        }
    if len(result) == 1 and optimize:
        result = list(result.values())[0]
    return result


def request_roles(request_user, perm, users=None, groups=None, content_type=None, object_pk=None):
    users = users or []
    if users:
        user_permission_list = [
            UserObjectPermission(
                permission=perm,
                content_type=content_type, object_pk=str(object_pk),
                user=user,
            )
            for user in users
            if user
        ]
        if user_permission_list:
            UserObjectPermission.objects.bulk_create(user_permission_list, ignore_conflicts=True)

    groups = groups or []
    if groups:
        group_permission_list = [
            GroupObjectPermission(
                permission=perm,
                content_type=content_type, object_pk=str(object_pk),
                group=group,
            )
            for group in groups
            if group
        ]
        if group_permission_list:
            GroupObjectPermission.objects.bulk_create(group_permission_list, ignore_conflicts=True)

    utils.save_permission_log(
        request_user, perm.codename, PermissionLog.ASSIGN,
        users=users, groups=groups, object_pk=object_pk,
    )

    if not settings.IS_BUSINESS_SITE:
        utils.IdmUtils().request_roles(
            perm.codename,
            users=[user.username for user in users],
            groups=[utils.get_group_id(group.name) for group in groups],
            object_pk=object_pk,
        )


def reject_roles(request_user, perm, users=None, groups=None, content_type=None, object_pk=None):
    users = users or []
    if users:
        user_permission_qs = (
            UserObjectPermission.objects.filter(
                permission=perm,
                content_type=content_type, object_pk=str(object_pk),
                user__in=users,
            )
        )
        user_permission_qs.delete()

    groups = groups or []
    if groups:
        group_permission_qs = (
            GroupObjectPermission.objects.filter(
                permission=perm,
                content_type=content_type, object_pk=str(object_pk),
                group__in=groups,
            )
        )
        group_permission_qs.delete()

    utils.save_permission_log(
        request_user, perm.codename, PermissionLog.REMOVE,
        users=users, groups=groups, object_pk=object_pk,
    )

    if not settings.IS_BUSINESS_SITE:
        utils.IdmUtils().reject_roles(
            perm.codename,
            users=[user.username for user in users],
            groups=[utils.get_group_id(group.name) for group in groups],
            object_pk=object_pk,
        )


def get_or_create_groups(group_ids):
    return [
        utils.get_group(group_id, create=True)
        for group_id in group_ids or []
    ]


def get_or_create_users(user_uids, org):
    users = []
    dir_id = org.dir_id if org else None
    for it in user_uids or []:
        uid = None
        cloud_uid = None
        if isinstance(it, (str, int)):
            uid = str(it)
        elif isinstance(it, dict):
            uid = it.get('uid')
            cloud_uid = it.get('cloud_uid')
        elif isinstance(it, Schema):
            uid = getattr(it, 'uid', None)
            cloud_uid = getattr(it, 'cloud_uid', None)
        if uid or cloud_uid:
            user = User.objects.get_or_create_user(
                uid, cloud_uid, dir_id,
                link_organizations=False,
            )
            users.append(user)
    return users


def set_owner_access(request_user, obj, action_type=None):
    ct = ContentType.objects.get_for_model(obj)
    perm = get_change_permission(ct, action_type)
    reject_users = (
        UserObjectPermission.objects.filter(
            content_type=ct, object_pk=str(obj.pk),
            permission=perm,
        )
        .exclude(user=obj.user)
        .select_related('user')
    )
    reject_groups = (
        GroupObjectPermission.objects.filter(
            content_type=ct, object_pk=str(obj.pk),
            permission=perm,
        )
        .select_related('group')
    )
    request_roles(
        request_user, perm,
        users=[obj.user],
        content_type=ct, object_pk=obj.pk,
    )
    reject_roles(
        request_user, perm,
        users=[it.user for it in reject_users],
        groups=[it.group for it in reject_groups],
        content_type=ct, object_pk=obj.pk,
    )


def set_common_access(request_user, obj, action_type=None):
    ct = ContentType.objects.get_for_model(obj)
    perm = get_change_permission(ct, action_type)
    common_group = get_common_group(obj.org)
    reject_users = (
        UserObjectPermission.objects.filter(
            content_type=ct, object_pk=str(obj.pk),
            permission=perm,
        )
        .exclude(user=obj.user)
        .select_related('user')
    )
    reject_groups = (
        GroupObjectPermission.objects.filter(
            content_type=ct, object_pk=str(obj.pk),
            permission=perm,
        )
        .exclude(group=common_group)
        .select_related('group')
    )
    request_roles(
        request_user, perm,
        groups=[common_group],
        content_type=ct, object_pk=obj.pk,
    )
    reject_roles(
        request_user, perm,
        users=[it.user for it in reject_users],
        groups=[it.group for it in reject_groups],
        content_type=ct, object_pk=obj.pk,
    )


def set_restricted_access(request_user, obj, users, groups, action_type=None):
    ct = ContentType.objects.get_for_model(obj)
    perm = get_change_permission(ct, action_type)
    reject_users = (
        UserObjectPermission.objects.filter(
            content_type=ct, object_pk=str(obj.pk),
            permission=perm,
        )
        .exclude(user=obj.user)
        .exclude(user__in=users)
        .select_related('user')
    )
    reject_groups = (
        GroupObjectPermission.objects.filter(
            content_type=ct, object_pk=str(obj.pk),
            permission=perm,
        )
        .exclude(group__in=groups)
        .select_related('group')
    )
    request_roles(
        request_user, perm,
        users=users, groups=groups,
        content_type=ct, object_pk=obj.pk,
    )
    reject_roles(
        request_user, perm,
        users=[it.user for it in reject_users],
        groups=[it.group for it in reject_groups],
        content_type=ct, object_pk=obj.pk,
    )


def set_survey_access(request_user, obj, access_type, users=None, groups=None, action_type=None):
    if access_type == FORM_ACCESS_TYPE_OWNER:
        set_owner_access(request_user, obj, action_type)
    elif access_type == FORM_ACCESS_TYPE_COMMON:
        set_common_access(request_user, obj, action_type)
    elif access_type == FORM_ACCESS_TYPE_RESTRICTED:
        _users = get_or_create_users(users, obj.org)
        _groups = get_or_create_groups(groups)
        set_restricted_access(request_user, obj, _users, _groups, action_type)
