from functools import wraps

from django.contrib.auth.models import User
from django.core.urlresolvers import reverse_lazy
from django.http import JsonResponse
from django.views.generic.edit import UpdateView as BaseUpdateView, CreateView as BaseCreateView, DeleteView as BaseDeleteView
from django.contrib import messages
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.views.generic import TemplateView

from onadata.apps.fieldsight.models import Organization, Project, Site, Region
from onadata.apps.fsforms.models import FieldSightXF, FInstance 
from onadata.apps.users.models import UserProfile
from .helpers import json_from_object
from onadata.apps.userrole.models import UserRole
from django.shortcuts import get_object_or_404
from rest_framework.permissions import BasePermission
from django.db.models import Q
from onadata.apps.logger.models import XForm



# ConditionalFormMixin =  Returns whether the user has full acess or readonly access through "is_readonly" attribute which is either True or False for a specific form. The url parameter should have "fsxf_id" which is form id.
# SPFMixin = Site, Project mixin needs two parameters in url "is_project" and "pk" which can be site_id or project_id. It checks permission for resppective pk depending upon "is_project" attribute from url.
# Readonly site/project levelMixin = It checks if user has access to just view certain pages/view. 

# Important , in near future roles should be cached or some similar alternatives should be added.


class LoginRequiredMixin(object):
    @classmethod
    def as_view(cls, **kwargs):
        view = super(LoginRequiredMixin, cls).as_view(**kwargs)
        return login_required(view)


class OrganizationRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(OrganizationRoleMixin, self).dispatch(request, *args, **kwargs)
        organization_id = self.kwargs.get('pk')
        user_id = request.user.id
        user_role = request.roles.filter(organization_id = organization_id, group_id=1)
        if user_role:
            return super(OrganizationRoleMixin, self).dispatch(request, *args, **kwargs)
        raise PermissionDenied()


class SuperUserRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(SuperUserRoleMixin, self).dispatch(request, *args, **kwargs)    
        raise PermissionDenied()


class ProjectRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(ProjectRoleMixin, self).dispatch(request, *args, **kwargs)
        
        project_id = self.kwargs.get('pk')
        user_id = request.user.id
        user_role = request.roles.filter(user_id = user_id, project_id = project_id, group_id=2)
        
        if user_role:
            return super(ProjectRoleMixin, self).dispatch(request, *args, **kwargs)
        organization_id = Project.objects.get(pk=project_id).organization.id
        user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group_id=1)
        
        if user_role_asorgadmin:
            return super(ProjectRoleMixin, self).dispatch(request, *args, **kwargs)

        raise PermissionDenied()
#use when project role and doner role is required mostly it is like readonly because doner is only allowed to read only
class ReadonlyProjectLevelRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(ReadonlyProjectLevelRoleMixin, self).dispatch(request, is_donor_only=False, *args, **kwargs)
        
        project_id = self.kwargs.get('pk')
        user_id = request.user.id
        user_role = request.roles.filter(project_id = project_id, group_id=2)
        
        if user_role:
            return super(ReadonlyProjectLevelRoleMixin, self).dispatch(request, is_donor_only=False, *args, **kwargs)

        organization_id = Project.objects.get(pk=project_id).organization.id
        user_role_asorgadmin = request.roles.filter(organization_id = organization_id, group_id=1)
        
        if user_role_asorgadmin:
            return super(ReadonlyProjectLevelRoleMixin, self).dispatch(request, is_donor_only=False, *args, **kwargs)

        user_role_asdonor = request.roles.filter(project_id = project_id, group_id=7)
        if user_role_asdonor:
            return super(ReadonlyProjectLevelRoleMixin, self).dispatch(request, is_donor_only=True, *args, **kwargs)

        raise PermissionDenied()

class ReadonlySiteLevelRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):

        if request.group.name == "Super Admin":
            return super(ReadonlySiteLevelRoleMixin, self).dispatch(request, is_donor_only=False, *args, **kwargs)
        
        site_id = self.kwargs.get('pk')
        user_id = request.user.id
        user_role = request.roles.filter(user_id = user_id, site_id = site_id, group_id__in=[3,4])
        
        if user_role:
            return super(ReadonlySiteLevelRoleMixin, self).dispatch(request, is_donor_only=False, *args, **kwargs)
        
        project = Site.objects.get(pk=site_id).project
        user_role_aspadmin = request.roles.filter(user_id = user_id, project_id = project.id, group_id=2)
        if user_role_aspadmin:
            return super(ReadonlySiteLevelRoleMixin, self).dispatch(request, is_donor_only=False, *args, **kwargs)

        organization_id = project.organization.id
        user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group_id=1)
        if user_role_asorgadmin:
            return super(ReadonlySiteLevelRoleMixin, self).dispatch(request, is_donor_only=False, *args, **kwargs)

        user_role_asdonor = request.roles.filter(user_id = user_id, project_id = project.id, group_id=7)
        if user_role_asdonor:
            return super(ReadonlySiteLevelRoleMixin, self).dispatch(request, is_donor_only=True, *args, **kwargs)

        raise PermissionDenied()

