# -*- coding: utf-8 -*-
import logging

from django.conf import settings
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.db.models import F, Value, CharField
from enum import Enum
from guardian.models import UserObjectPermission, GroupObjectPermission
from rest_framework import viewsets, parsers, serializers
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.serializers import ValidationError
from urllib import parse as urlparse

from events.accounts.models import User
from events.common_app.utils import lazy_re_compile
from events.idm import utils
from events.rest_framework_contrib.mixins import ExternalGenericApiViewV1Mixin
from events.rest_framework_contrib.permissions import ANY, IsSuperUser, IdmClientOnly
from events.surveyme.models import Survey, SurveyGroup

logger = logging.getLogger(__name__)


class RoleGroupField(serializers.IntegerField):
    def to_representation(self, value):
        return utils.get_group_id(value)


re_passport_login = lazy_re_compile(r'^(yndx-[^@]+)@yandex.ru$')


def get_passport_login(email):
    m = re_passport_login.match(email)
    if m:
        return m.group(1)


class RoleSerializer(serializers.Serializer):
    def to_representation(self, value):
        result = {
            'path': f'/role/{value.role}/',
        }
        if not settings.IS_BUSINESS_SITE and value.user_uid:
            result['uid'] = value.user_uid
        if value.first_name:
            result['login'] = value.first_name
        if value.group_name:
            result['group'] = utils.get_group_id(value.group_name)
        fields = {}
        if value.object_pk:
            fields['object_pk'] = value.object_pk
        if settings.IS_BUSINESS_SITE and value.email:
            passport_login = get_passport_login(value.email)
            if passport_login:
                fields['passport-login'] = passport_login
        if fields:
            result['fields'] = fields
        return result


class MembershipSerializer(serializers.Serializer):
    login = serializers.CharField(source='user__username')
    uid = serializers.CharField(source='user__uid')
    group = RoleGroupField(source='group__name')


class AddRemoveRoleSerializer(serializers.Serializer):
    role = serializers.JSONField()
    uid = serializers.CharField(required=False, default=None)
    group = serializers.CharField(required=False, default=None)
    login = serializers.CharField(required=False, default=None)
    fields = serializers.JSONField(required=False, default={})
    fired = serializers.BooleanField(required=False, default=False)

    def validate(self, data):
        if not ((bool(data.get('uid')) or bool(data.get('login'))) ^ bool(data.get('group'))):
            raise ValidationError('Must have uid/login or group field')
        return data


class AddRemoveMembershipSerializer(serializers.Serializer):
    data = serializers.JSONField(default=[])


class StreamPagination:
    page_size_field = 'page_size'
    page_size = 25
    ordering = 'id'
    condition_field = 'id'

    def paginate_queryset(self, queryset, request, view=None):
        self.request = request
        page_size = int(request.query_params.get(self.page_size_field) or self.page_size)
        condition_value = int(request.query_params.get(self.condition_field) or 0)
        condition = {f'{self.condition_field}__gt': condition_value}
        queryset = queryset.filter(**condition)
        if self.ordering:
            queryset = queryset.order_by(self.ordering)
        result = list(queryset[:page_size])
        self.has_next = len(result) == page_size
        if self.has_next:
            last_row = result[-1]
            if isinstance(last_row, dict):
                self.last_condition_value = last_row.get(self.condition_field)
            else:
                self.last_condition_value = getattr(last_row, self.condition_field, None)
        else:
            self.last_condition_value = None
        return result

    def get_next_link(self):
        if not self.has_next:
            return
        query_params = self.request.query_params.copy()
        query_params[self.condition_field] = self.last_condition_value
        query = urlparse.urlencode(query_params)
        return urlparse.urlunsplit(('', '', self.request.path, query, ''))

    def get_paginated_response(self, data):
        return Response({
            'next': self.get_next_link(),
            'results': data,
        })


class IdmPagination(StreamPagination):
    field_name = None
    page_size = 500

    def get_paginated_response(self, data):
        return Response({
            'code': 0,
            'next-url': self.get_next_link(),
            self.field_name: data,
        })


class MembershipPagination(IdmPagination):
    field_name = 'memberships'


class StatusTypes(str, Enum):
    superuser = '1'
    support = '2'
    user_objects = '3'
    group_objects = '4'

    def get_next(self) -> 'StatusTypes':
        if self == self.superuser:
            return self.support
        elif self == self.support:
            return self.user_objects
        elif self == self.user_objects:
            return self.group_objects

    @classmethod
    def create(cls, value):
        default = cls.superuser
        if not value:
            return default
        if value not in cls.__members__.values():
            return default
        return cls(value)


class RolePagination(IdmPagination):
    field_name = 'roles'

    def get_next_link(self):
        next_link = super().get_next_link()
        if next_link:
            return next_link

        st = StatusTypes.create(self.request.query_params.get('st'))
        next_st = st.get_next()
        if not next_st:
            return
        query_params = self.request.query_params.copy()
        query_params[self.condition_field] = ''
        query_params['st'] = next_st.value
        query = urlparse.urlencode(query_params)
        return urlparse.urlunsplit(('', '', self.request.path, query, ''))


