from collections import OrderedDict
from datetime import datetime

from dateutil.relativedelta import relativedelta
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from intranet.femida.src.api.core.permissions import IsRecruiter
from intranet.femida.src.api.core.views import BaseView
from intranet.femida.src.api.stats import getters
from intranet.femida.src.core.shortcuts import get_object_or_40x
from intranet.femida.src.interviews.models import Application, Interview

User = get_user_model()


class StatsViewBase(BaseView):

    permission_classes = [IsAuthenticated, IsRecruiter]
    group_by_method_mapping = None

    def get_stats_method_kwargs(self):
        kwargs = {}
        username = self.request.query_params.get('user')
        month = self.request.query_params.get('month')

        if username:
            user = User.objects.get(username=username)
            kwargs['user'] = user

        if month:
            kwargs['date_from'] = datetime.strptime(month, '%Y-%m')
            kwargs['date_to'] = kwargs['date_from'] + relativedelta(months=1)

        return kwargs

    def get(self, request, *args, **kwargs):
        group_by = request.query_params.get('group_by')
        error_msg = 'Incorrect `group_by`. Available values: {}'.format(
            ', '.join(self.group_by_method_mapping.keys())
        )

        if group_by is None:
            return Response(
                data={'error': error_msg},
                status=status.HTTP_400_BAD_REQUEST,
            )

        stats_method = self.group_by_method_mapping.get(group_by)
        if stats_method is None:
            return Response(
                data={'error': error_msg},
                status=status.HTTP_400_BAD_REQUEST,
            )

        method_kwargs = self.get_stats_method_kwargs()
        stats = stats_method(**method_kwargs)

        try:
            total = sum(i['count'] for i in stats)
        except KeyError:
            total = None

        return Response({
            'details': stats,
            'total': total,
        })


class ApplicationsStatsView(StatsViewBase):

    group_by_method_mapping = {
        'month': getters.get_applications_by_month,
    }


class InterviewsStatsView(StatsViewBase):

    group_by_method_mapping = {
        'state': getters.get_interviews_by_state,
        'grade': getters.get_interviews_by_grade,
        'month': getters.get_interviews_by_month,
        'interviewer': getters.get_interviews_by_interviewer,
        'application': getters.get_interviews_by_application,
    }


class ProblemsStatsView(StatsViewBase):

    group_by_method_mapping = {
        'month': getters.get_problems_by_month,
    }


class AssignmentsStatsView(StatsViewBase):

    group_by_method_mapping = {
        'problem': getters.get_assignments_by_problem,
    }


def average(iterable):
    return sum(iterable) / float(len(iterable)) if iterable else 0


def get_user_averages(user):
    """
    Отдает два значения:
    Первое - средней грейд, который ставит user собеседуемым
    Второе - средний грейд всех кандидатов, в собеседовании которых
    участвовал user
    """
    application_ids = list(
        Application.objects
        .filter(interviews__interviewer_id=user.id, finished__isnull=False)
        .values_list('id', flat=True)
    )
    interview_tuples = (
        Interview.objects
        .filter(application_id__in=application_ids, grade__isnull=False)
        .values('interviewer_id', 'grade')
    )

    all_grades = [i['grade'] for i in interview_tuples]
    user_grades = [
        i['grade'] for i in interview_tuples if i['interviewer_id'] == user.id
    ]
    return average(user_grades), average(all_grades)


class UserStatsView(BaseView):

    permission_classes = [IsAuthenticated, IsRecruiter]

    def get(self, request, username, *args, **kwargs):
        user = get_object_or_40x(User, username=username)
        average_interview_grade, average_application_grade = (
            get_user_averages(user)
        )
        return Response(OrderedDict([
            ('avg_interview_grade', average_interview_grade),
            ('avg_application_grade', average_application_grade),
            ('coeff', average_interview_grade / average_application_grade),
        ]))
