import factory
import pytz

from django.utils import timezone

from scipost.constants import SCIPOST_SUBJECT_AREAS
from scipost.models import Contributor
from journals.constants import SCIPOST_JOURNALS_DOMAINS
from common.helpers import random_arxiv_identifier_without_version_number, random_scipost_journal

from .constants import STATUS_UNASSIGNED, STATUS_EIC_ASSIGNED, STATUS_RESUBMISSION_INCOMING,\
                       STATUS_PUBLISHED, SUBMISSION_TYPE, STATUS_RESUBMITTED, STATUS_VETTED,\
                       REFEREE_QUALIFICATION, RANKING_CHOICES, QUALITY_SPEC, REPORT_REC,\
                       REPORT_STATUSES, STATUS_UNVETTED, STATUS_DRAFT
from .models import Submission, Report, RefereeInvitation

from faker import Faker


class SubmissionFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Submission

    author_list = factory.Faker('name')
    submitted_by = factory.Iterator(Contributor.objects.all())
    submitted_to_journal = factory.Sequence(lambda n: random_scipost_journal())
    title = factory.lazy_attribute(lambda x: Faker().sentence())
    abstract = factory.lazy_attribute(lambda x: Faker().paragraph())
    arxiv_link = factory.Faker('uri')
    arxiv_identifier_wo_vn_nr = factory.Sequence(
                                    lambda n: random_arxiv_identifier_without_version_number())
    subject_area = factory.Iterator(SCIPOST_SUBJECT_AREAS[0][1], getter=lambda c: c[0])
    domain = factory.Iterator(SCIPOST_JOURNALS_DOMAINS, getter=lambda c: c[0])
    abstract = Faker().paragraph()
    author_comments = Faker().paragraph()
    remarks_for_editors = Faker().paragraph()
    submission_type = factory.Iterator(SUBMISSION_TYPE, getter=lambda c: c[0])
    is_current = True

    @factory.post_generation
    def fill_arxiv_fields(self, create, extracted, **kwargs):
        '''Fill empty arxiv fields.'''
        self.arxiv_link = 'https://arxiv.org/abs/%s' % self.arxiv_identifier_wo_vn_nr
        self.arxiv_identifier_w_vn_nr = '%sv1' % self.arxiv_identifier_wo_vn_nr
        self.arxiv_vn_nr = kwargs.get('arxiv_vn_nr', 1)

    @factory.post_generation
    def contributors(self, create, extracted, **kwargs):
        contributors = list(Contributor.objects.order_by('?')[:4])

        # Auto-add the submitter as an author
        self.submitted_by = contributors.pop()

        if not create:
            return
        self.authors.add(self.submitted_by)

        # Add three random authors
        for contrib in contributors:
            self.authors.add(contrib)
            self.author_list += ', %s %s' % (contrib.user.first_name, contrib.user.last_name)

    @factory.post_generation
    def dates(self, create, extracted, **kwargs):
        timezone.now()
        if kwargs.get('submission', False):
            self.submission_date = kwargs['submission']
            self.cycle.update_deadline()
            return
        self.submission_date = Faker().date_time_between(start_date="-3y", end_date="now",
                                                         tzinfo=pytz.UTC).date()
        self.latest_activity = Faker().date_time_between(start_date=self.submission_date,
                                                         end_date="now", tzinfo=pytz.UTC)
        self.cycle.update_deadline()


class UnassignedSubmissionFactory(SubmissionFactory):
    '''This Submission is a 'new request' by a Contributor for its Submission.'''
    status = STATUS_UNASSIGNED


class EICassignedSubmissionFactory(SubmissionFactory):
    status = STATUS_EIC_ASSIGNED
    open_for_commenting = True
    open_for_reporting = True

    @factory.post_generation
    def eic(self, create, extracted, **kwargs):
        '''Assign an EIC to submission.'''
        author_ids = list(self.authors.values_list('id', flat=True))
        self.editor_in_charge = (Contributor.objects.order_by('?')
                                            .exclude(pk=self.submitted_by.pk)
                                            .exclude(pk__in=author_ids).first())


class ResubmittedSubmissionFactory(SubmissionFactory):
    '''This Submission is a `resubmitted` version.'''
    status = STATUS_RESUBMITTED
    open_for_commenting = False
    open_for_reporting = False
    is_current = False
    is_resubmission = False


