from celery import shared_task
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import gettext as _
from moss import MOSS

from judge.models import Contest, ContestMoss, ContestParticipation, Submission
from judge.utils.celery import Progress

__all__ = ("rescore_contest", "run_moss")


@shared_task(bind=True)
def rescore_contest(self, contest_key):
    contest = Contest.objects.get(key=contest_key)
    participations = contest.users

    rescored = 0
    with Progress(
        self, participations.count(), stage=_("Recalculating contest scores")
    ) as p:
        for participation in participations.iterator():
            for contest_submission in participation.submissions.iterator():
                submission = contest_submission.submission
                contest_problem = contest_submission.problem
                contest_submission.points = round(
                    submission.case_points
                    / submission.case_total
                    * contest_problem.points
                    if submission.case_total > 0
                    else 0,
                    3,
                )
                if (
                    not contest_problem.partial
                    and contest_submission.points != contest_problem.points
                ):
                    contest_submission.points = 0
                contest_submission.save()
            participation.recompute_results()
            rescored += 1
            if rescored % 10 == 0:
                p.done = rescored
    return rescored


@shared_task(bind=True)
def run_moss(self, contest_key):
    moss_api_key = settings.MOSS_API_KEY
    if moss_api_key is None:
        raise ImproperlyConfigured("No MOSS API Key supplied")

    contest = Contest.objects.get(key=contest_key)
    ContestMoss.objects.filter(contest=contest).delete()

    length = len(ContestMoss.LANG_MAPPING) * contest.problems.count()
    moss_results = []

    with Progress(self, length, stage=_("Running MOSS")) as p:
        for problem in contest.problems.all():
            for dmoj_lang, moss_lang in ContestMoss.LANG_MAPPING:
                result = ContestMoss(
                    contest=contest, problem=problem, language=dmoj_lang
                )

                subs = (
                    Submission.objects.filter(
                        contest__participation__virtual__in=(
                            ContestParticipation.LIVE,
                            ContestParticipation.SPECTATE,
                        ),
                        contest_object=contest,
                        problem=problem,
                        language__common_name=dmoj_lang,
                    )
                    .order_by("-points")
                    .values_list("user__user__username", "source__source")
                )

                if subs.exists():
                    moss_call = MOSS(
                        moss_api_key,
                        language=moss_lang,
                        matching_file_limit=100,
                        comment="%s - %s" % (contest.key, problem.code),
                    )

                    users = set()

                    for username, source in subs:
                        if username in users:
                            continue
                        users.add(username)
                        moss_call.add_file_from_memory(username, source.encode("utf-8"))

                    result.url = moss_call.process()
                    result.submission_count = len(users)

                moss_results.append(result)
                p.did(1)

    ContestMoss.objects.bulk_create(moss_results)

    return len(moss_results)
