from __future__ import unicode_literals

import collections
import urllib

from django import shortcuts
from django.db import transaction
from django.conf import settings
from django.core.urlresolvers import reverse_lazy
from django.views import generic as views
from django.views.generic.base import TemplateView, View
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django_yauth.decorators import yalogin_required
import yenv

from app import tanker
from app import forms
from app.views import mixins
from core import models
from core.models import StaticPage
from core.tanker.api import Keyset, Key


__all__ = [
    'ListStaticPageView',
    'StaticPageView',
    'PreviewPageView',
    'DeletePageView'
]


class ListStaticPageView(TemplateView):

    template_name = 'static_page.html'

    def get_context_data(self, **kwargs):
        context = super(ListStaticPageView, self).get_context_data(**kwargs)
        pages_by_type = collections.defaultdict(dict)
        pages = StaticPage.objects.order_by('page_type', 'language').all()

        for page in pages:
            pages_by_type[page.page_type][page.language] = page

        context['pages'] = pages_by_type.items()
        return context

    @method_decorator(yalogin_required)
    @method_decorator(permission_required('core.change_staticpage', raise_exception=True))
    def dispatch(self, request, *args, **kwargs):
        return super(
            ListStaticPageView,
            self,
        ).dispatch(request, *args, **kwargs)


class StatusFieldsMixin(object):

    def get_context_data(self, **kwargs):
        return super(StatusFieldsMixin, self).get_context_data(
            page_translation_model=models.PageTranslation,
            **kwargs
        )


class ListPageView(
        StatusFieldsMixin, mixins.SuperuserRequiredMixin, views.ListView):
    context_object_name = 'pages'
    template_name = 'static_page.html'
    
    def get_queryset(self):
        return models.EditablePage.objects.all().order_by('order')

    def get_context_data(self, **kwargs):
        context = super(ListPageView, self).get_context_data(**kwargs)
        context.update(
            statuses=models.PageTranslation.STATUSES,
            pages=self._get_page_translations(),
        )
        return context

    def _get_page_translations(self):
        for page in self.object_list:
            yield page, self._get_pages_by_status(page)

    def _get_pages_by_status(self, page):
        for status, _ in models.PageTranslation.STATUSES:
            yield models.PageTranslation.objects.filter(page=page, status=status).first()


class CreatePageView(
        mixins.SuperuserRequiredMixin, StatusFieldsMixin, views.CreateView):
    form_class = forms.EditablePageForm
    success_url = reverse_lazy('page:list')
    template_name = 'page/create.html'

    def form_valid(self, form):
        response = super(CreatePageView, self).form_valid(form)
        translation = models.PageTranslation.objects.create(
            page=form.instance,
            status=models.PageTranslation.STATUS_DRAFT,
            title_ru=form.cleaned_data['title_ru'],
            text_ru=form.cleaned_data['text_ru'],
        )
        return response


class PageUpdateView(
        mixins.SuperuserRequiredMixin, StatusFieldsMixin, views.UpdateView):
    form_class = forms.EditablePageForm
    model = models.EditablePage
    success_url = reverse_lazy('page:list')
    template_name = 'page/update.html'
    status = None

    def get_initial(self):
        translation = self._get_translation()
        return dict(
            title_ru=translation.title_ru,
            text_ru=translation.text_ru,
        )

    def _get_translation(self):
        return models.PageTranslation.objects.get(
            page=self.object,
            status=self.status,
        )

    def get_context_data(self, **kwargs):
        tanker_admin_url = settings.TANKER_ADMIN_URL + '?' + urllib.urlencode({
            'project': settings.TANKER_ID,
            'branch': 'master',
            'keyset': tanker.PageKeyset('index').name,
        })
        return super(PageUpdateView, self).get_context_data(
            tanker_admin_url=tanker_admin_url,
            **kwargs
        )

    def form_valid(self, form):
        response = super(PageUpdateView, self).form_valid(form)
        try:
            self._update_page_translation(form)
        except tanker.MissingTranslationError as exc:
            redirect_url_name = 'page:update-%s' % self.status
            page_slug = form.cleaned_data['slug']
            messages.error(self.request, _('Missing translation: %s' % exc))
            return shortcuts.redirect(redirect_url_name, slug=page_slug)
        return response

    def _update_page_translation(self, form):
        raise NotImplemented

    def _update_translation_content(self, translation, form):
        for attr in ('title_ru', 'text_ru'):
            setattr(translation, attr, form.cleaned_data[attr])
        return translation

    def _delete_substituted_translation(self, status):
        return models.PageTranslation.objects.filter(page=self.object, status=status).delete()

    def _clone_translation(self, translation, status):
        translation.pk = None
        translation.status = status
        translation.title_en = None
        translation.text_en = None
        return translation

    def _upload_to_tanker(self, translation):
        client = self._get_tanker_client()
        keyset = tanker.PageUploadKeyset(
            slug=translation.page.slug,
            name=translation.title_ru,
            text=translation.text_ru,
        )
        existing_keysets = client.list()
        # Check that existing_keysets is Iterable not Mock from tests
        if (isinstance(existing_keysets, collections.Iterable)
                and keyset.name in existing_keysets):
            result = client.merge(keyset)
        else:
            result = client.create(keyset)
        return result

    def _get_tanker_client(self):
        return tanker.TankerAdapter(settings.TANKER_ID, settings.TANKER_TOKEN)

    def _is_draft(self, status):
        return status == models.PageTranslation.STATUS_DRAFT

    def _is_uploaded(self, status):
        return status == models.PageTranslation.STATUS_UPLOADED

    def _is_downloaded(self, status):
        return status == models.PageTranslation.STATUS_DOWNLOADED

    def _is_published(self, status):
        return status == models.PageTranslation.STATUS_PUBLISHED


