from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.http import HttpResponseRedirect, HttpResponseBadRequest, Http404
from django.shortcuts import render, get_object_or_404, redirect
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_protect, csrf_exempt

from mbed.comlibs import util
from mbed.comlibs.util import get_tag_list_text, add_flash, slugify, check_permissions, get_permissions
from mbed.repositories.models import Repository
from mbed.groups.models import MbedGroup

from wakawaka.forms import WikiPageForm, CloseCommentForm
from wakawaka.models import WikiPage, Revision, Wiki

import xml
import difflib
import logging

__all__ = ['index', 'newpage', 'page', 'edit', 'revisions', 'changes', 'revision_list', 'page_list']

mlog = logging.getLogger('wakawaka')


def _get_wiki(kwargs):
    """Looks up slug and returns a wiki """
    if type(kwargs) == dict:

        if 'wikislug' in kwargs:

            return get_object_or_404(Wiki, slug=kwargs['wikislug'])
        else:
            #test for repo relation
            if 'repo_slug' in kwargs:
                if 'group_slug' in kwargs:
                    wiki_slug = kwargs['group_slug']
                    repo = get_object_or_404(Repository, user=get_object_or_404(MbedGroup, slug=wiki_slug).user, slug=kwargs['repo_slug'])
                else:
                    wiki_slug = kwargs['username']
                    repo = get_object_or_404(Repository, user=get_object_or_404(User, username=wiki_slug), slug=kwargs['repo_slug'])

                return get_object_or_404(Wiki, object_id=repo.id, content_type=ContentType.objects.get_for_model(repo))
            elif 'group_slug' in kwargs:
                group_slug = kwargs['group_slug']
                team = get_object_or_404(MbedGroup, slug=group_slug)
                wiki_slug = 'team_%s' % team.id
                (wiki, created) = Wiki.objects.get_or_create(slug=wiki_slug,
                                                 object_id=team.id,
                                                 content_type=ContentType.objects.get_for_model(team))
                return wiki

    return get_object_or_404(Wiki, slug=wiki_slug)


def invalid_slug(request, slug, **kwargs):
    template_name = 'wakawaka/invalid_slug.html'
    slug = slug[:40]
    suggestion = slugify(slug, unique=False)
    context = {'wiki': _get_wiki(kwargs), 'slug': slug, 'suggestion': suggestion}
    return render(request, template_name, context)


@csrf_exempt
def preview(request, **kwargs):
    template_name = 'wakawaka/preview.html'
    title = request.POST.get('title', '')

    data = xml.sax.saxutils.unescape(request.POST.get('content', ''))
    context = {'content': data, 'title': title, 'wikislug': request.POST.get('wikislug', '')}
    return render(request, template_name, context)


@csrf_exempt
def newpage(request, **kwargs):
    check_permissions(request, _get_wiki(kwargs), ['read'])
    if request.method == "POST":
        return HttpResponseRedirect(request.POST.get('newpage', 'Sandbox'))
    return render(request, 'wakawaka/newpage.html', {'wiki': _get_wiki(kwargs)})


@csrf_protect
def page(request, slug, rev_id=None, template_name='wakawaka/page.html', extra_context=None, *args, **kwargs):
    '''
    Displays a wiki page. Redirects to the edit view if the page doesn't exist.
    '''
    check_permissions(request, _get_wiki(kwargs), ['read'])
    if extra_context is None:
        extra_context = {}

    action = request.GET.get('action', 'view')

    if not action:
        action = request.POST.get('action', 'view')

    if action == 'edit':
        rev_id = request.GET.get('revision')
        return edit(request, slug, *args, rev_id=rev_id, extra_context=extra_context, **kwargs)

    if action == 'history':
        return revisions(request, slug, extra_context=extra_context, **kwargs)

    if action == 'compare':
        return changes(request, slug, extra_context=extra_context, **kwargs)

    try:
        wiki = _get_wiki(kwargs)
        page = WikiPage.objects.filter(slug=slug, wiki=wiki)
        if not page.exists():
            if slug.endswith('/'):
                page = WikiPage.objects.filter(slug=slug.rstrip('/'), wiki=wiki)
                if not page.exists():
                    raise WikiPage.DoesNotExist
                else:
                    return HttpResponseRedirect(page[0].get_absolute_url())
            else:
                raise WikiPage.DoesNotExist
        page = page[0]

        rev_id = request.GET.get('revision')
        # if they have no read permission, they cant get any further
        check_permissions(request, page.wiki, ['read'])

        rev = page.current

        # Display an older revision if rev_id is givena

        if rev_id:
            rev_specific = get_object_or_404(Revision, pk=rev_id)
            if rev.pk != rev_specific.pk:
                rev_specific.is_not_current = True
            rev = rev_specific

    # The Page does not exist, redirect to the edit form or
    # deny, if the user has no permission to add pages
    except ValueError:
        logging.debug("ValueError")
        raise Http404
    except WikiPage.DoesNotExist:
        if request.user.is_authenticated():
            mlog.debug("Redirect to create page %s" % slug, extra=util.r2d(request))
            try:
                check_permissions(request, _get_wiki(kwargs), ['write'])
                return HttpResponseRedirect('?action=edit')
            except:
                # Crude creation of blank Homepage if it does not exist team wikis
                if (wiki.get_content_type() == 'mbed group'):
                    user = wiki.content_object.user
                    if slug == 'Homepage':
                        page = WikiPage.objects.create(slug=slug.rstrip('/'),
                                                       wiki=wiki,
                                                       allow_comments=wiki.allow_page_comments,
                                                       user=user)
                        (revision, revision_created) = Revision.objects.get_or_create(page=page, content='', message="Default content", user=user)
                        return HttpResponseRedirect(page.get_absolute_url())
                raise Http404
        raise Http404

    if request.method == 'POST':
        form = CloseCommentForm(request.POST, instance=page)
        if form.is_valid():
            form.save()
        return redirect(page)
    else:
        form = CloseCommentForm(instance=page)

    template_context = {
        'wikipage': page,
        'wiki': page.wiki,
        'permissions': get_permissions(request, page.wiki),
        'rev': rev,
    }
    template_context.update(extra_context)

    template_context['in_content_object'] = True
    return render(request, template_name, template_context)


