import logging
import traceback

from django.db import models
from django.conf import settings
from django.contrib.auth.models import Group, Permission
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist

from guardian.utils import get_user_obj_perms_model
from guardian.shortcuts import assign_perm, get_groups_with_perms
from guardian.models import UserObjectPermission, GroupObjectPermission

from geonode.base.models import ResourceBase
from geonode.groups.models import GroupProfile

logger = logging.getLogger(__name__)


class Geocollection(models.Model):
    """
    A collection is a set of resources linked to a GeoNode group
    """
    group = models.ForeignKey(GroupProfile, related_name='group_collections', on_delete=models.CASCADE)
    resources = models.ManyToManyField(ResourceBase, related_name='resource_collections')
    name = models.CharField(max_length=128, unique=True)
    slug = models.SlugField(max_length=128, unique=True)

    def get_users_with_perms(self):
        """
        Override of the Guardian get_users_with_perms
        """
        ctype = ContentType.objects.get_for_model(self)
        permissions = {}

        for perm in Permission.objects.filter(codename='access_geocollection', content_type_id=ctype.id):
            permissions[perm.id] = perm.codename

        user_model = get_user_obj_perms_model(self)
        users_with_perms = user_model.objects \
            .filter(
                object_pk=self.pk,
                content_type_id=ctype.id,
                permission_id__in=permissions) \
            .values('user_id', 'permission_id')

        users = {}
        for item in users_with_perms:
            if item['user_id'] in users:
                users[item['user_id']].append(permissions[item['permission_id']])
            else:
                users[item['user_id']] = [permissions[item['permission_id']], ]

        profiles = {}
        for profile in get_user_model().objects.filter(id__in=list(users.keys())):
            profiles[profile] = users[profile.id]

        return profiles

    def remove_object_permissions(self):
        UserObjectPermission.objects \
            .filter(content_type=ContentType.objects.get_for_model(self),
                    object_pk=self.id) \
            .delete()
        GroupObjectPermission.objects \
            .filter(content_type=ContentType.objects.get_for_model(self),
                    object_pk=self.id) \
            .delete()

    def set_default_permissions(self):
        """
        Set default permissions.
        """
        # remove all permissions
        self.remove_object_permissions()

        # default permissions for anonymous users
        anonymous_group, created = Group.objects.get_or_create(name='anonymous')

        # assign permissions to 'Anyone' if 'DEFAULT_ANONYMOUS_VIEW_PERMISSION' is True
        if settings.DEFAULT_ANONYMOUS_VIEW_PERMISSION:
            assign_perm('access_geocollection', anonymous_group, self)

        # default permissions to the Geocollection group members
        assign_perm('access_geocollection', self.group.group, self)

    def set_permissions(self, perm_spec):
        anonymous_group = Group.objects.get(name='anonymous')
        self.remove_object_permissions()

        if 'users' in perm_spec and "AnonymousUser" in perm_spec['users']:
            if perm_spec['users']['AnonymousUser']:
                for perm in perm_spec['users']['AnonymousUser']:
                    assign_perm(perm, anonymous_group, self)

        if 'users' in perm_spec:
            for username, perms in perm_spec['users'].items():
                if get_user_model().objects.filter(username=username).exists():
                    user = get_user_model().objects.get(username=username)
                    logger.error(f" assign_perm '{user}' -> {perms}")
                    for perm in perms:
                        assign_perm(perm, user, self)

        if 'groups' in perm_spec:
            for group, perms in perm_spec['groups'].items():
                group = Group.objects.get(name=group)
                for perm in perms:
                    assign_perm(perm, group, self)

    def get_all_level_info(self):
        resource = self.get_self_resource()
        users = self.get_users_with_perms()
        groups = get_groups_with_perms(
            resource,
            attach_perms=True)
        if groups:
            for group in groups:
                try:
                    group_profile = GroupProfile.objects.get(slug=group.name)
                    managers = group_profile.get_managers()
                    if managers:
                        for manager in managers:
                            if manager not in users and not manager.is_superuser and manager != resource.owner:
                                assign_perm('access_geocollection', manager, resource)
                                users[manager] = ['access_geocollection', ]
                except GroupProfile.DoesNotExist:
                    tb = traceback.format_exc()
                    logger.debug(tb)
        if resource.group:
            try:
                group_profile = resource.group
                managers = group_profile.get_managers()
                if managers:
                    for manager in managers:
                        if manager not in users and not manager.is_superuser and manager != resource.owner:
                            assign_perm('access_geocollection', manager, resource)
                            users[manager] = ['access_geocollection', ]
            except GroupProfile.DoesNotExist:
                tb = traceback.format_exc()
                logger.debug(tb)

        info = {
            'users': users,
            'groups': groups
        }

        return info

    def get_self_resource(self):
        try:
            if hasattr(self, "resourcebase_ptr_id"):
                return self.resourcebase_ptr
        except ObjectDoesNotExist:
            pass
        return self

    def __str__(self):
        return self.name

    class Meta:
        permissions = (
            ('access_geocollection', 'Can view geocollection'),
        )