import itertools

from django.views import generic
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse
from django.conf import settings
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.db.models import Count
from django.shortcuts import HttpResponse

from oscar.core.loading import get_classes
from oscar.apps.promotions.conf import PROMOTION_CLASSES

SingleProduct, RawHTML, Image, MultiImage, AutomaticProductList, \
    PagePromotion, HandPickedProductList \
    = get_classes('promotions.models',
                  ['SingleProduct', 'RawHTML', 'Image', 'MultiImage',
                   'AutomaticProductList', 'PagePromotion',
                   'HandPickedProductList'])
SelectForm, RawHTMLForm, PagePromotionForm, HandPickedProductListForm, \
    SingleProductForm, OrderedProductFormSet \
    = get_classes('dashboard.promotions.forms',
                  ['PromotionTypeSelectForm', 'RawHTMLForm',
                   'PagePromotionForm', 'HandPickedProductListForm',
                   'SingleProductForm', 'OrderedProductFormSet'])


class ListView(generic.TemplateView):
    template_name = 'dashboard/promotions/promotion_list.html'

    def get_context_data(self):
        # Need to load all promotions of all types and chain them together
        # no pagination required for now.
        data = []
        num_promotions = 0
        for klass in PROMOTION_CLASSES:
            objects = klass.objects.all()
            num_promotions += objects.count()
            data.append(objects)
        promotions = itertools.chain(*data)
        ctx = {
            'num_promotions': num_promotions,
            'promotions': promotions,
            'select_form': SelectForm(),
        }
        return ctx


class CreateRedirectView(generic.RedirectView):
    permanent = True

    def get_redirect_url(self, **kwargs):
        code = self.request.GET.get('promotion_type', None)
        urls = {}
        for klass in PROMOTION_CLASSES:
            urls[klass.classname()] = reverse('dashboard:promotion-create-%s' %
                                              klass.classname())
        return urls.get(code, None)


class PageListView(generic.TemplateView):
    template_name = 'dashboard/promotions/pagepromotion_list.html'

    def get_context_data(self, *args, **kwargs):
        pages = PagePromotion.objects.all().values(
            'page_url').distinct().annotate(freq=Count('id'))
        return {'pages': pages}


class PageDetailView(generic.TemplateView):
    template_name = 'dashboard/promotions/page_detail.html'

    def get_context_data(self, *args, **kwargs):
        path = self.kwargs['path']
        return {'page_url': path,
                'positions': self.get_positions_context_data(path), }

    def get_positions_context_data(self, path):
        ctx = []
        for code, name in settings.OSCAR_PROMOTION_POSITIONS:
            promotions = PagePromotion._default_manager.select_related() \
                                                       .filter(page_url=path,
                                                               position=code)
            ctx.append({
                'code': code,
                'name': name,
                'promotions': promotions.order_by('display_order'),
            })
        return ctx

    def post(self, request, **kwargs):
        """
        When called with a post request, try and get 'promo[]' from
        the post data and use it to reorder the page content blocks.
        """
        data = dict(request.POST).get('promo[]')
        self._save_page_order(data)
        return HttpResponse(status=200)

    def _save_page_order(self, data):
        """
        Save the order of the pages. This gets used when an ajax request
        posts backa new order for promotions within page regions.
        """
        for index, item in enumerate(data):
            page = PagePromotion.objects.get(pk=item)
            if page.display_order != index:
                page.display_order = index
                page.save()


class PromotionMixin(object):

    def get_template_names(self):
        return ['dashboard/promotions/%s_form.html' % self.model.classname(),
                'dashboard/promotions/form.html']


class DeletePagePromotionView(generic.DeleteView):
    template_name = 'dashboard/promotions/delete_pagepromotion.html'
    model = PagePromotion

    def get_success_url(self):
        messages.info(self.request, _("Content block removed successfully"))
        return reverse('dashboard:promotion-list-by-url',
                       kwargs={'path': self.object.page_url})


# ============
# CREATE VIEWS
# ============


class CreateView(PromotionMixin, generic.CreateView):

    def get_success_url(self):
        messages.success(self.request, _("Content block created successfully"))
        return reverse('dashboard:promotion-update',
                       kwargs={'ptype': self.model.classname(),
                               'pk': self.object.id})

    def get_context_data(self, *args, **kwargs):
        ctx = super(CreateView, self).get_context_data(*args, **kwargs)
        ctx['heading'] = self.get_heading()
        return ctx

    def get_heading(self):
        if hasattr(self, 'heading'):
            return getattr(self, 'heading')
        return _('Create a new %s content block') % self.model._type


class CreateRawHTMLView(CreateView):
    model = RawHTML
    form_class = RawHTMLForm


class CreateSingleProductView(CreateView):
    model = SingleProduct
    form_class = SingleProductForm


class CreateImageView(CreateView):
    model = Image


class CreateMultiImageView(CreateView):
    model = MultiImage


class CreateAutomaticProductListView(CreateView):
    model = AutomaticProductList