#use when doner role is required to load site dashboard
class DonorSiteViewRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(DonorSiteViewRoleMixin, self).dispatch(request, *args, **kwargs)
        
        site = get_object_or_404(Site, pk=self.kwargs.get('pk'))
        user_id = request.user.id
        user_role = request.roles.filter(user_id = user_id, project_id = site.project_id, group_id=7)
        
        if user_role:
            return super(DonorSiteViewRoleMixin, self).dispatch(request, *args, **kwargs)
        organization_id = Project.objects.get(pk=site.project_id).organization.id
        user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group_id=1)
        
        if user_role_asorgadmin:
            return super(DonorSiteViewRoleMixin, self).dispatch(request, *args, **kwargs)

        raise PermissionDenied()

# Use when doner role is required to load donor dashboard
class DonorRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(DonorRoleMixin, self).dispatch(request, *args, **kwargs)
        
        project_id = self.kwargs.get('pk')
        user_id = request.user.id
        user_role = request.roles.filter(user_id = user_id, project_id = project_id, group_id=7)
        
        if user_role:
            return super(DonorRoleMixin, self).dispatch(request, *args, **kwargs)
        organization_id = Project.objects.get(pk=project_id).organization.id
        user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group_id=1)
        
        if user_role_asorgadmin:
            return super(DonorRoleMixin, self).dispatch(request, *args, **kwargs)

        raise PermissionDenied()

class ReviewerRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):

        if request.group.name == "Super Admin":
            return super(ReviewerRoleMixin, self).dispatch(request, *args, **kwargs)
        
        site_id = self.kwargs.get('pk')
        user_id = request.user.id
        user_role = request.roles.filter(user_id = user_id, site_id = site_id, group_id=3)
        
        if user_role:
            return super(ReviewerRoleMixin, self).dispatch(request, *args, **kwargs)
        
        project = Site.objects.get(pk=site_id).project
        user_role_aspadmin = request.roles.filter(user_id = user_id, project_id = project.id, group_id=2)
        if user_role_aspadmin:
            return super(ReviewerRoleMixin, self).dispatch(request, *args, **kwargs)

        organization_id = project.organization.id
        user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group_id=1)
        if user_role_asorgadmin:
            return super(ReviewerRoleMixin, self).dispatch(request, *args, **kwargs)

        # user_role_asSs = request.roles.filter(user_id = user_id, site_id = site_id, group__name="Site Supervisor")
        # if user_roleas_Ss:


        raise PermissionDenied()

class SiteRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):

        if request.group.name == "Super Admin":
            return super(SiteRoleMixin, self).dispatch(request, is_supervisor_only=False, *args, **kwargs)
        
        site_id = self.kwargs.get('pk')
        user_id = request.user.id
        user_role = request.roles.filter(site_id = site_id, group_id=3)
        
        if user_role:
            return super(SiteRoleMixin, self).dispatch(request, is_supervisor_only=False, *args, **kwargs)
        
        project = Site.objects.get(pk=site_id).project
        user_role_aspadmin = request.roles.filter(project_id = project.id, group_id=2)
        if user_role_aspadmin:
            return super(SiteRoleMixin, self).dispatch(request, is_supervisor_only=False, *args, **kwargs)

        organization_id = project.organization.id
        user_role_asorgadmin = request.roles.filter(organization_id = organization_id, group_id=1)
        if user_role_asorgadmin:
            return super(SiteRoleMixin, self).dispatch(request, is_supervisor_only=False, *args, **kwargs)

        user_role = request.roles.filter(site_id = site_id, group_id=4)
        if user_role:
            return super(SiteRoleMixin, self).dispatch(request, is_supervisor_only=True, *args, **kwargs)

        raise PermissionDenied()

class SiteDeleteRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):

        if request.group.name == "Super Admin":
            return super(SiteDeleteRoleMixin, self).dispatch(request, *args, **kwargs)
        
        site_id = self.kwargs.get('pk')
        user_id = request.user.id
        
        project = Site.objects.get(pk=site_id).project
        user_role_aspadmin = request.roles.filter(user_id = user_id, project_id = project.id, group_id=2)
        
        if user_role_aspadmin:
            return super(SiteDeleteRoleMixin, self).dispatch(request, *args, **kwargs)

        organization_id = project.organization.id
        user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group_id=1)
        
        if user_role_asorgadmin:
            return super(SiteDeleteRoleMixin, self).dispatch(request, *args, **kwargs)

        raise PermissionDenied()



class ProjectRoleMixinDeleteView(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):

        if request.group.name == "Super Admin":
            return super(ProjectRoleMixinDeleteView, self).dispatch(request, *args, **kwargs)
        
        project_id = self.kwargs.get('pk')
        user_id = request.user.id

        organization_id = Project.objects.get(pk=project_id).organization.id
        user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group_id=1)
        
        if user_role_asorgadmin:
            return super(ProjectRoleMixinDeleteView, self).dispatch(request, *args, **kwargs)

        raise PermissionDenied()

class ReviewerRoleMixinDeleteView(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):

        if request.group.name == "Super Admin":
            return super(ReviewerRoleMixinDeleteView, self).dispatch(request, *args, **kwargs)
        
        site_id = self.kwargs.get('pk')
        user_id = request.user.id
        
        user_role = request.roles.filter(user_id = user_id, site_id = site_id, group_id=3)
        
        if user_role:
            return super(SiteSupervisorRoleMixin, self).dispatch(request, *args, **kwargs)
        project = Site.objects.get(pk=site_id).project
        user_role_aspadmin = request.roles.filter(user_id = user_id, project_id = project.id, group_id=2)
        if user_role_aspadmin:
            return super(ReviewerRoleMixinDeleteView, self).dispatch(request, *args, **kwargs)

        organization_id = project.organization.id
        user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group_id=1)
        if user_role_asorgadmin:
            return super(ReviewerRoleMixinDeleteView, self).dispatch(request, *args, **kwargs)

        raise PermissionDenied()


class ProjectRoleView(LoginRequiredMixin):
    def form_valid(self, form):
        if self.request.kwargs.get('pk'):
            form.instance.project = self.request.kwargs.get('pk')
        return super(ProjectRoleView, self).form_valid(form)

    def get_queryset(self):
        return super(ProjectRoleView, self).get_queryset()



class SPFmixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.group.name == "Super Admin":
                return super(SPFmixin, self).dispatch(request, *args, **kwargs)

        user_id = request.user.id

        if self.kwargs.get('is_project') == '0':
            site_id = self.kwargs.get('pk')
            user_role = request.roles.filter(user_id = user_id, site_id = site_id, group_id=3)
            if user_role:
                return super(SPFmixin, self).dispatch(request, *args, **kwargs)
            project_id=Site.objects.get(pk=site_id).project.id
        
        else:
            project_id = self.kwargs.get('pk')

        user_role = request.roles.filter(user_id = user_id, project_id = project_id, group_id=2)
        if user_role:
            return super(SPFmixin, self).dispatch(request, *args, **kwargs)

        organization_id = Project.objects.get(pk=project_id).organization.id
        user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group_id=1)
        if user_role_asorgadmin:
            return super(SPFmixin, self).dispatch(request, *args, **kwargs)

        raise PermissionDenied()    


class FormMixin(LoginRequiredMixin):
    def dispatch(self, request, fsxf_id, *args, **kwargs):
        if request.group.name == "Super Admin":
                return super(FormMixin, self).dispatch(request, fsxf_id, *args, **kwargs)

        user_id = request.user.id
        form = get_object_or_404(FieldSightXF, pk=fsxf_id)

        if form.site is not None:
            site_id = form.site.id
            user_role = request.roles.filter(site_id = site_id, group_id=3)
            if user_role:
                return super(FormMixin, self).dispatch(request, fsxf_id, *args, **kwargs)
            project_id=Site.objects.get(pk=site_id).project.id
        
        else:
            project_id = form.project.id

        user_role = request.roles.filter(project_id = project_id, group_id=2)
        if user_role:
            return super(FormMixin, self).dispatch(request, fsxf_id, *args, **kwargs)

        organization_id = Project.objects.get(pk=project_id).organization.id
        user_role_asorgadmin = request.roles.filter(organization_id = organization_id, group_id=1)
        if user_role_asorgadmin:
            return super(FormMixin, self).dispatch(request, fsxf_id, *args, **kwargs)

        raise PermissionDenied()   

