import datetime
import feedparser
import re
import requests

from django.db.models import Q
from django.utils import timezone
from django.shortcuts import get_object_or_404, render
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.models import User
from django.core.mail import EmailMessage
from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect
from django.views.decorators.csrf import csrf_protect
from django.db.models import Avg

from .models import Commentary
from .forms import RequestCommentaryForm, DOIToQueryForm, IdentifierToQueryForm
from .forms import VetCommentaryForm, CommentarySearchForm, commentary_refusal_dict

from comments.models import Comment
from comments.forms import CommentForm
from scipost.models import Contributor
from scipost.models import title_dict
from scipost.forms import AuthenticationForm


################
# Commentaries
################


@login_required
@permission_required('scipost.can_request_commentary_pages', raise_exception=True)
def request_commentary(request):
    if request.method == 'POST':
        form = RequestCommentaryForm(request.POST)
        if form.is_valid():
            errormessage = ''
            existing_commentary = None
            if not form.cleaned_data['arxiv_identifier'] and not form.cleaned_data['pub_DOI']:
                errormessage = ('You must provide either a DOI (for a published paper) '
                                'or an arXiv identifier (for a preprint).')
            elif (form.cleaned_data['arxiv_identifier'] and
                  (Commentary.objects
                   .filter(arxiv_identifier=form.cleaned_data['arxiv_identifier']).exists())):
                errormessage = 'There already exists a Commentary Page on this preprint, see'
                existing_commentary = get_object_or_404(
                    Commentary,
                    arxiv_identifier=form.cleaned_data['arxiv_identifier'])
            elif (form.cleaned_data['pub_DOI'] and
                  Commentary.objects.filter(pub_DOI=form.cleaned_data['pub_DOI']).exists()):
                errormessage = 'There already exists a Commentary Page on this publication, see'
                existing_commentary = get_object_or_404(Commentary, pub_DOI=form.cleaned_data['pub_DOI'])
            if errormessage:
                doiform = DOIToQueryForm()
                identifierform = IdentifierToQueryForm()
                context = {'form': form, 'doiform': doiform, 'identifierform': identifierform,
                           'errormessage': errormessage,
                           'existing_commentary': existing_commentary}
                return render(request, 'commentaries/request_commentary.html', context)

            # Otherwise we can create the Commentary
            contributor = Contributor.objects.get(user=request.user)
            commentary = Commentary (
                requested_by = contributor,
                type = form.cleaned_data['type'],
                discipline = form.cleaned_data['discipline'],
                domain = form.cleaned_data['domain'],
                subject_area = form.cleaned_data['subject_area'],
                pub_title = form.cleaned_data['pub_title'],
                arxiv_identifier = form.cleaned_data['arxiv_identifier'],
                pub_DOI = form.cleaned_data['pub_DOI'],
                metadata = form.cleaned_data['metadata'],
                author_list = form.cleaned_data['author_list'],
                journal = form.cleaned_data['journal'],
                volume = form.cleaned_data['volume'],
                pages = form.cleaned_data['pages'],
                pub_date = form.cleaned_data['pub_date'],
                pub_abstract = form.cleaned_data['pub_abstract'],
                latest_activity = timezone.now(),
                )
            commentary.parse_links_into_urls()
            commentary.save()

            context = {'ack_header': 'Thank you for your request for a Commentary Page',
                       'ack_message': 'Your request will soon be handled by an Editor. ',
                       'followup_message': 'Return to your ',
                       'followup_link': reverse('scipost:personal_page'),
                       'followup_link_label': 'personal page'}
            return render(request, 'scipost/acknowledgement.html', context)
    else:
        form = RequestCommentaryForm()
    doiform = DOIToQueryForm()
    identifierform = IdentifierToQueryForm()
    context = {'form': form, 'doiform': doiform, 'identifierform': identifierform}
    return render(request, 'commentaries/request_commentary.html', context)