class DraftUpdateView(PageUpdateView):
    status = models.PageTranslation.STATUS_DRAFT

    @transaction.atomic
    def _update_page_translation(self, form):
        next_status = form.cleaned_data.get('status')
        translation = self._update_translation_content(
            self._get_translation(), form)
        if self._is_uploaded(next_status):
            self._delete_substituted_translation(next_status)
            translation.status = next_status
            self._upload_to_tanker(translation)
        return translation.save()


class UploadedUpdateView(PageUpdateView):
    status = models.PageTranslation.STATUS_UPLOADED
    template_name = 'page/update-uploaded.html'

    @transaction.atomic
    def _update_page_translation(self, form):
        next_status = form.cleaned_data.get('status')
        translation = self._update_translation_content(
            self._get_translation(), form)
        if self._is_uploaded(next_status):
            self._upload_to_tanker(translation)
        elif self._is_draft(next_status):
            self._delete_substituted_translation(next_status)
            translation = self._clone_translation(translation, next_status)
        elif self._is_downloaded(next_status):
            client = self._get_tanker_client()
            keyset = tanker.PageKeyset(translation.page.slug)
            keyset_content = client.download(keyset)
            translation_keyset = tanker.PageKeysetTranslationAdapter(
                slug=translation.page.slug, content=keyset_content)
            self._delete_substituted_translation(next_status)
            for key, value in translation_keyset.get_translation_keys():
                setattr(translation, key, value)
            translation.status = models.PageTranslation.STATUS_DOWNLOADED
        return translation.save()


class EnglishTranslationMixin(object):

    def get_context_data(self, **kwargs):
        translation = self._get_translation()
        return super(EnglishTranslationMixin, self).get_context_data(
            title_translation=translation.title_en,
            text_translation=translation.text_en,
            **kwargs
        )


class DownloadedUpdateView(EnglishTranslationMixin, PageUpdateView):
    status = models.PageTranslation.STATUS_DOWNLOADED
    template_name = 'page/update-downloaded.html'

    @transaction.atomic
    def _update_page_translation(self, form):
        next_status = form.cleaned_data.get('status')
        translation = self._get_translation()
        if self._is_published(next_status):
            self._delete_substituted_translation(next_status)
            translation.status = next_status
        elif self._is_draft(next_status) or self._is_uploaded(next_status):
            self._delete_substituted_translation(next_status)
            translation = self._update_translation_content(
                self._clone_translation(translation, next_status), form)
        return translation.save()


class PublishedUpdateView(EnglishTranslationMixin, PageUpdateView):
    status = models.PageTranslation.STATUS_PUBLISHED
    template_name = 'page/update-published.html'

    @transaction.atomic
    def _update_page_translation(self, form):
        next_status = form.cleaned_data.get('status')
        translation = self._update_translation_content(
            self._get_translation(), form)
        if self._is_draft(next_status):
            self._delete_substituted_translation(next_status)
            translation = self._clone_translation(translation, next_status)
        elif self._is_uploaded(next_status):
            self._delete_substituted_translation(next_status)
            translation = self._clone_translation(translation, next_status)
            self._upload_to_tanker(translation)
        return translation.save()


class StaticPageView(TemplateView):
    template_name = 'static_page_edit.html'

    def _send_to_tanker(self, instance):
        tanker_client = tanker.TankerAdapter(
            project_id=settings.TANKER_ID,
            base_url=settings.TANKER_URL,
            token=settings.TANKER_TOKEN,
        )
        # FIXME(remedy) continue here
        t = object()

        keyset_name = '%s_%s' % (yenv.type, StaticPage.TANKER_TYPE)
        keyset = Keyset(keyset_name)

        key_name = '{page_type}.{field}'.format

        language = instance.get_tanker_language_code()

        for field in StaticPage.TRANSLATABLE_FIELDS:
            key = Key(key_name(page_type=instance.page_type, field=field))
            key.add_translation(
                language=language,
                form=getattr(instance, field),
            )
            keyset.add_key(key)
        t.merge_keyset(keyset, language)

    def get(self, request, page_id=None):
        instance = None
        if page_id is not None:
            instance = get_object_or_404(StaticPage, pk=page_id)
        form = forms.StaticPageForm(instance=instance)
        return self.render_to_response(dict(form=form))

    def post(self, request, page_id=None):
        instance = None
        if page_id is not None:
            instance = get_object_or_404(StaticPage, pk=page_id)
        form = forms.StaticPageForm(request.POST, instance=instance)
        if form.is_valid():
            instance = form.save()
            self._send_to_tanker(instance)
            messages.success(request, str(_('Page was successfully created')))
            return redirect('page:list')
        messages.error(request, str(_('Page already exist')))
        return self.render_to_response(dict(form=form))

    @method_decorator(yalogin_required)
    @method_decorator(permission_required('core.change_staticpage', raise_exception=True))
    @method_decorator(permission_required('core.add_staticpage', raise_exception=True))
    def dispatch(self, request, *args, **kwargs):
        return super(StaticPageView, self).dispatch(request, *args, **kwargs)


class DeletePageView(mixins.SuperuserRequiredMixin, views.DeleteView):
    model = models.EditablePage
    success_url = reverse_lazy('page:list')


class PreviewPageView(mixins.SuperuserRequiredMixin, views.DetailView):
    model = models.EditablePage
    template_name = 'static_page_preview.html'

    def get_context_data(self, **kwargs):
        translation = self._get_translation()
        language = self.kwargs['language']
        html = getattr(translation, 'html_%s' % language)
        return super(PreviewPageView, self).get_context_data(html=html, **kwargs) 

    def _get_translation(self):
        return models.PageTranslation.objects.get(
            page=self.object,
            status=self.kwargs['status'],
        )