class ReadonlyFormMixin(LoginRequiredMixin):
    def dispatch(self, request, fsxf_id, *args, **kwargs):
        if request.group.name == "Super Admin":
                return super(ReadonlyFormMixin, self).dispatch(request, fsxf_id, *args, **kwargs)

        form = get_object_or_404(FieldSightXF, pk=fsxf_id)

        if form.site is not None:
            site_id = form.site.id
            user_role = request.roles.filter(site_id = site_id, group_id__in=[3,4])
            if user_role:
                return super(ReadonlyFormMixin, self).dispatch(request, fsxf_id, *args, **kwargs)
            project_id=Site.objects.get(pk=site_id).project.id
        
        else:
            project_id = form.project.id

        user_role = request.roles.filter(project_id = project_id, group_id__in=[2,7])
        if user_role:
            return super(ReadonlyFormMixin, self).dispatch(request, fsxf_id, *args, **kwargs)

        #reviewer
        if request.roles.filter(site__project__id=project_id, group__name="Reviewer").exists():
            return super(ReadonlyFormMixin, self).dispatch(request, fsxf_id, *args, **kwargs)

        organization_id = Project.objects.get(pk=project_id).organization.id
        user_role_asorgadmin = request.roles.filter(organization_id = organization_id, group_id = 1)
        if user_role_asorgadmin:
            return super(ReadonlyFormMixin, self).dispatch(request, fsxf_id, *args, **kwargs)

        raise PermissionDenied()   


