# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from __future__ import absolute_import
from django.contrib import messages
from django.db.models import Q
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import redirect, get_object_or_404
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _
from django.views.generic.base import TemplateView, View
from django.views.generic.edit import FormView
from django.views.generic.list import ListView

from wiki.core.http import send_file
from wiki.decorators import get_article, response_forbidden
from wiki.plugins.attachments import models, settings, forms
from wiki.views.mixins import ArticleMixin


class AttachmentView(ArticleMixin, FormView):
    
    form_class = forms.AttachmentForm
    template_name="wiki/plugins/attachments/index.html"
    
    @method_decorator(get_article(can_read=True))
    def dispatch(self, request, article, *args, **kwargs):
        if article.can_moderate(request.user):
            self.attachments = models.Attachment.objects.filter(
                articles=article, current_revision__deleted=False
            ).exclude(
                current_revision__file=None
            ).order_by('original_filename')
            
            self.form_class = forms.AttachmentArchiveForm
        else:
            self.attachments = models.Attachment.objects.active().filter(articles=article)
        
        # Fixing some weird transaction issue caused by adding commit_manually to form_valid
        return super(AttachmentView, self).dispatch(request, article, *args, **kwargs)
    
    def form_valid(self, form):
        
        if (self.request.user.is_anonymous() and not settings.ANONYMOUS or 
            not self.article.can_write(self.request.user) or
            self.article.current_revision.locked):
            return response_forbidden(self.request, self.article, self.urlpath)
        
        attachment_revision = form.save()
        if isinstance(attachment_revision, list):
            messages.success(self.request, _('Successfully added: %s') % (", ".join([ar.get_filename() for ar in attachment_revision])))
        else:
            messages.success(self.request, _('%s was successfully added.') % attachment_revision.get_filename())
        self.article.clear_cache()
        
        return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id)
    
    def get_form_kwargs(self):
        kwargs = FormView.get_form_kwargs(self)
        kwargs['article'] = self.article
        kwargs['request'] = self.request
        return kwargs
    
    def get_context_data(self, **kwargs):
        kwargs['attachments'] = self.attachments
        kwargs['deleted_attachments'] = models.Attachment.objects.filter(articles=self.article, current_revision__deleted=True)
        kwargs['search_form'] = forms.SearchForm()
        kwargs['selected_tab'] = 'attachments'
        kwargs['anonymous_disallowed'] = self.request.user.is_anonymous() and not settings.ANONYMOUS
        return super(AttachmentView, self).get_context_data(**kwargs)


class AttachmentHistoryView(ArticleMixin, TemplateView):
    
    template_name="wiki/plugins/attachments/history.html"
    
    @method_decorator(get_article(can_read=True))
    def dispatch(self, request, article, attachment_id, *args, **kwargs):
        if article.can_moderate(request.user):
            self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article)
        else:
            self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article)
        return super(AttachmentHistoryView, self).dispatch(request, article, *args, **kwargs)
    
    def get_context_data(self, **kwargs):
        kwargs['attachment'] = self.attachment
        kwargs['revisions'] = self.attachment.attachmentrevision_set.all().order_by('-revision_number')
        kwargs['selected_tab'] = 'attachments'
        return super(AttachmentHistoryView, self).get_context_data(**kwargs)


