from operator import attrgetter

from django.db.models import Max
from django.http import JsonResponse

from judge.models import ContestParticipation, Problem, Profile, Submission
from judge.utils.ranker import ranker
from judge.views.contests import contest_ranking_list


def error(message):
    return JsonResponse({"error": message}, status=422)


def api_v2_user_info(request):
    """
    {
        "points": 100.0,
        "rating": 2452,
        "rank": "user",
        "organizations": [],
        "solved_problems": ["ccc14s4", ...],
        "attempted_problems": [
            {
                "code": "Hello, World!",
                "points": 1.0,
                "max_points": 2.0
            }
        ],
        "authored_problems": ["dmpg16s4"],
        "contest_history": [
            {
                "contest": {
                    "code": "halloween14",
                    "name": "Kemonomimi Party",
                    "tags": ["seasonal"],
                    "time_limit": null,
                    "start_time": "2014-10-31T04:00:00+00:00",
                    "end_time": "2014-11-10T05:00:00+00:00"
                },
                "rank": 1,
                "rating:": 1800
            },
            // ...
        ]
    }
    """
    try:
        username = request.GET["username"]
    except KeyError:
        return error("no username passed")
    if not username:
        return error("username argument not provided")
    try:
        profile = Profile.objects.get(user__username=username)
    except Profile.DoesNotExist:
        return error("no such user")

    last_rating = list(profile.ratings.order_by("-contest__end_time"))

    resp = {
        "rank": profile.display_rank,
        "organizations": list(profile.organizations.values_list("key", flat=True)),
    }

    contest_history = []
    for participation in ContestParticipation.objects.filter(
        user=profile, virtual=0, contest__is_visible=True
    ).order_by("-contest__end_time"):
        contest = participation.contest

        problems = list(
            contest.contest_problems.select_related("problem")
            .defer("problem__description")
            .order_by("order")
        )
        rank, result = next(
            filter(
                lambda data: data[1].user == profile.user,
                ranker(
                    contest_ranking_list(contest, problems),
                    key=attrgetter("points", "cumtime"),
                ),
            )
        )

        contest_history.append(
            {
                "contest": {
                    "code": contest.key,
                    "name": contest.name,
                    "tags": list(contest.tags.values_list("name", flat=True)),
                    "time_limit": contest.time_limit
                    and contest.time_limit.total_seconds(),
                    "start_time": contest.start_time.isoformat(),
                    "end_time": contest.end_time.isoformat(),
                },
                "rank": rank,
                "rating": result.participation_rating,
            }
        )

    resp["contests"] = {
        "current_rating": last_rating[0].rating if last_rating else None,
        "history": contest_history,
    }

    solved_problems = []
    attempted_problems = []

    problem_data = (
        Submission.objects.filter(
            points__gt=0,
            user=profile,
            problem__is_public=True,
            problem__is_organization_private=False,
        )
        .annotate(max_pts=Max("points"))
        .values_list("max_pts", "problem__points", "problem__code")
        .distinct()
    )
    for awarded_pts, max_pts, problem in problem_data:
        if awarded_pts == max_pts:
            solved_problems.append(problem)
        else:
            attempted_problems.append(
                {
                    "awarded": awarded_pts,
                    "max": max_pts,
                    "problem": problem,
                }
            )

    resp["problems"] = {
        "points": profile.points,
        "solved": solved_problems,
        "attempted": attempted_problems,
        "authored": list(
            Problem.objects.filter(
                is_public=True, is_organization_private=False, authors=profile
            ).values_list("code", flat=True)
        ),
    }

    return JsonResponse(resp)