#whwerver only readonly permissions are required for urls which includes fsxf_id, this mixin can be used. The function
#returns "is_readonly" attribute either True or False so make sure to use it to determine readonly features in view or template.
class ConditionalFormMixin(LoginRequiredMixin):
    def dispatch(self, request, fsxf_id, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(ConditionalFormMixin, self).dispatch(request, fsxf_id, is_read_only= False, *args, **kwargs)

        user_id = request.user.id
        form = get_object_or_404(FieldSightXF, pk=fsxf_id)

        if form.site is not None:
            site_id = form.site.id
            user_role = request.roles.filter(site_id = site_id, group_id=3)
            if user_role:
                return super(ConditionalFormMixin, self).dispatch(request, fsxf_id, is_read_only= False, *args, **kwargs)
            project_id=form.site.project_id
        
        else:
            project_id = form.project.id

        user_role = request.roles.filter(project_id = project_id, group_id=2)
        if user_role:
            return super(ConditionalFormMixin, self).dispatch(request, fsxf_id, is_read_only= False, *args, **kwargs)

        if request.roles.filter(project_id=project_id, group__name="Reviewer").exists():
            return super(ConditionalFormMixin, self).dispatch(request, fsxf_id, is_read_only=False, *args, **kwargs)

        if request.roles.filter(project_id=project_id, group__name="Site Supervisor").exists():
            return super(ConditionalFormMixin, self).dispatch(request, fsxf_id, is_read_only=False, *args, **kwargs)

        organization_id = Project.objects.get(pk=project_id).organization.id
        user_role_asorgadmin = request.roles.filter(organization_id = organization_id, group_id=1)
        if user_role_asorgadmin:
            return super(ConditionalFormMixin, self).dispatch(request, fsxf_id, is_read_only= False, *args, **kwargs)
        if form.site is not None:
            user_role = request.roles.filter(Q(site_id = form.site_id, group_id=4) | Q(project_id = form.site.project_id, group_id=7))
        else:
            user_role = request.roles.filter(project_id = form.project_id, group_id=7)
        if user_role:
            return super(ConditionalFormMixin, self).dispatch(request, fsxf_id, is_read_only= True, *args, **kwargs)
        # return super(ConditionalFormMixin, self).dispatch(request, fsxf_id, is_read_only=True, *args, **kwargs)
        raise PermissionDenied()   






class MyFormMixin(LoginRequiredMixin):
    def dispatch(self, request, xf_id, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(MyFormMixin, self).dispatch(request, xf_id, *args, **kwargs)

        user_id = request.user.id
        xform = get_object_or_404(XForm, pk=xf_id)

        if xform.user_id == user_id:
            return super(MyFormMixin, self).dispatch(request, xf_id, *args, **kwargs)

        raise PermissionDenied()

class EndRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(EndRoleMixin, self).dispatch(request, *args, **kwargs)
        role_to_end = UserRole.objects.get(pk=self.kwargs.get('pk'))
        if role_to_end.group_id == 2:
            user_role = request.roles.filter(organization_id = role_to_end.organization_id, group_id=1)
            if user_role:
                return super(EndRoleMixin, self).dispatch(request, *args, **kwargs)
        
        elif role_to_end.group_id == 3 or role_to_end.group_id == 4:
            user_role = request.roles.filter(Q(project_id = role_to_end.project_id, group_id=2) | Q(organization_id = role_to_end.organization_id, group_id=1))
            if user_role:
                return super(EndRoleMixin, self).dispatch(request, *args, **kwargs)     
        raise PermissionDenied() 


class FInstanceRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(FInstanceRoleMixin, self).dispatch(request, *args, **kwargs)
        finstance = get_object_or_404(FInstance, instance_id=self.kwargs.get('instance_pk'))
        if finstance.site or finstance.project:
            user_id = request.user.id
            project = finstance.project
            if finstance.site:
                site_id = finstance.site.id
                
                user_role = request.roles.filter(site_id = site_id, group_id=3)
                
                if user_role:
                    return super(FInstanceRoleMixin, self).dispatch(request, *args, **kwargs)
                
                user_role_aspadmin = request.roles.filter(project_id = finstance.site.project_id, group_id=2)
                if user_role_aspadmin:
                    return super(FInstanceRoleMixin, self).dispatch(request, *args, **kwargs)

                organization_id = finstance.site.project.organization_id
                user_role_asorgadmin = request.roles.filter(organization_id = organization_id, group_id=1)
                
                if user_role_asorgadmin:
                    return super(FInstanceRoleMixin, self).dispatch(request, *args, **kwargs)
                project = finstance.site.project
            
            if project:
                user_role_aspadmin = request.roles.filter(project_id = project.id, group_id=2)
                if user_role_aspadmin:
                    return super(FInstanceRoleMixin, self).dispatch(request, *args, **kwargs)
                organization_id = project.organization.id
                user_role_asorgadmin = request.roles.filter(organization_id = organization_id, group_id=1)
                if user_role_asorgadmin:
                    return super(FInstanceRoleMixin, self).dispatch(request, *args, **kwargs)
            
        raise PermissionDenied() 


class FullMapViewMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):
        if request.group.name == "Super Admin":
            return super(FullMapViewMixin, self).dispatch(request, *args, **kwargs)
            
        user_id = request.user.id
        user_role = request.roles.filter(user_id = user_id, group_id__in=[7, 1, 2])
        
        if user_role:
            return super(FullMapViewMixin, self).dispatch(request, *args, **kwargs)
        
        raise PermissionDenied()


class RegionRoleMixin(LoginRequiredMixin):
    def dispatch(self, request, *args, **kwargs):

        if request.group.name == "Super Admin":
            return super(RegionRoleMixin, self).dispatch(request, *args, **kwargs)
        
        region_id = self.kwargs.get('pk')
        user_id = request.user.id
        
        project = Region.objects.get(pk=region_id).project
        user_role_aspadmin = request.roles.filter(user_id = user_id, project_id = project.id, group_id=2)
        
        if user_role_aspadmin:
            return super(RegionRoleMixin, self).dispatch(request, *args, **kwargs)

        organization_id = project.organization.id
        user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group_id=1)
        
        if user_role_asorgadmin:
            return super(RegionRoleMixin, self).dispatch(request, *args, **kwargs)

        raise PermissionDenied()
# for api mixins/permissions

# class ProjectPermission(BasePermission):
#     def has_permission(self, request, view):
#         if request.group.name == "Super Admin":
#             return super(ProjectRoleMixin, self).dispatch(request, *args, **kwargs)
        
#         project_id = self.kwargs.get('pk')
#         user_id = request.user.id
#         user_role = request.roles.filter(user_id = user_id, project_id = project_id, group__name="Project Manager")
        
#         if user_role:
#             return True
        
#         organization_id = Project.objects.get(pk=project_id).organization.id
#         user_role_asorgadmin = request.roles.filter(user_id = user_id, organization_id = organization_id, group__name="Organization Admin")
        
#         if user_role_asorgadmin:
#             return True

#         return False