@permission_required('scipost.can_request_commentary_pages', raise_exception=True)
def prefill_using_DOI(request):
    """ Probes CrossRef API with the DOI, to pre-fill the form. """
    if request.method == "POST":
        doiform = DOIToQueryForm(request.POST)
        if doiform.is_valid():
            # Check if given doi is of expected form:
            doipattern = re.compile("^10.[0-9]{4,9}/[-._;()/:a-zA-Z0-9]+")
            errormessage = ''
            existing_commentary = None
            if not doipattern.match(doiform.cleaned_data['doi']):
                errormessage = 'The DOI you entered is improperly formatted.'
            elif Commentary.objects.filter(pub_DOI=doiform.cleaned_data['doi']).exists():
                errormessage = 'There already exists a Commentary Page on this publication, see'
                existing_commentary = get_object_or_404(Commentary, pub_DOI=doiform.cleaned_data['doi'])
            if errormessage:
                form = RequestCommentaryForm()
                identifierform = IdentifierToQueryForm()
                context = {'form': form, 'doiform': doiform, 'identifierform': identifierform,
                           'errormessage': errormessage,
                           'existing_commentary': existing_commentary}
                return render(request, 'commentaries/request_commentary.html', context)

            # Otherwise we query Crossref for the information:
            try:
                queryurl = 'http://api.crossref.org/works/%s' % doiform.cleaned_data['doi']
                doiquery = requests.get(queryurl)
                doiqueryJSON = doiquery.json()
                metadata = doiqueryJSON
                pub_title = doiqueryJSON['message']['title'][0]
                authorlist = (doiqueryJSON['message']['author'][0]['given'] + ' ' +
                              doiqueryJSON['message']['author'][0]['family'])
                for author in doiqueryJSON['message']['author'][1:]:
                    authorlist += ', ' + author['given'] + ' ' + author['family']
                journal = doiqueryJSON['message']['container-title'][0]

                try:
                    volume = doiqueryJSON['message']['volume']
                except KeyError:
                    volume = ''

                pages = ''
                try:
                    pages = doiqueryJSON['message']['article-number'] # for Phys Rev
                except KeyError:
                    pass
                try:
                    pages = doiqueryJSON['message']['page']
                except KeyError:
                    pass

                pub_date = ''
                try:
                    pub_date = (str(doiqueryJSON['message']['issued']['date-parts'][0][0]) + '-' +
                                str(doiqueryJSON['message']['issued']['date-parts'][0][1]))
                    try:
                        pub_date += '-' + str(doiqueryJSON['message']['issued']['date-parts'][0][2])
                    except (IndexError, KeyError):
                        pass
                except (IndexError, KeyError):
                    pass
                pub_DOI = doiform.cleaned_data['doi']
                form = RequestCommentaryForm(
                    initial={'type': 'published', 'metadata': metadata,
                             'pub_title': pub_title, 'author_list': authorlist,
                             'journal': journal, 'volume': volume,
                             'pages': pages, 'pub_date': pub_date,
                             'pub_DOI': pub_DOI})
                identifierform = IdentifierToQueryForm()
                context = {'form': form, 'doiform': doiform, 'identifierform': identifierform,}
                context['title'] = pub_title
                return render(request, 'commentaries/request_commentary.html', context)
            except (IndexError, KeyError, ValueError):
                pass
        else:
            pass
    return redirect(reverse('commentaries:request_commentary'))

