from django.shortcuts import redirect
from django.views.generic.edit import UpdateView, FormView
from django.views.generic.base import TemplateResponseMixin, View
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from django.http import HttpResponseRedirect


import django_journal


from . import models
from . import forms
from . import cbv
from . import utils


class ProfileView(cbv.FormWithPrefixMixin, cbv.FormWithRequestMixin, cbv.FormWithPostTarget, UpdateView):
    form_class = forms.ProfileForm
    template_name = 'docbow/profile.html'
    prefix = 'profile'
    success_url = '#profile'

    def get_object(self):
        try:
            return models.DocbowProfile.objects.get(user=self.request.user)
        except models.DocbowProfile.DoesNotExist:
            return None

    def is_post_target(self):
        return 'profile-clear_mobile_phone' in self.request.POST or \
                'profile-validate' in self.request.POST

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        if 'profile-clear_mobile_phone' in self.request.POST:
            if self.object:
                self.object.mobile_phone = ''
                self.object.save()
            return redirect('profile')
        return super(ProfileView, self).post(request, *args, **kwargs)

    def form_valid(self, form):
        self.request.record('update-profile', 'modified its profile',
                **form.cleaned_data)
        return super(ProfileView, self).form_valid(form)


class DelegateView(cbv.FormWithPrefixMixin, cbv.FormWithPostTarget, FormView):
    form_class = forms.DelegationForm
    template_name = 'docbow/delegate.html'
    success_url = '#delegate'
    prefix = 'delegate'

    def __init__(self, *args, **kwargs):
        super(DelegateView, self).__init__(*args, **kwargs)

    def add_journal_to_delegations(self, delegations):
        delegations__to = [ delegation.to for delegation in delegations ]
        journals = django_journal.Journal.objects \
                .for_objects(delegations__to) \
                .filter(tag__name='login') \
                .order_by('-time')[:len(delegations__to)*5]
        journal_by_users = dict()
        for journal in journals:
            for objectdata in journal.objectdata_set.all():
                if objectdata.tag.name == 'delegate':
                    journal_by_users.setdefault(objectdata.object_id, []) \
                            .append(journal.time)
        for delegation in delegations:
            delegation.journals = journal_by_users.get(delegation.to.id, [])[:5]

    @property
    def delegations(self):
        qs = models.Delegation.objects.all()
        qs = qs.filter(by=self.request.user)
        qs = qs.select_related()
        qs = qs.prefetch_related('to__docbowprofile')
        self.add_journal_to_delegations(qs)
        return qs

    def get_form_kwargs(self, **kwargs):
        kwargs = super(DelegateView, self).get_form_kwargs(**kwargs)
        kwargs['user'] = self.request.user
        kwargs['delegatees'] = [ delegation.to for delegation in self.delegations ]
        return kwargs

    def delete(self, delegation):
        request = self.request
        delegation.delete()
        delegate_user = delegation.to
        request.record('delete-delegation', 'deleted delegation '
                '{delegation} to user {delegated}',
                delegation=delegation.id,
                delegated=delegate_user)
        user = request.user
        # notify delegate
        ctx = {
                'user': utils.clean_ldap_user(user),
                'delegate': delegate_user,
                'to': [delegate_user.email],
                'reply_to': user.email,
              }

        models.Notification.objects.create(kind='delete-delegation', ctx=ctx)
        messages.info(request, _('Delegation %s supressed') %
                delegate_user)
        messages.info(request, _('%s has been notified.') %
                delegate_user.email)
        request.record('notify', 'notified {email} of '
                'the deletion of its delegate user',
                email=delegate_user.email)

        # delete guest accounts
        if delegation.guest_delegate:
            delegate_user.delete()
            request.record('delete-delegate', 'deleted delegate '
                    'user {username}, {first_name} {last_name}, '
                    '({email})',
                    username=delegate_user.username,
                    first_name=delegate_user.first_name,
                    last_name=delegate_user.last_name,
                    email=delegate_user.email)
        return HttpResponseRedirect(self.success_url)

    def is_post_target(self):
        if 'delegate-create' in self.request.POST:
            return True
        for delegation in self.delegations:
            username = delegation.to.username
            if 'delegate-delete-{username}.x'.format(username=username) in \
                    self.request.POST:
                return True
        return False

    def post(self, request, *args, **kwargs):
        if 'delegate-create' in request.POST:
            return super(DelegateView, self).post(request, *args, **kwargs)
        for delegation in self.delegations:
            username = delegation.to.username
            if 'delegate-delete-{username}.x'.format(username=username) in \
                    self.request.POST:
                return self.delete(delegation)
        return self.get(request, *args, **kwargs)

    def form_valid(self, form):
        request = self.request
        is_guest = not form.cleaned_data.get('existing_user')
        ctx = {
            'user': utils.clean_ldap_user(request.user),
            'reply_to' :request.user.email,
            'is_guest': is_guest,
        }
        if is_guest:
            delegate_username = utils.get_free_delegation_number(request.user)
            delegate_user = User(username=delegate_username,
                    first_name=form.cleaned_data.get('first_name', ''),
                    last_name=form.cleaned_data.get('last_name', ''),
                    email=form.cleaned_data.get('email', ''))
            delegate_user.set_unusable_password()
            delegate_user.save()
            models.DocbowProfile.objects.create(user=delegate_user, is_guest=True)
            delegation, created = models.Delegation.objects.get_or_create(by=request.user,
                    to=delegate_user)
            request.record('create-delegate', 'created delegate with '
                    'parameters {first_name} {last_name} <{email}>',
                    **form.cleaned_data)
            ctx.update({
                'password_reset_link':
                    utils.make_password_reset_url(request, delegate_user),
                'to': [form.cleaned_data['email']],
                'delegate': delegate_user,
                'delegation': delegation,
            })
            messages.info(request, 
                    _('New delegation to user %s created.') % delegate_user.username)
            messages.info(request,
                    _('A notification was sent to %s about this new delegation') % delegate_user.email)
        else:
            delegate_user = form.cleaned_data.get('existing_user')
            delegate_username = delegate_user.username
            delegation, created = models.Delegation.objects.get_or_create(by=request.user,
                    to=delegate_user)
            ctx.update({
                'to': models.all_emails(delegate_user),
                'delegate': delegate_user,
                'delegation': delegation,
            })
            messages.info(request,
                    _('New delegation to user %s created.') %
                    delegate_user.username)
            messages.info(request, 
                    _('A notification was sent to %s about this new delegation') % delegate_user.get_full_name())
        models.Notification.objects.create(kind='new-delegation', ctx=ctx)
        request.record('create-delegation', 'created delegation to '
                'user {delegated}', delegated=delegate_user)
        return super(DelegateView, self).form_valid(form)


    def get_context_data(self, **kwargs):
        ctx = super(DelegateView, self).get_context_data(**kwargs)
        ctx['delegations'] = self.delegations
        ctx['received_delegations'] = self.request.user.delegations_by.all()
        return ctx