class IdmViewSet(ExternalGenericApiViewV1Mixin, viewsets.GenericViewSet):
    permission_classes = [ANY(IsSuperUser, IdmClientOnly)]
    parser_classes = (parsers.JSONParser, parsers.FormParser)
    detail = None

    @action(methods=['get'], url_path='info', detail=False)
    def info(self, request, *args, **kwargs):
        form_fields = [{
            'slug': 'object_pk',
            'name': {
                'ru': 'Код формы',
                'en': 'Form ID',
            },
            'type': 'charfield',
            'required': True,
            'options': {'blank_allowed': False},
        }]
        group_fields = [{
            'slug': 'object_pk',
            'name': {
                'ru': 'Код группы форм',
                'en': 'Group Form ID',
            },
            'type': 'charfield',
            'required': True,
            'options': {'blank_allowed': False},
        }]
        passport_fields = [{
            'slug': 'passport-login',
            'name': {
                'ru': 'Паспортный логин',
                'en': 'Passport login',
            },
            'type': 'passportlogin',
            'required': True,
        }]
        superuser = settings.ROLE_SUPERUSER, {
            'name': {
                'ru': 'Суперпользователь',
                'en': 'Superuser',
            },
        }
        support = settings.ROLE_SUPPORT, {
            'name': {
                'ru': 'Техподдержка',
                'en': 'Support',
            },
        }
        if settings.IS_BUSINESS_SITE:
            superuser[1]['fields'] = passport_fields
            support[1]['fields'] = passport_fields

        form_manager = settings.ROLE_FORM_MANAGER, {
            'name': {
                'ru': 'Менеджер формы',
                'en': 'Form Manager',
            },
            'fields': form_fields,
        }
        group_manager = settings.ROLE_GROUP_MANAGER, {
            'name': {
                'ru': 'Менеджер группы форм',
                'en': 'Group Form Manager',
            },
            'fields': group_fields,
        }
        form_filedownload = settings.ROLE_FORM_FILEDOWNLOAD, {
            'name': {
                'ru': 'Скачивание файлов для форм',
                'en': 'Form File Download',
            },
            'fields': form_fields,
        }
        group_filedownload = settings.ROLE_GROUP_FILEDOWNLOAD, {
            'name': {
                'ru': 'Скачивание файлов для групп форм',
                'en': 'Group Form File Download',
            },
            'fields': group_fields,
        }
        roles = [superuser, support]
        if not settings.IS_BUSINESS_SITE:
            roles += [form_manager, group_manager, form_filedownload, group_filedownload]

        return Response({
            'code': 0,
            'roles': {
                'slug': 'role',
                'name': 'роль',
                'values': dict(roles),
            },
        })

    @action(methods=['post'], url_path='add-role', detail=False)
    def add_role(self, request, *args, **kwargs):
        logger.info('add-role: %s', request.data)
        serializer = AddRemoveRoleSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        role = serializer.validated_data['role']['role']
        uid = serializer.validated_data['uid']
        group = serializer.validated_data['group']
        login = serializer.validated_data['login']
        fields = serializer.validated_data.get('fields') or {}
        object_pk = fields.get('object_pk')
        passport_login = fields.get('passport-login')

        utils.add_role(request.user, role, uid, group, login, object_pk, passport_login)
        result = {'code': 0}
        if object_pk:
            result['data'] = {'object_pk': object_pk}
        return Response(result)

    @action(methods=['post'], url_path='remove-role', detail=False)
    def remove_role(self, request, *args, **kwargs):
        logger.info('remove-role: %s', request.data)
        serializer = AddRemoveRoleSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        role = serializer.validated_data['role']['role']
        uid = serializer.validated_data['uid']
        group = serializer.validated_data['group']
        login = serializer.validated_data['login']
        fields = serializer.validated_data.get('fields') or {}
        object_pk = fields.get('object_pk')
        passport_login = fields.get('passport-login')
        fired = serializer.validated_data['fired']

        utils.remove_role(request.user, role, uid, group, login, object_pk, passport_login, fired)
        return Response({'code': 0})

    @action(methods=['post'], url_path='add-batch-memberships', detail=False)
    def add_batch_memberships(self, request, *args, **kwargs):
        logger.debug('add-batch-memberships: %s', request.data)
        serializer = AddRemoveMembershipSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        data = serializer.validated_data['data']

        utils.add_batch_memberships(data)
        return Response({'code': 0})

    @action(methods=['post'], url_path='remove-batch-memberships', detail=False)
    def remove_batch_memberships(self, request, *args, **kwargs):
        logger.debug('remove-batch-memberships: %s', request.data)
        serializer = AddRemoveMembershipSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        data = serializer.validated_data['data']

        utils.remove_batch_memberships(data)
        return Response({'code': 0})

    def get_memberships_qs(self):
        UserToGroup = User.groups.through
        return (
            UserToGroup.objects.filter(group__name__startswith='group:')
            .values_list('id', 'user__username', 'user__uid', 'group__name', named=True)
        )

    @action(methods=['get'], url_path='get-memberships', detail=False)
    def get_memberships(self, request, *args, **kwargs):
        paginator = MembershipPagination()
        page = paginator.paginate_queryset(self.get_memberships_qs(), request)
        serializer = MembershipSerializer(page, many=True)
        return paginator.get_paginated_response(serializer.data)

    def get_superuser_roles_qs(self):
        return (
            User.objects.filter(is_superuser=True)
            .annotate(
                role=Value(settings.ROLE_SUPERUSER, CharField()),
                user_uid=F('uid'),
                group_name=Value(None, CharField()),
                object_pk=Value(None, CharField()),
            )
            .values_list('id', 'role', 'user_uid', 'first_name', 'email', 'group_name', 'object_pk', named=True)
        )

    def get_support_roles_qs(self):
        return (
            User.objects.filter(is_staff=True)
            .annotate(
                role=Value(settings.ROLE_SUPPORT, CharField()),
                user_uid=F('uid'),
                group_name=Value(None, CharField()),
                object_pk=Value(None, CharField()),
            )
            .values_list('id', 'role', 'user_uid', 'first_name', 'email', 'group_name', 'object_pk', named=True)
        )

    def get_user_object_roles_qs(self):
        ct = ContentType.objects.get_for_models(Survey, SurveyGroup)
        permissions = list(
            Permission.objects.filter(
                content_type__in=ct.values(),
                codename__in=(
                    settings.ROLE_FORM_MANAGER, settings.ROLE_GROUP_MANAGER,
                    settings.ROLE_FORM_FILEDOWNLOAD, settings.ROLE_GROUP_FILEDOWNLOAD,
                ),
            )
            .order_by()
            .values_list('pk', flat=True)
        )
        return (
            UserObjectPermission.objects.filter(
                content_type__in=ct.values(),
                permission_id__in=permissions,
            )
            .annotate(
                role=F('permission__codename'),
                user_uid=F('user__uid'),
                first_name=Value(None, CharField()),
                email=Value(None, CharField()),
                group_name=Value(None, CharField()),
            )
            .values_list('id', 'role', 'user_uid', 'first_name', 'email', 'group_name', 'object_pk', named=True)
        )

    def get_group_object_roles_qs(self):
        ct = ContentType.objects.get_for_models(Survey, SurveyGroup)
        permissions = list(
            Permission.objects.filter(
                content_type__in=ct.values(),
                codename__in=(
                    settings.ROLE_FORM_MANAGER, settings.ROLE_GROUP_MANAGER,
                    settings.ROLE_FORM_FILEDOWNLOAD, settings.ROLE_GROUP_FILEDOWNLOAD,
                ),
            )
            .order_by()
            .values_list('pk', flat=True)
        )
        return (
            GroupObjectPermission.objects.filter(
                content_type__in=ct.values(),
                permission_id__in=permissions,
                group__name__startswith='group:',
            )
            .annotate(
                role=F('permission__codename'),
                user_uid=Value(None, CharField()),
                first_name=Value(None, CharField()),
                email=Value(None, CharField()),
                group_name=F('group__name'),
            )
            .values_list('id', 'role', 'user_uid', 'first_name', 'email', 'group_name', 'object_pk', named=True)
        )

    def get_roles_qs(self, st: StatusTypes):
        if settings.IS_BUSINESS_SITE:
            return self.get_roles_business_qs(st)
        return self.get_roles_intranet_qs(st)

    def get_roles_intranet_qs(self, st: StatusTypes):
        match st:
            case StatusTypes.superuser:
                qs = self.get_superuser_roles_qs()
            case StatusTypes.support:
                qs = self.get_support_roles_qs()
            case StatusTypes.user_objects:
                qs = self.get_user_object_roles_qs()
            case StatusTypes.group_objects:
                qs = self.get_group_object_roles_qs()
            case _:
                qs = self.get_user_roles_qs()
        return qs

    def get_roles_business_qs(self, st: StatusTypes):
        match st:
            case StatusTypes.superuser:
                qs = self.get_superuser_roles_qs()
            case StatusTypes.support:
                qs = self.get_support_roles_qs()
            case StatusTypes.user_objects:
                qs = User.objects.none()
            case StatusTypes.group_objects:
                qs = User.objects.none()
            case _:
                qs = self.get_user_roles_qs()
        return qs

    @action(methods=['get'], url_path='get-roles', detail=False)
    def get_roles(self, request, *args, **kwargs):
        st = StatusTypes.create(self.request.query_params.get('st'))
        paginator = RolePagination()
        page = paginator.paginate_queryset(self.get_roles_qs(st), request)
        serializer = RoleSerializer(page, many=True)
        return paginator.get_paginated_response(serializer.data)