@permission_required('scipost.can_request_commentary_pages', raise_exception=True)
def prefill_using_identifier(request):
    """ Probes arXiv with the identifier, to pre-fill the form. """
    if request.method == "POST":
        identifierform = IdentifierToQueryForm(request.POST)
        if identifierform.is_valid():
            # Check if given identifier is of expected form:
            # we allow 1 or 2 digits for version
            identifierpattern_new = re.compile("^[0-9]{4,}.[0-9]{4,5}v[0-9]{1,2}$")
            identifierpattern_old = re.compile("^[-.a-z]+/[0-9]{7,}v[0-9]{1,2}$")
            errormessage = ''
            existing_commentary = None
            if not (identifierpattern_new.match(identifierform.cleaned_data['identifier']) or
                    identifierpattern_old.match(identifierform.cleaned_data['identifier'])):
                errormessage = ('The identifier you entered is improperly formatted '
                                '(did you forget the version number?).')
            elif (Commentary.objects
                  .filter(arxiv_identifier=identifierform.cleaned_data['identifier']).exists()):
                errormessage = 'There already exists a Commentary Page on this preprint, see'
                existing_commentary = get_object_or_404(
                    Commentary, arxiv_identifier=identifierform.cleaned_data['identifier'])
            if errormessage:
                form = RequestCommentaryForm()
                doiform = DOIToQueryForm()
                context = {'form': form, 'doiform': doiform, 'identifierform': identifierform,
                           'errormessage': errormessage,
                           'existing_commentary': existing_commentary}
                return render(request, 'commentaries/request_commentary.html', context)
            # Otherwise we query arXiv for the information:
            try:
                queryurl = ('http://export.arxiv.org/api/query?id_list=%s'
                            % identifierform.cleaned_data['identifier'])
                arxivquery = feedparser.parse(queryurl)

                # If paper has been published, should comment on published version
                try:
                    arxiv_journal_ref = arxivquery['entries'][0]['arxiv_journal_ref']
                    errormessage = ('This paper has been published as ' + arxiv_journal_ref
                                    + '. Please comment on the published version.')
                except (IndexError, KeyError):
                    pass
                try:
                    arxiv_doi = arxivquery['entries'][0]['arxiv_doi']
                    errormessage = ('This paper has been published under DOI ' + arxiv_DOI
                                    + '. Please comment on the published version.')
                except (IndexError, KeyError):
                    pass

                if errormessage:
                    form = RequestCommentaryForm()
                    doiform = DOIToQueryForm()
                    context = {'form': form, 'doiform': doiform, 'identifierform': identifierform,
                               'errormessage': errormessage,
                               'existing_commentary': existing_commentary}
                    return render(request, 'commentaries/request_commentary.html', context)

                # otherwise prefill the form:
                metadata = arxivquery
                pub_title = arxivquery['entries'][0]['title']
                authorlist = arxivquery['entries'][0]['authors'][0]['name']
                for author in arxivquery['entries'][0]['authors'][1:]:
                    authorlist += ', ' + author['name']
                arxiv_link = arxivquery['entries'][0]['id']
                abstract = arxivquery['entries'][0]['summary']
                form = RequestCommentaryForm(
                    initial={'type': 'preprint', 'metadata': metadata,
                             'pub_title': pub_title, 'author_list': authorlist,
                             'arxiv_identifier': identifierform.cleaned_data['identifier'],
                             'arxiv_link': arxiv_link, 'pub_abstract': abstract})
                doiform = DOIToQueryForm()
                context = {'form': form, 'doiform': doiform, 'identifierform': identifierform}
                context['title'] = pub_title
                return render(request, 'commentaries/request_commentary.html', context)
            except (IndexError, KeyError, ValueError): # something went wrong with processing the arXiv data
                errormessage = 'An error occurred while processing the arXiv data. Are you sure this identifier exists?'
                form = RequestCommentaryForm()
                doiform = DOIToQueryForm()
                context = {'form': form, 'doiform': doiform, 'identifierform': identifierform,
                           'errormessage': errormessage,
                           'existing_commentary': existing_commentary}
                return render(request, 'commentaries/request_commentary.html', context)
        else:
            pass
    return redirect(reverse('commentaries:request_commentary'))


@permission_required('scipost.can_vet_commentary_requests', raise_exception=True)
def vet_commentary_requests(request):
    contributor = Contributor.objects.get(user=request.user)
    commentary_to_vet = Commentary.objects.filter(vetted=False).first() # only handle one at a time
    form = VetCommentaryForm()
    context = {'contributor': contributor, 'commentary_to_vet': commentary_to_vet, 'form': form }
    return render(request, 'commentaries/vet_commentary_requests.html', context)