class ResubmissionFactory(SubmissionFactory):
    """
    This Submission is a newer version of a Submission which is
    already known by the SciPost database.
    """
    status = STATUS_RESUBMISSION_INCOMING
    open_for_commenting = True
    open_for_reporting = True
    is_resubmission = True

    @factory.post_generation
    def fill_arxiv_fields(self, create, extracted, **kwargs):
        '''Fill empty arxiv fields.'''
        self.arxiv_link = 'https://arxiv.org/abs/%s' % self.arxiv_identifier_wo_vn_nr
        self.arxiv_identifier_w_vn_nr = '%sv2' % self.arxiv_identifier_wo_vn_nr
        self.arxiv_vn_nr = 2

    @factory.post_generation
    def eic(self, create, extracted, **kwargs):
        '''Assign an EIC to submission.'''
        author_ids = list(self.authors.values_list('id', flat=True))
        self.editor_in_charge = (Contributor.objects.order_by('?')
                                            .exclude(pk=self.submitted_by.pk)
                                            .exclude(pk__in=author_ids).first())

    @factory.post_generation
    def dates(self, create, extracted, **kwargs):
        """Overwrite the parent `dates` method to skip the update_deadline call."""
        timezone.now()
        if kwargs.get('submission', False):
            self.submission_date = kwargs['submission']
            return
        self.submission_date = Faker().date_time_between(start_date="-3y", end_date="now",
                                                         tzinfo=pytz.UTC).date()
        self.latest_activity = Faker().date_time_between(start_date=self.submission_date,
                                                         end_date="now", tzinfo=pytz.UTC)


class PublishedSubmissionFactory(SubmissionFactory):
    status = STATUS_PUBLISHED
    open_for_commenting = False
    open_for_reporting = False


class ReportFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Report

    status = factory.Iterator(REPORT_STATUSES, getter=lambda c: c[0])
    submission = factory.Iterator(Submission.objects.all())
    date_submitted = Faker().date_time_between(start_date="-3y", end_date="now", tzinfo=pytz.UTC)
    vetted_by = factory.Iterator(Contributor.objects.all())
    author = factory.Iterator(Contributor.objects.all())
    qualification = factory.Iterator(REFEREE_QUALIFICATION, getter=lambda c: c[0])
    strengths = Faker().paragraph()
    weaknesses = Faker().paragraph()
    report = Faker().paragraph()
    requested_changes = Faker().paragraph()
    validity = factory.Iterator(RANKING_CHOICES, getter=lambda c: c[0])
    significance = factory.Iterator(RANKING_CHOICES, getter=lambda c: c[0])
    originality = factory.Iterator(RANKING_CHOICES, getter=lambda c: c[0])
    clarity = factory.Iterator(RANKING_CHOICES, getter=lambda c: c[0])
    formatting = factory.Iterator(QUALITY_SPEC, getter=lambda c: c[0])
    grammar = factory.Iterator(QUALITY_SPEC, getter=lambda c: c[0])
    recommendation = factory.Iterator(REPORT_REC, getter=lambda c: c[0])
    remarks_for_editors = Faker().paragraph()


class DraftReportFactory(ReportFactory):
    status = STATUS_DRAFT
    vetted_by = None


class UnVettedReportFactory(ReportFactory):
    status = STATUS_UNVETTED
    vetted_by = None


class VettedReportFactory(ReportFactory):
    status = STATUS_VETTED


class RefereeInvitationFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = RefereeInvitation

    submission = factory.SubFactory('submissions.factories.SubmissionFactory')
    referee = factory.Iterator(Contributor.objects.all())

    invitation_key = factory.Faker('md5')
    invited_by = factory.Iterator(Contributor.objects.all())

    @factory.post_generation
    def contributor_fields(self, create, extracted, **kwargs):
        self.title = self.referee.title
        self.first_name = self.referee.user.first_name
        self.last_name = self.referee.user.last_name
        self.email_address = self.referee.user.email


class AcceptedRefereeInvitationFactory(RefereeInvitationFactory):
    accepted = True
    date_responded = Faker().date_time_between(start_date="-1y", end_date="now", tzinfo=pytz.UTC)