from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response

from django.contrib import auth
from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_protect
from django.shortcuts import get_object_or_404

from misago.conf import settings
from misago.core.mail import mail_user
from misago.users.serializers import (
    AnonymousUserSerializer, AuthenticatedUserSerializer, ChangeForgottenPasswordSerializer,
    LoginSerializer, ResendActivationSerializer, SendPasswordFormSerializer)
from misago.users.tokens import (
    make_activation_token, make_password_change_token)

from .rest_permissions import UnbannedAnonOnly, UnbannedOnly


UserModel = auth.get_user_model()


def gateway(request):
    if request.method == 'POST':
        return login(request)
    else:
        return session_user(request)


@api_view(['POST'])
@permission_classes((UnbannedAnonOnly, ))
@csrf_protect
def login(request):
    """
    POST /auth/ with CSRF, username and password
    will attempt to authenticate new user
    """
    serializer = LoginSerializer(request, data=request.data)
    serializer.is_valid(raise_exception=True)

    user = serializer.validated_data['user']
    auth.login(request, user)

    return Response(
        AuthenticatedUserSerializer(user).data,
    )


@api_view()
def session_user(request):
    """GET /auth/ will return current auth user, either User or AnonymousUser"""
    if request.user.is_authenticated:
        UserSerializer = AuthenticatedUserSerializer
    else:
        UserSerializer = AnonymousUserSerializer

    return Response(UserSerializer(request.user).data)


@api_view(['GET'])
def get_requirements(request):
    """GET /auth/requirements/ will return password and username requirements"""
    requirements = {
        'username': {
            'min_length': settings.username_length_min,
            'max_length': settings.username_length_max,
        },
        'password': [],
    }

    for validator in settings.AUTH_PASSWORD_VALIDATORS:
        validator_dict = {'name': validator['NAME'].split('.')[-1]}
        validator_dict.update(validator.get('OPTIONS', {}))
        requirements['password'].append(validator_dict)

    return Response(requirements)


@api_view(['POST'])
@permission_classes((UnbannedAnonOnly, ))
@csrf_protect
def send_activation(request):
    """
    POST /auth/send-activation/ with CSRF token and email
    will mail account activation link to requester
    """
    serializer = ResendActivationSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    serializer.raise_if_banned()

    user = serializer.validated_data['user']

    mail_subject = _("Activate %(user)s account on %(forum_name)s forums") % {
        'user': user.username,
        'forum_name': settings.forum_name,
    }

    mail_user(
        request,
        user,
        mail_subject,
        'misago/emails/activation/by_user',
        {
            'activation_token': make_activation_token(user),
        },
    )

    return Response({
        'username': user.username,
        'email': user.email,
    })


@api_view(['POST'])
@permission_classes((UnbannedOnly, ))
@csrf_protect
def send_password_form(request):
    """
    POST /auth/send-password-form/ with CSRF token and email
    will mail change password form link to requester
    """
    serializer = SendPasswordFormSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    serializer.raise_if_banned()

    user = serializer.validated_data['user']

    mail_subject = _("Change %(user)s password on %(forum_name)s forums") % {
        'user': user.username,
        'forum_name': settings.forum_name,
    }

    confirmation_token = make_password_change_token(user)

    mail_user(
        request,
        user,
        mail_subject,
        'misago/emails/change_password_form_link',
        {
            'confirmation_token': confirmation_token,
        },
    )

    return Response({
        'username': user.username,
        'email': user.email,
    })


class PasswordChangeFailed(Exception):
    pass


@api_view(['POST'])
@permission_classes((UnbannedOnly, ))
@csrf_protect
def change_forgotten_password(request, pk):
    """
    POST /auth/change-password/user/ with CSRF and new password
    will change forgotten password
    """
    user = get_object_or_404(UserModel, pk=pk, is_active=True)
    serializer = ChangeForgottenPasswordSerializer(user, data=request.data)
    serializer.is_valid(raise_exception=True)

    serializer.save()

    return Response({'username': user.username})