@permission_required('scipost.can_vet_commentary_requests', raise_exception=True)
def vet_commentary_request_ack(request, commentary_id):
    if request.method == 'POST':
        form = VetCommentaryForm(request.POST)
        commentary = Commentary.objects.get(pk=commentary_id)
        if form.is_valid():
            if form.cleaned_data['action_option'] == '1':
                # accept the commentary as is
                commentary.vetted = True
                commentary.vetted_by = Contributor.objects.get(user=request.user)
                commentary.latest_activity = timezone.now()
                commentary.save()
                email_text = ('Dear ' + title_dict[commentary.requested_by.title] + ' '
                              + commentary.requested_by.user.last_name
                              + ', \n\nThe Commentary Page you have requested, '
                              'concerning publication with title '
                              + commentary.pub_title + ' by ' + commentary.author_list
                              + ', has been activated at https://scipost.org/commentary/'
                              + str(commentary.arxiv_or_DOI_string)
                              + '. You are now welcome to submit your comments.'
                              '\n\nThank you for your contribution, \nThe SciPost Team.')
                emailmessage = EmailMessage('SciPost Commentary Page activated', email_text,
                                            'SciPost commentaries <commentaries@scipost.org>',
                                            [commentary.requested_by.user.email],
                                            ['commentaries@scipost.org'],
                                            reply_to=['commentaries@scipost.org'])
                emailmessage.send(fail_silently=False)
            elif form.cleaned_data['action_option'] == '0':
                # re-edit the form starting from the data provided
                form2 = RequestCommentaryForm(initial={'pub_title': commentary.pub_title,
                                                       'arxiv_link': commentary.arxiv_link,
                                                       'pub_DOI_link': commentary.pub_DOI_link,
                                                       'author_list': commentary.author_list,
                                                       'pub_date': commentary.pub_date,
                                                       'pub_abstract': commentary.pub_abstract})
                commentary.delete()
                email_text = ('Dear ' + title_dict[commentary.requested_by.title] + ' '
                              + commentary.requested_by.user.last_name
                              + ', \n\nThe Commentary Page you have requested, '
                              'concerning publication with title ' + commentary.pub_title
                              + ' by ' + commentary.author_list
                              + ', has been activated (with slight modifications to your submitted details).'
                              ' You are now welcome to submit your comments.'
                              '\n\nThank you for your contribution, \nThe SciPost Team.')
                emailmessage = EmailMessage('SciPost Commentary Page activated', email_text,
                                            'SciPost commentaries <commentaries@scipost.org>',
                                            [commentary.requested_by.user.email],
                                            ['commentaries@scipost.org'],
                                            reply_to=['commentaries@scipost.org'])
                emailmessage.send(fail_silently=False)
                context = {'form': form2 }
                return render(request, 'commentaries/request_commentary.html', context)
            elif form.cleaned_data['action_option'] == '2':
                # the commentary request is simply rejected
                email_text = ('Dear ' + title_dict[commentary.requested_by.title] + ' '
                              + commentary.requested_by.user.last_name
                              + ', \n\nThe Commentary Page you have requested, '
                              'concerning publication with title '
                              + commentary.pub_title + ' by ' + commentary.author_list
                              + ', has not been activated for the following reason: '
                              + commentary_refusal_dict[int(form.cleaned_data['refusal_reason'])]
                              + '.\n\nThank you for your interest, \nThe SciPost Team.')
                if form.cleaned_data['email_response_field']:
                    email_text += '\n\nFurther explanations: ' + form.cleaned_data['email_response_field']
                emailmessage = EmailMessage('SciPost Commentary Page not activated', email_text,
                                            'SciPost commentaries <commentaries@scipost.org>',
                                            [commentary.requested_by.user.email],
                                            ['commentaries@scipost.org'],
                                            reply_to=['comentaries@scipost.org'])
                emailmessage.send(fail_silently=False)
                commentary.delete()

    context = {'ack_header': 'SciPost Commentary request vetted.',
               'followup_message': 'Return to the ',
               'followup_link': reverse('commentaries:vet_commentary_requests'),
               'followup_link_label': 'Commentary requests page'}
    return render(request, 'scipost/acknowledgement.html', context)