class AttachmentReplaceView(ArticleMixin, FormView):
    
    form_class = forms.AttachmentForm
    template_name="wiki/plugins/attachments/replace.html"
    
    @method_decorator(get_article(can_write=True, not_locked=True))
    def dispatch(self, request, article, attachment_id, *args, **kwargs):
        if request.user.is_anonymous() and not settings.ANONYMOUS:
            return response_forbidden(request, article, kwargs.get('urlpath', None))
        if article.can_moderate(request.user):
            self.attachment = get_object_or_404(models.Attachment, 
                id=attachment_id, articles=article)
            self.can_moderate = True
        else:
            self.attachment = get_object_or_404(models.Attachment.objects.active(), 
                id=attachment_id, articles=article)
            self.can_moderate = False
        return super(AttachmentReplaceView, self).dispatch(request, article, *args, **kwargs)
    
    def get_form_class(self):
        if self.can_moderate:
            return forms.AttachmentReplaceForm
        else:
            return forms.AttachmentForm
    
    def form_valid(self, form):
        
        try:
            attachment_revision = form.save(commit=True)
            attachment_revision.set_from_request(self.request)
            attachment_revision.previous_revision = self.attachment.current_revision
            attachment_revision.save()
            self.attachment.current_revision = attachment_revision
            self.attachment.save()
            messages.success(self.request, _('%s uploaded and replaces old attachment.') % attachment_revision.get_filename())
            self.article.clear_cache()
        except models.IllegalFileExtension as e:
            messages.error(self.request, _('Your file could not be saved: %s') % e)
            return redirect("wiki:attachments_replace", attachment_id=self.attachment.id,
                            path=self.urlpath.path, article_id=self.article.id)
        
        if self.can_moderate:
            older_revisions = self.attachment.attachmentrevision_set.exclude(
                id=attachment_revision.id,
                created__lte=attachment_revision.created,
            )
            # Because of signalling, the files are automatically removed...
            older_revisions.delete()
            
        
        return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id)
    
    def get_form(self, form_class):
        form = FormView.get_form(self, form_class)
        form.fields['file'].help_text = _('Your new file will automatically be renamed to match the file already present. Files with different extensions are not allowed.')
        return form
    
    def get_form_kwargs(self):
        kwargs = super(AttachmentReplaceView, self).get_form_kwargs()
        kwargs['article'] = self.article
        kwargs['request'] = self.request
        kwargs['attachment'] = self.attachment
        return kwargs
    
    def get_initial(self, **kwargs):
        return {'description': self.attachment.current_revision.description}
    
    def get_context_data(self, **kwargs):
        kwargs['attachment'] = self.attachment
        kwargs['selected_tab'] = 'attachments'
        return super(AttachmentReplaceView, self).get_context_data(**kwargs)


class AttachmentDownloadView(ArticleMixin, View):
    
    @method_decorator(get_article(can_read=True))
    def dispatch(self, request, article, attachment_id, *args, **kwargs):
        if article.can_moderate(request.user):
            self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article)
        else:
            self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article)
        revision_id = kwargs.get('revision_id', None)
        if revision_id:
            self.revision = get_object_or_404(models.AttachmentRevision, id=revision_id, attachment__articles=article)
        else:
            self.revision = self.attachment.current_revision
        return super(AttachmentDownloadView, self).dispatch(request, article, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        if self.revision:
            if settings.USE_LOCAL_PATH:
                try:
                    return send_file(request, self.revision.file.path, 
                                     self.revision.created, self.attachment.original_filename)
                except OSError:
                    pass
            else:
                return HttpResponseRedirect(self.revision.file.url)
        raise Http404
    
class AttachmentChangeRevisionView(ArticleMixin, View):
    
    form_class = forms.AttachmentForm
    template_name="wiki/plugins/attachments/replace.html"
    
    @method_decorator(get_article(can_write=True, not_locked=True))
    def dispatch(self, request, article, attachment_id, revision_id, *args, **kwargs):
        if article.can_moderate(request.user):
            self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article)
        else:
            self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article)
        self.revision = get_object_or_404(models.AttachmentRevision, id=revision_id, attachment__articles=article)
        return super(AttachmentChangeRevisionView, self).dispatch(request, article, *args, **kwargs)
    
    def post(self, request, *args, **kwargs):
        self.attachment.current_revision = self.revision
        self.attachment.save()
        self.article.clear_cache()
        messages.success(self.request, _('Current revision changed for %s.') % self.attachment.original_filename)
        
        return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id)
    
    def get_context_data(self, **kwargs):
        kwargs['selected_tab'] = 'attachments'
        return ArticleMixin.get_context_data(self, **kwargs)

