# coding: utf-8


import json
import logging

from django.conf import settings
from django.db.models import Q
from tastypie import http
from tastypie.bundle import Bundle

from idm.api.exceptions import BadRequest
from idm.api.frontend import apifields
from idm.api.frontend.base import FrontendApiResource
from idm.api.frontend.forms import MembershipForm, PassportLoginAssignmentForm
from idm.core.constants.groupmembership import GROUPMEMBERSHIP_STATE
from idm.core.models import UserPassportLogin, Role
from idm.core.tasks.passport import RegisterPassportLogin
from idm.sync import passport
from idm.users.constants.group import GROUP_TYPES
from idm.users.models import GroupMembership
from idm.utils.passport_login import login_is_valid


log = logging.getLogger(__name__)


class MembershipResource(FrontendApiResource):
    """
    Состав группы
    """
    user = apifields.UserForeignKey()
    group = apifields.GroupForeignKey()
    passport_login = apifields.UserPassportLoginForeignKey()

    class Meta(FrontendApiResource.Meta):
        abstract = False
        object_class = GroupMembership
        resource_name = 'membership'
        list_allowed_methods = ['get', 'patch']
        limit = 100

    def get_object_list(self, request, **kwargs):
        form = MembershipForm(request.GET)
        if not form.is_valid():
            raise BadRequest(form.errors)
        query = form.cleaned_data
        # direction=up -> страница пользователя
        if query['direction'] == 'up':
            order_by = ('group__type', 'group__level', 'group__name')
        else:
            order_by = ('user__username', 'group__external_id')
        qs = GroupMembership.objects.filter(
            group__type__in=GROUP_TYPES.USER_GROUPS
        ).select_related('user', 'user__department_group', 'group', 'passport_login').order_by(*order_by)
        return qs

    def patch_detail(self, request, **kwargs):
        membership_pk = int(kwargs['pk'])
        membership = GroupMembership.objects.select_related('user', 'user__department_group', 'passport_login').filter(pk=membership_pk).first()
        if not membership:
            return self.create_response(request, 'Указанное членство не существует', response_class=http.HttpNotFound)

        if not membership.user_is_able_to_assign_passport_login(request.user):
            return self.create_response(
                request,
                'Вы не можете привязать логин. Недостаточно прав',
                response_class=http.HttpForbidden,
            )

        try:
            data = json.loads(request.body)
        except ValueError:
            return self.create_response(
                request,
                'Запрос должен быть передан в формате json',
                response_class=http.HttpBadRequest
            )
        form = PassportLoginAssignmentForm(data, empty_permitted=True, use_required_attribute=False)
        if not form.is_valid():
            raise BadRequest(form.errors)

        passport_login = form.cleaned_data['passport_login']

        if passport_login is None:
            # Отвязываем логин
            if membership.passport_login:
                membership.passport_login = None
                membership.notified_about_passport_login = False
                membership.save(update_fields=['passport_login', 'notified_about_passport_login'])
                membership.deprive_roles_with_old_logins()
                log.info('Passport login <%s> successfully removed from <%s> membership', passport_login,
                         membership.pk)
                return self.create_response(request, '', response_class=http.HttpAccepted)
            else:
                return self.create_response(request, '', response_class=http.HttpNotModified)

        if len(passport_login) > settings.MAX_PASSPORT_LOGIN_LENGTH:
            return self.create_response(
                request,
                'Логин не может быть длиннее {} смволов'.format(settings.MAX_PASSPORT_LOGIN_LENGTH),
                response_class=http.HttpBadRequest,
            )
        if not login_is_valid(passport_login):
            return self.create_response(
                request,
                'Логин может содержать только латиницу, цифры, точки и дефисы (не более одного символа подряд),'
                ' должен начинаться с буквы, не может оканчиваться точкой или дефисом',
                response_class=http.HttpBadRequest,
            )

        existing_passport_login = UserPassportLogin.objects.filter(login=passport_login).first()

        if existing_passport_login:
            # Если логин в базе существует, привязываем существующий
            if membership.user_id != existing_passport_login.user_id:
                return self.create_response(
                    request,
                    'Попытка привязать логин, который не связан с пользователем '
                    'указанного членства. Выберите другой логин',
                    response_class=http.HttpBadRequest,
                )
            if membership.passport_login == existing_passport_login:
                return self.create_response(request, '', response_class=http.HttpNotModified)
            membership.passport_login = existing_passport_login
            membership.save(update_fields=['passport_login'])

            membership.poke_awaiting_personal_roles()
            membership.deprive_roles_with_old_logins()

            log.info('Passport login <%s> successfully assigned with <%s> membership', passport_login, membership.pk)
            return self.create_response(request, '', response_class=http.HttpAccepted)
        else:
            # Ищем существующий логин в паспорте
            if passport.exists(passport_login):
                log.warning('Passport login <%s> registered but not found in UserPassportLogin', passport_login)
                return self.create_response(
                    request,
                    'Попытка использовать логин, неизвестный в IDM. Выберите другой логин',
                    response_class=http.HttpBadRequest,
                )
            # Логина не существует, регистрируем
            RegisterPassportLogin.apply_async(kwargs={
                'passport_login': passport_login,
                'membership_id': membership_pk,
            })

            return self.create_response(request, '', response_class=http.HttpAccepted)

    def build_filters(self, request, filters=None):
        form = MembershipForm(filters)
        if not form.is_valid():
            raise BadRequest(form.errors)

        query = form.cleaned_data
        qset_filters = Q()

        if query['group']:
            if query['mode'] and query['mode'] == 'direct':
                qset_filters &= Q(group_id__in=[g.id for g in query['group']])
            else:
                qset_filters &= Q(group__groupclosure_parents__parent_id__in=[g.id for g in query['group']])

        if query['direction'] == 'down' or (query['mode'] and query['mode'] == 'direct'):
            qset_filters &= Q(is_direct=True)

        if query['user']:
            qset_filters &= Q(user__in=query['user'])

        if query['is_active'] is not None:
            filter_states = (
                GROUPMEMBERSHIP_STATE.ACTIVE_STATES if query['is_active']
                else GROUPMEMBERSHIP_STATE.INACTIVE_STATES
            )
            qset_filters &= Q(state__in=filter_states)

        if query['state']:
            qset_filters &= Q(state=query['state'])

        if query['group__type']:
            qset_filters &= Q(group__type__in=query['group__type'])

        return qset_filters

    def apply_filters(self, request, applicable_filters, **kwargs):
        return self.get_object_list(request).filter(applicable_filters)

    def dehydrate(self, bundle: Bundle) -> Bundle:
        bundle = super(MembershipResource, self).dehydrate(bundle)

        bundle.request.user.fetch_department_group()
        bundle.data['requester_can_assign_passport_login'] = bundle.obj.user_is_able_to_assign_passport_login(
            bundle.request.user
        )
        return bundle