def commentaries(request):
    if request.method == 'POST':
        form = CommentarySearchForm(request.POST)
        if form.is_valid() and form.has_changed():
            commentary_search_list = Commentary.objects.filter(
                pub_title__icontains=form.cleaned_data['pub_title_keyword'],
                author_list__icontains=form.cleaned_data['pub_author'],
                pub_abstract__icontains=form.cleaned_data['pub_abstract_keyword'],
                vetted=True,
                )
            commentary_search_list.order_by('-pub_date')
        else:
            commentary_search_list = []

    else:
        form = CommentarySearchForm()
        commentary_search_list = []

    comment_recent_list = (Comment.objects.filter(status='1')
                           .order_by('-date_submitted')[:10])

    commentary_recent_list = (Commentary.objects.filter(vetted=True)
                              .order_by('-latest_activity')[:10])
    context = {'form': form, 'commentary_search_list': commentary_search_list,
               'comment_recent_list': comment_recent_list,
               'commentary_recent_list': commentary_recent_list }
    return render(request, 'commentaries/commentaries.html', context)


def browse(request, discipline, nrweeksback):
    if request.method == 'POST':
        form = CommentarySearchForm(request.POST)
        if form.is_valid() and form.has_changed():
            commentary_search_list = Commentary.objects.filter(
                pub_title__icontains=form.cleaned_data['pub_title_keyword'],
                author_list__icontains=form.cleaned_data['pub_author'],
                pub_abstract__icontains=form.cleaned_data['pub_abstract_keyword'],
                vetted=True,
                )
            commentary_search_list.order_by('-pub_date')
        else:
            commentary_search_list = []
        context = {'form': form, 'commentary_search_list': commentary_search_list}
        return HttpResponseRedirect(request, 'commentaries/commentaries.html', context)
    else:
        form = CommentarySearchForm()
    commentary_browse_list = Commentary.objects.filter(
        vetted=True, discipline=discipline,
        latest_activity__gte=timezone.now() + datetime.timedelta(weeks=-int(nrweeksback))
        )
    context = {'form': form, 'discipline': discipline, 'nrweeksback': nrweeksback,
               'commentary_browse_list': commentary_browse_list }
    return render(request, 'commentaries/commentaries.html', context)


def commentary_detail(request, arxiv_or_DOI_string):
    commentary = get_object_or_404(Commentary, arxiv_or_DOI_string=arxiv_or_DOI_string)
    comments = commentary.comment_set.all()
    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            author = Contributor.objects.get(user=request.user)
            newcomment = Comment(commentary=commentary, author=author,
                is_rem=form.cleaned_data['is_rem'],
                is_que=form.cleaned_data['is_que'],
                is_ans=form.cleaned_data['is_ans'],
                is_obj=form.cleaned_data['is_obj'],
                is_rep=form.cleaned_data['is_rep'],
                is_val=form.cleaned_data['is_val'],
                is_lit=form.cleaned_data['is_lit'],
                is_sug=form.cleaned_data['is_sug'],
                comment_text=form.cleaned_data['comment_text'],
                remarks_for_editors=form.cleaned_data['remarks_for_editors'],
                date_submitted=timezone.now(),
                )
            newcomment.save()
            author.nr_comments = Comment.objects.filter(author=author).count()
            author.save()
            context = {'ack_header': 'Thank you for contributing a Comment.',
                       'ack_message': 'It will soon be vetted by an Editor.',
                       'followup_message': 'Back to the ',
                       'followup_link': reverse(
                           'commentaries:commentary',
                           kwargs={'arxiv_or_DOI_string': newcomment.commentary.arxiv_or_DOI_string}
                       ),
                       'followup_link_label': ' Commentary page you came from'
                   }
            return render(request, 'scipost/acknowledgement.html', context)
    else:
        form = CommentForm()
    try:
        author_replies = Comment.objects.filter(commentary=commentary,
                                                is_author_reply=True,
                                                status__gte=1)
    except Comment.DoesNotExist:
        author_replies = ()
    context = {'commentary': commentary,
               'comments': comments.filter(status__gte=1).order_by('-date_submitted'),
               'author_replies': author_replies, 'form': form}
    return render(request, 'commentaries/commentary_detail.html', context)