class CreateHandPickedProductListView(CreateView):
    model = HandPickedProductList
    form_class = HandPickedProductListForm

    def get_context_data(self, **kwargs):
        ctx = super(CreateHandPickedProductListView,
                    self).get_context_data(**kwargs)
        if 'product_formset' not in kwargs:
            ctx['product_formset'] \
                = OrderedProductFormSet(instance=self.object)
        return ctx

    def form_valid(self, form):
        promotion = form.save(commit=False)
        product_formset = OrderedProductFormSet(self.request.POST,
                                                instance=promotion)
        if product_formset.is_valid():
            promotion.save()
            product_formset.save()
            self.object = promotion
            messages.success(self.request,
                             _('Product list content block created'))
            return HttpResponseRedirect(self.get_success_url())

        ctx = self.get_context_data(product_formset=product_formset)
        return self.render_response(ctx)


# ============
# UPDATE VIEWS
# ============


class UpdateView(PromotionMixin, generic.UpdateView):
    actions = ('add_to_page', 'remove_from_page')
    link_form_class = PagePromotionForm

    def get_context_data(self, *args, **kwargs):
        ctx = super(UpdateView, self).get_context_data(*args, **kwargs)
        ctx['heading'] = _("Update content block")
        ctx['promotion'] = self.get_object()
        ctx['link_form'] = self.link_form_class()
        content_type = ContentType.objects.get_for_model(self.model)
        ctx['links'] = PagePromotion.objects.filter(content_type=content_type,
                                                    object_id=self.object.id)
        return ctx

    def post(self, request, *args, **kwargs):
        action = request.POST.get('action', None)
        if action in self.actions:
            self.object = self.get_object()
            return getattr(self, action)(self.object, request, *args, **kwargs)
        return super(UpdateView, self).post(request, *args, **kwargs)

    def get_success_url(self):
        messages.info(self.request, _("Content block updated successfully"))
        return reverse('dashboard:promotion-list')

    def add_to_page(self, promotion, request, *args, **kwargs):
        instance = PagePromotion(content_object=self.get_object())
        form = self.link_form_class(request.POST, instance=instance)
        if form.is_valid():
            form.save()
            page_url = form.cleaned_data['page_url']
            messages.success(request, _("Content block '%(block)s' added to"
                                        " page '%(page)s'")
                             % {'block': promotion.name,
                                'page': page_url})
            return HttpResponseRedirect(reverse('dashboard:promotion-update',
                                                kwargs=kwargs))

        main_form = self.get_form_class()(instance=self.object)
        ctx = self.get_context_data(form=main_form)
        ctx['link_form'] = form
        return self.render_to_response(ctx)

    def remove_from_page(self, promotion, request, *args, **kwargs):
        link_id = request.POST['pagepromotion_id']
        try:
            link = PagePromotion.objects.get(id=link_id)
        except PagePromotion.DoesNotExist:
            messages.error(request, _("No link found to delete"))
        else:
            page_url = link.page_url
            link.delete()
            messages.success(request, _("Content block removed from page '%s'")
                             % page_url)
        return HttpResponseRedirect(reverse('dashboard:promotion-update',
                                            kwargs=kwargs))


class UpdateRawHTMLView(UpdateView):
    model = RawHTML
    form_class = RawHTMLForm


class UpdateSingleProductView(UpdateView):
    model = SingleProduct
    form_class = SingleProductForm


class UpdateImageView(UpdateView):
    model = Image


class UpdateMultiImageView(UpdateView):
    model = MultiImage


class UpdateAutomaticProductListView(UpdateView):
    model = AutomaticProductList


class UpdateHandPickedProductListView(UpdateView):
    model = HandPickedProductList
    form_class = HandPickedProductListForm

    def get_context_data(self, **kwargs):
        ctx = super(UpdateHandPickedProductListView,
                    self).get_context_data(**kwargs)
        if 'product_formset' not in kwargs:
            ctx['product_formset'] \
                = OrderedProductFormSet(instance=self.object)
        return ctx

    def form_valid(self, form):
        promotion = form.save(commit=False)
        product_formset = OrderedProductFormSet(self.request.POST,
                                                instance=promotion)
        if product_formset.is_valid():
            promotion.save()
            product_formset.save()
            self.object = promotion
            messages.success(self.request, _('Product list promotion updated'))
            return HttpResponseRedirect(self.get_success_url())

        ctx = self.get_context_data(product_formset=product_formset)
        return self.render_to_response(ctx)

# ============
# DELETE VIEWS
# ============


class DeleteView(generic.DeleteView):
    template_name = 'dashboard/promotions/delete.html'

    def get_success_url(self):
        messages.info(self.request, _("Content block deleted successfully"))
        return reverse('dashboard:promotion-list')


class DeleteRawHTMLView(DeleteView):
    model = RawHTML


class DeleteSingleProductView(DeleteView):
    model = SingleProduct


class DeleteImageView(DeleteView):
    model = Image


class DeleteMultiImageView(DeleteView):
    model = MultiImage


class DeleteAutomaticProductListView(DeleteView):
    model = AutomaticProductList


class DeleteHandPickedProductListView(DeleteView):
    model = HandPickedProductList