class PasswordChangeView(cbv.FormWithPrefixMixin, cbv.FormWithPostTarget, FormView):
    template_name = 'registration/password_change_form.html'
    form_class = forms.PasswordChangeFormWithLogging
    prefix = 'password-change'
    success_url = '#password-change'

    def is_post_target(self):
        return 'password-change-validate' in self.request.POST

    def form_valid(self, form):
        messages.info(self.request, _('Password changed'))
        return super(PasswordChangeView, self).form_valid(form)

    def get_form_kwargs(self, **kwargs):
        kwargs = super(PasswordChangeView, self).get_form_kwargs(**kwargs)
        kwargs['user'] = self.request.user
        return kwargs


class FullProfileView(TemplateResponseMixin, View):
    # multiplex all profile views
    template_name = 'docbow/full-profile.html'
    subviews = (
            ('profile_form', ProfileView),
            ('delegate_form', DelegateView),
            ('password_change_form', PasswordChangeView),
    )

    def dispatch(self, request, *args, **kwargs):
        if models.is_guest(request.user):
            self.subviews = filter(lambda s: s[0] != 'delegate_form', self.subviews)
            print self.subviews
        return super(FullProfileView, self).dispatch(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        ctx = {}
        for var_name, view_class in self.subviews:
            res = self.get_view(var_name, view_class)
            if isinstance(res, dict):
                ctx.update(res)
            else:
                return res
        return self.render_to_response(ctx)

    def get_view(self, var_name, view_class):
        view = view_class()
        view.request, view.args, view.kwargs = \
                self.request, self.args, self.kwargs
        if hasattr(view, 'get_object'):
            view.object = view.get_object()
        if self.request.method == 'POST' and view.is_post_target():
            res = view.post(self.request, *self.args, **self.kwargs)
            if isinstance(res, HttpResponseRedirect):
                return res
            else:
                ctx = res.context_data
                ctx[var_name] = ctx.pop('form')
                return ctx
        else:
            form_class = view.get_form_class()
            form = view.get_form(form_class)
            return view.get_context_data(**{var_name: form})

    def get(self, request, *args, **kwargs):
        ctx = {}
        for var_name, view_class in self.subviews:
            ctx.update(self.get_view(var_name, view_class))
        return self.render_to_response(ctx)