def index(request, **kwargs):
    '''
    Redirects to the default wiki index name.
    '''
    check_permissions(request, _get_wiki(kwargs), ['read'])
    wiki = _get_wiki(kwargs)
    if (wiki.content_type == ContentType.objects.get_for_model(Repository)):
        index_slug = 'Special:Allpages'
    elif (wiki.content_type == ContentType.objects.get_for_model(MbedGroup)):
        index_slug = 'Special:Allpages'
    else:
        index_slug = getattr(settings, 'WAKAWAKA_DEFAULT_INDEX', 'Homepage')
    return HttpResponseRedirect(wiki.get_absolute_url() + index_slug)


@csrf_protect
def edit(request, slug, rev_id=None, template_name='wakawaka/edit.html', extra_context=None, *args, **kwargs):
    '''
    Displays the form for editing and deleting a page.
    '''
    check_permissions(request, _get_wiki(kwargs), ['read'])

    if extra_context is None:
        extra_context = {}
    # Get the page for slug and get a specific revision, if given
    try:
        page = WikiPage.objects.get(wiki=_get_wiki(kwargs), slug=slug)

        rev = page.current
        initial = {
            'content': page.current.content,
            'tags': get_tag_list_text(page.tags()),
            'editingrevision': page.current.id,
            'name': slug,
            'hidelog': page.hidelog
        }

        # Do not allow editing wiki pages if the user has no permission

        if rev_id:
            logging.debug("Editing earlier revision %s"%rev_id)
            # There is a specific revision, fetch this
            rev_specific = Revision.objects.get(pk=rev_id)
            if rev.pk != rev_specific.pk:
                rev = rev_specific
                rev.is_not_current = True
                initial = {'content': rev.content, 'message': _('Reverted to "%s"' % rev.message), 'tags':get_tag_list_text(page.tags()), 'name':slug}


    # This page does not exist, create a dummy page
    # Note that it's not saved here
    except WikiPage.DoesNotExist:
        check_permissions(request, _get_wiki(kwargs), ['write'])

        # Do not allow adding wiki pages if the user has no permission
        page = WikiPage(user=request.user, wiki=_get_wiki(kwargs), slug=slug)
        page.is_initial = True
        rev = None
        initial = {'message': _('Initial revision'), 'name':slug}
    except ValueError:
        raise Http404

    # Don't display the delete form if the user has nor permission
    delete_form = None
    # The user has permission, then do
    """
    #Don't allow deletes for anyone
    if request.user.has_perm('wakawaka.delete_wikipage') or \
       request.user.has_perm('wakawaka.delete_revision'):
        delete_form = DeleteWikiPageForm(request)
        if request.method == 'POST' and request.POST.get('delete'):
            delete_form = DeleteWikiPageForm(request, request.POST)
            if delete_form.is_valid():
                return delete_form.delete_wiki(request, page, rev)
    """
    # Page add/edit form
    try:
        existing_page = WikiPage.objects.get(slug=slug, wiki=_get_wiki(kwargs))
    except:
        existing_page = False
    form = WikiPageForm(None, request, initial=initial, wiki=_get_wiki(kwargs), page=existing_page)

    if request.method == 'POST':
        check_permissions(request, _get_wiki(kwargs), ['write'])
        form = WikiPageForm(request.POST, request, wiki=_get_wiki(kwargs), page=existing_page)

        if form.is_valid():
            # Save the form and redirect to the page view
            try:
                # Check that the page already exist
                page = WikiPage.objects.get(slug=slug, wiki=_get_wiki(kwargs))
            except WikiPage.DoesNotExist:
                # Must be a new one, create that page
                page = WikiPage.objects.create(wiki=_get_wiki(kwargs),user=request.user, slug=slug)

            form.save(request, page)

            add_flash(request, type='OK', title="Page saved.")
            #TODO auto follow
            return HttpResponseRedirect(page.wiki.get_absolute_url() + page.slug)

    template_context = {
        'form': form,
        'delete_form': delete_form,
        'page': page,
        'wiki': page.wiki,
        'rev': rev,
        'permissions': get_permissions(request, page.wiki),

    }
    template_context.update(extra_context)
    return render(request, template_name, template_context)


