import logging

from django.utils.translation import ugettext as _
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.base import TemplateView
from django.template.loader import render_to_string
from django.contrib import messages
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import authenticate, login


from authentic2.utils import continue_to_next_url, record_authentication_event, redirect, redirect_to_login

from . import models, util, app_settings

logger = logging.getLogger(__name__)

def handle_request(request):
    # Check certificate validity
    ssl_info  = util.SSLInfo(request)
    accept_self_signed = app_settings.ACCEPT_SELF_SIGNED

    if not ssl_info.cert:
        logger.error('SSL Client Authentication failed: '
            'SSL CGI variable CERT is missing')
        messages.add_message(request, messages.ERROR,
            _('SSL Client Authentication failed. '
            'No client certificate found.'))
        return redirect_to_login(request)
    elif not accept_self_signed and not ssl_info.verify:
        logger.error('SSL Client Authentication failed: '
            'SSL CGI variable VERIFY is not SUCCESS')
        messages.add_message(request, messages.ERROR,
            _('SSL Client Authentication failed. '
            'Your client certificate is not valid.'))
        return redirect_to_login(request)

    # SSL entries for this certificate?
    user = authenticate(ssl_info=ssl_info)

    # If the user is logged in, no need to create an account
    # If there is an SSL entries, no need for account creation,
    # just need to login, treated after
    if 'do_creation' in request.session and not user \
            and not request.user.is_authenticated():
        from backends import SSLBackend
        if SSLBackend().create_user(ssl_info):
            user = authenticate(ssl_info=ssl_info)
            logger.info(u'account created for %s', user)
        else:
            logger.error('account creation failure')
            messages.add_message(request, messages.ERROR,
            _('SSL Client Authentication failed. Internal server error.'))
            return redirect_to_login(request)

    # No SSL entries and no user session, redirect account linking page
    if not user and not request.user.is_authenticated():
        return render(request, 'auth/account_linking_ssl.html')

    # No SSL entries but active user session, perform account linking
    if not user and request.user.is_authenticated():
        from backend import SSLBackend
        if SSLBackend().link_user(ssl_info, request.user):
            logger.info('Successful linking of the SSL '
               'Certificate to an account, redirection to %s' % next_url)
        else:
            logger.error('login() failed')
            messages.add_message(request, messages.ERROR,
            _('SSL Client Authentication failed. Internal server error.'))
            return redirect_to_login(request)

    # SSL Entries found for this certificate,
    # if the user is logged out, we login
    if not request.user.is_authenticated():
        login(request, user)
        record_authentication_event(request, how='ssl')
        return continue_to_next_url(request)

    # SSL Entries found for this certificate, if the user is logged in, we
    # check that the SSL entry for the certificate is this user.
    # else, we make this certificate point on that user.
    if user.username != request.user.username:
        logger.warning(u'The certificate belongs to %s, '
            'but %s is logged with, we change the association!',
            user, request.user)
        from backends import SSLBackend
        cert = SSLBackend().get_certificate(ssl_info)
        cert.user = request.user
        cert.save()
    return continue_to_next_url(request)

###
 # post_account_linking
 # @request
 #
 # Called after an account linking.
 ###
@csrf_exempt
def post_account_linking(request):
    logger.info('auth2_ssl Return after account linking form filled')
    if request.method == "POST":
        if 'do_creation' in request.POST \
                and request.POST['do_creation'] == 'on':
            logger.info('account creation asked')
            request.session['do_creation'] = 'do_creation'
            return redirect_to_login(request, login_url='user_signin_ssl')
        form = AuthenticationForm(data=request.POST)
        if form.is_valid():
            logger.info('form valid')
            user = form.get_user()
            try:
                login(request, user)
                record_authentication_event(request, how='password')
            except:
                logger.error('login() failed')
                messages.add_message(request, messages.ERROR,
                _('SSL Client Authentication failed. Internal server error.'))

            logger.debug('session opened')
            return redirect_to_login(request, login_url='user_signin_ssl')
        else:
            logger.warning('form not valid - Try again! (Brute force?)')
            return render(request, 'auth/account_linking_ssl.html')
    else:
        return render(request, 'auth/account_linking_ssl.html')

def profile(request, template_name='ssl/profile.html', *args, **kwargs):
    context = kwargs.pop('context', {})
    certificates = models.ClientCertificate.objects.filter(user=request.user)
    context.update({'certificates': certificates})
    return render_to_string(template_name, context, request=request)

def delete_certificate(request, certificate_pk):
    qs = models.ClientCertificate.objects.filter(pk=certificate_pk)
    count = qs.count()
    qs.delete()
    if count:
        logger.info('client certificate %s deleted', certificate_pk)
        messages.info(request, _('Certificate deleted.'))
    return redirect(request, 'account_management',
            fragment='a2-ssl-certificate-profile')

class SslErrorView(TemplateView):
    template_name = 'error_ssl.html'
error_ssl = SslErrorView.as_view()
