# coding: utf-8
from django.conf.urls import url
from django.db.models import Q
from tastypie.utils.urls import trailing_slash
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext_lazy as _
from django.conf import settings

from idm.api.exceptions import BadRequest, Forbidden
from idm.api.frontend import apifields
from idm.api.frontend.apifields import AllButRelated
from idm.api.frontend.base import FrontendApiResource
from idm.api.frontend.forms import (
    UserForm,
    PassportLoginRequestForm,
    UserUpdateForm,
)
from idm.users.constants.user import USER_TYPES
from idm.users.models import User
from idm.core.models import UserPassportLogin, Action

USERNAME_REGEXP = r'[\w][-_.\w]+'


class UserResource(FrontendApiResource):
    """
    Ресурс пользователя
    """

    department_group = apifields.GroupForeignKey(attribute='department_group', use_in=AllButRelated)

    class Meta(FrontendApiResource.Meta):
        abstract = False
        object_class = User
        queryset = (
            User.objects.select_related('department_group').
            order_by('username', 'first_name', 'last_name')
        )
        resource_name = 'users'
        allowed_methods = ['get']
        detail_allowed_methods = ['get', 'patch']
        detail_uri_name = 'username'
        fields = [
            'username', 'email', 'sex', 'is_active', 'position', 'date_joined',
            'fired_at', 'department_group', 'type', 'notify_responsibles',
            'is_robot',
        ]
        limit = 100
        insensitive_keys = {'username'}

    def prepend_urls(self):
        return [
            # позволяем находить пользователей в API по логинам (но исключаем служебный урл /schema)
            url(r'^(?P<resource_name>{resource_name})/(?P<username>(?!schema){username_regexp})/$'.format(
                resource_name=self._meta.resource_name,
                username_regexp=USERNAME_REGEXP
            ), self.wrap_view('dispatch_detail'), name='api_dispatch_detail'),
            # возвращаем список паспортных логинов пользователя
            url(r'^(?P<resource_name>{resource_name})/(?P<username>{username_regexp})/passport-logins{slash}$'.format(
                resource_name=self._meta.resource_name,
                username_regexp=USERNAME_REGEXP,
                slash=trailing_slash()
            ), self.wrap_view('get_passport_logins'), name='api_get_passport_logins'),
        ]

    def get_passport_logins(self, request, username, *args, **kwargs):
        """
        Ручка отдает не только существующий список паспортных логинов пользователя, но
        и предлагает варианты свободных для создания
        """
        try:
            user = User.objects.users().get(username=username)
        except User.DoesNotExist:
            raise BadRequest('Invalid user')
        passport_login_form = PassportLoginRequestForm(user, request.GET)
        if passport_login_form.is_valid():
            system = passport_login_form.cleaned_data['system']
            if system is not None:
                current_user_passport_logins = system.get_user_passport_logins(
                    user,
                    internal_only=system.slug in settings.SYSTEMS_SUPPORT_ONLY_IDM_CREATED_PASSPORT_LOGINS
                )
                current_logins = sorted(current_user_passport_logins, key=len)
                role_node = passport_login_form.cleaned_data['path']
                available_user_passport_logins = system.get_available_user_passport_logins(user, role_node)
            else:
                current_logins = set(user.passport_logins.values_list('login', flat=True).order_by('login'))
                base_login = 'yndx-{username}'.format(username=user.username)
                if not UserPassportLogin.objects.filter(login=base_login).exists():
                    available_user_passport_logins = [base_login]
                else:
                    available_user_passport_logins = UserPassportLogin.objects.generate_sequentially(base_login,
                                                                                                     current_logins)
        else:
            raise BadRequest(passport_login_form.errors)

        return self.create_response(request, {
            'current': list(current_logins),
            'available': list(available_user_passport_logins)
        })

    def get_object_list_for_detail(self, request, **kwargs):
        return self.Meta.queryset._clone()

    def get_object_list(self, request, **kwargs):
        return self.Meta.queryset.users()._clone()

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

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

        query = form.cleaned_data
        kwargs = {}

        if query['is_active'] is not None:
            kwargs['is_active'] = query['is_active']

        if query['updated__since'] is not None:
            kwargs['updated__gte'] = query['updated__since']

        if query['updated__until'] is not None:
            kwargs['updated__lte'] = query['updated__until']

        return Q(**kwargs)

    def dehydrate(self, bundle):
        bundle = super(UserResource, self).dehydrate(bundle)

        # Полное имя
        bundle.data['full_name'] = bundle.obj.get_full_name()
        bundle.data['username'] = str(bundle.obj.username)

        return bundle

    def dehydrate_for_detail(self, bundle):
        # Логин и полное имя начальника
        head = bundle.obj.head

        bundle.data['head'] = bundle.data['chief'] = {
            'full_name': head.get_full_name(),
            'username': head.username,
        } if head else None

        # Департаменты в порядке сверху вниз
        departments = []
        for group in bundle.obj.departments_chain:
            departments.append({
                'id': group.external_id,
                'name': group.get_name(),
                'url': group.slug,
            })
        departments.reverse()
        bundle.data['departments'] = departments

        # Синхронизирован ли пользователь со стаффом (считаем по наличию у него департамента)
        bundle.data['is_synced_with_staff'] = (
            bool(bundle.obj.department_group_id)
            or bundle.obj.type == USER_TYPES.TVM_APP
        )

        return bundle

    def patch_detail(self, request, **kwargs):
        """Редактирование пользователя"""
        user = get_object_or_404(User, username=kwargs['username'])

        data = self.deserialize(request, request.body)
        requester = self.get_requester(request, data)

        form = UserUpdateForm(data, initial=user.__dict__)

        if not form.is_valid():
            raise BadRequest(form.errors)
        cleaned_data = form.cleaned_data
        changed_data = form.changed_data

        if 'notify_responsibles' in changed_data:
            if not user.is_robot:
                raise Forbidden(_('Разрешено редактировать только роботов'))

            if not (
                requester.impersonated.is_superuser or
                requester.impersonated.robots.filter(id=user.id).exists()
            ):
                raise Forbidden(_('Только ответственные за робота могут его редактировать'))
        elif 'is_frozen' in changed_data:
            if not (
                requester.impersonated.is_superuser or
                requester.impersonated.username in (settings.ABC_TVM_ID, settings.ABC_ROBOT_LOGIN)
            ):
                raise Forbidden(_('Нет прав на изменение is_frozen'))

        update_fields = []
        for field in changed_data:
            update_fields.append(field)
            setattr(user, field, cleaned_data[field])
        if update_fields:
            user.save(update_fields=update_fields)

            Action.objects.create(
                robot=user, requester=requester.impersonated,
                data=data,
            )
        return self.get_detail(request, **kwargs)