def revisions(request, slug, template_name='wakawaka/revisions.html', wiki=None,
                  extra_context=None, **kwargs):
    '''
    Displays the list of all revisions for a specific WikiPage
    '''
    page_num = int(request.GET.get('page', 1))
    check_permissions(request, _get_wiki(kwargs), ['read'])

    if extra_context is None:
        extra_context = {}
    wiki_page = get_object_or_404(WikiPage, slug=slug, wiki=_get_wiki(kwargs))
    paginator = Paginator(wiki_page.revisions.all(), 20)
    template_context = {
        'wiki_page': wiki_page,
        'wiki': wiki_page.wiki,
        'revisions': paginator.page(page_num),
        'page': page_num,
        'pages': paginator.num_pages,
        'per_page': 20
    }
    template_context.update(extra_context)
    return render(request, template_name, template_context)


def changes(request, slug, template_name='wakawaka/changes.html', extra_context=None, **kwargs):
    '''
    Displays the changes between two revisions.
    '''

    page_num = int(request.GET.get('page', 1))
    check_permissions(request, _get_wiki(kwargs), ['read'])

    if extra_context is None:
        extra_context = {}

    rev_a_id = request.GET.get('a', None)
    rev_b_id = request.GET.get('b', None)

    # Some stinky fingers manipulated the url
    if not rev_a_id or not rev_b_id:
        return HttpResponseBadRequest('Bad Request')

    try:
        rev_a = Revision.objects.get(pk=rev_a_id)
        rev_b = Revision.objects.get(pk=rev_b_id)
        wiki_page = WikiPage.objects.get(slug=slug, wiki=_get_wiki(kwargs))
    except ObjectDoesNotExist:
        raise Http404
    except ValueError:
        raise Http404

    if rev_a.content != rev_b.content:
        d = difflib.unified_diff(rev_b.content.splitlines(),
                                 rev_a.content.splitlines(),
                                 'Original', 'Current', lineterm='')
        difftext = '\n'.join(d)
    else:
        difftext = _(u'No changes were made between this two files.')

    paginator = Paginator(wiki_page.revisions.all(), 20)

    template_context = {
        'wiki_page': wiki_page,
        'wiki': wiki_page.wiki,
        'diff': difftext,
        'rev_a': rev_a,
        'rev_b': rev_b,
        'revisions': paginator.page(page_num),
        'page': page_num,
        'pages': paginator.num_pages,
        'per_page': 20
    }
    template_context.update(extra_context)
    return render(request, template_name, template_context)


# Some useful views
def revision_list(request, template_name='wakawaka/revision_list.html', extra_context=None, **kwargs):
    '''
    Displays a list of all recent revisions.
    '''

    check_permissions(request, _get_wiki(kwargs), ['read'])
    action = request.GET.get('action', 'view')

    if not action:
        action = request.POST.get('action', 'view')

    wiki = _get_wiki(kwargs)
    revision_list = Revision.objects.filter(page__wiki=wiki)
    paginator = Paginator(revision_list, 20)

    try:
        page = int(request.GET.get('page', '1'))
    except ValueError:
        page = 1
    try:
        revisions = paginator.page(page)

    except (EmptyPage, InvalidPage):
        revisions = paginator.page(paginator.num_pages)

    if extra_context is None:
        extra_context = {}

    template_context = {
        'wiki':wiki,
        'page_obj': revisions,
        'paginator': paginator,
        'request':request,
    }
    template_context.update(extra_context)
    return render(request, template_name, template_context)


def page_list(request, template_name='wakawaka/page_list.html', extra_context=None, *args, **kwargs):
    '''
    Displays all Pages
    '''
    wiki = _get_wiki(kwargs)
    check_permissions(request, wiki, ['read'])

    if request.GET.get('sort'):
        if request.GET['sort'] == 'date':
            wikipages_list = WikiPage.objects.order_by('-updated').filter(wiki=wiki)
        elif request.GET['sort'] == 'alpha':
            wikipages_list = WikiPage.objects.order_by('slug').filter(wiki=wiki)
        else:
            wikipages_list = WikiPage.objects.order_by('-score').filter(wiki=wiki)
    else:

        wikipages_list = WikiPage.objects.order_by('-score').filter(wiki=wiki)


    paginator = Paginator(wikipages_list, 50)

    try:
        page = int(request.GET.get('page', '1'))
    except ValueError:
        page = 1

    try:
        pages_page = paginator.page(page)

    except (EmptyPage, InvalidPage):
        pages_page = paginator.page(paginator.num_pages)

    if extra_context is None:
        extra_context = {}

    template_context = {
        'wiki':wiki,
        'page_obj': pages_page,
        'paginator': paginator,
        'request':request,
        'sort':request.GET.get('sort')
    }
    template_context.update(extra_context)

    return render(request, template_name, template_context)