class AttachmentAddView(ArticleMixin, View):
    
    @method_decorator(get_article(can_write=True, not_locked=True))
    def dispatch(self, request, article, attachment_id, *args, **kwargs):
        self.attachment = get_object_or_404(models.Attachment.objects.active().can_write(request.user), id=attachment_id)
        return super(AttachmentAddView, self).dispatch(request, article, *args, **kwargs)
    
    def post(self, request, *args, **kwargs):
        if not self.attachment.articles.filter(id=self.article.id):
            self.attachment.articles.add(self.article)
            self.attachment.save()
            self.article.clear_cache()
            messages.success(self.request, _('Added a reference to "%(att)s" from "%(art)s".') %
                             {'att': self.attachment.original_filename,
                              'art': self.article.current_revision.title})
        else:
            messages.error(self.request, _('"%(att)s" is already referenced.') % {'att': self.attachment.original_filename})
        return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id)
    

class AttachmentDeleteView(ArticleMixin, FormView):
    
    form_class = forms.DeleteForm
    template_name="wiki/plugins/attachments/delete.html"
    
    @method_decorator(get_article(can_write=True, not_locked=True))
    def dispatch(self, request, article, attachment_id, *args, **kwargs):
        self.attachment = get_object_or_404(models.Attachment, id=attachment_id, articles=article)
        if not self.attachment.can_delete(request.user):
            return response_forbidden(request, article, kwargs.get('urlpath', None))
        return super(AttachmentDeleteView, self).dispatch(request, article, *args, **kwargs)
    
    def form_valid(self, form):
        
        if self.attachment.article == self.article:
            revision = models.AttachmentRevision()
            revision.attachment = self.attachment
            revision.set_from_request(self.request)
            revision.deleted = True
            revision.file = self.attachment.current_revision.file if self.attachment.current_revision else None
            revision.description = self.attachment.current_revision.description if self.attachment.current_revision else ""
            revision.save()
            self.attachment.current_revision = revision
            self.attachment.save()
            self.article.clear_cache()
            messages.info(self.request, _('The file %s was deleted.') % self.attachment.original_filename)
        else:
            self.attachment.articles.remove(self.article)
            messages.info(self.request, _('This article is no longer related to the file %s.') % self.attachment.original_filename)
        self.article.clear_cache()
        return redirect("wiki:get", path=self.urlpath.path, article_id=self.article.id)

    def get_context_data(self, **kwargs):
        kwargs['attachment'] = self.attachment
        kwargs['selected_tab'] = 'attachments'
        return super(AttachmentDeleteView, self).get_context_data(**kwargs)


class AttachmentSearchView(ArticleMixin, ListView):
    
    template_name="wiki/plugins/attachments/search.html"
    allow_empty = True
    context_object_name = 'attachments'
    paginate_by = 10
    
    @method_decorator(get_article(can_write=True))
    def dispatch(self, request, article, *args, **kwargs):
        return super(AttachmentSearchView, self).dispatch(request, article, *args, **kwargs)
    
    def get_queryset(self):
        self.query = self.request.GET.get('query', None)
        if not self.query:
            qs = models.Attachment.objects.none()
        else:
            qs = models.Attachment.objects.active().can_read(self.request.user)
            qs = qs.filter(Q(original_filename__contains=self.query) |
                           Q(current_revision__description__contains=self.query) |
                           Q(article__current_revision__title__contains=self.query))
        return qs
    
    def get_context_data(self, **kwargs):
        # Is this a bit of a hack? Use better inheritance?
        kwargs_article = ArticleMixin.get_context_data(self, **kwargs)
        kwargs_listview = ListView.get_context_data(self, **kwargs)
        kwargs['search_form'] = forms.SearchForm(self.request.GET)
        kwargs['query'] = self.query
        kwargs.update(kwargs_article)
        kwargs.update(kwargs_listview)
        kwargs['selected_tab'] = 'attachments'
        return kwargs
