import structlog
from django.shortcuts import get_object_or_404
from rest_framework.decorators import api_view, permission_classes
from rest_framework.exceptions import PermissionDenied
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from vbv_lernwelt.core.models import User
from vbv_lernwelt.core.serializers import UserSerializer
from vbv_lernwelt.course.models import CourseCompletion, CourseSession
from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learnpath.models import (
    Circle,
    LearningUnit,
    LearningUnitPerformanceFeedbackType,
)
from vbv_lernwelt.notify.services import NotificationService
from vbv_lernwelt.self_evaluation_feedback.models import (
    CourseCompletionFeedback,
    SelfEvaluationFeedback,
)
from vbv_lernwelt.self_evaluation_feedback.serializers import (
    SelfEvaluationFeedbackSerializer,
)
from vbv_lernwelt.self_evaluation_feedback.utils import (
    AssessmentCounts,
    get_self_assessment_counts,
    get_self_evaluation_feedback_counts,
)

logger = structlog.get_logger(__name__)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def start_self_evaluation_feedback(request, learning_unit_id):
    feedback_provider_user_id = request.data.get("feedback_provider_user_id")

    learning_unit = get_object_or_404(LearningUnit, id=learning_unit_id)
    feedback_provider_user = get_object_or_404(User, id=feedback_provider_user_id)

    if not LearningMentor.objects.filter(
        course=learning_unit.get_course(),
        mentor=feedback_provider_user,
        participants__user=request.user,
    ).exists():
        raise PermissionDenied()

    # calling start multiple times shall be a no-op
    feedback, created = SelfEvaluationFeedback.objects.get_or_create(
        feedback_requester_user=request.user,
        feedback_provider_user=feedback_provider_user,
        learning_unit=learning_unit,
    )

    if created:
        NotificationService.send_self_evaluation_feedback_request_feedback_notification(
            self_evaluation_feedback=feedback
        )

    return Response({"success": True})


@api_view(["PUT"])
@permission_classes([IsAuthenticated])
def release_provider_self_evaluation_feedback(request, feedback_id):
    feedback = get_object_or_404(
        SelfEvaluationFeedback, id=feedback_id, feedback_provider_user=request.user
    )

    if feedback.feedback_submitted:
        return Response({"success": True})

    feedback.feedback_submitted = True
    feedback.save()

    NotificationService.send_self_evaluation_feedback_received_notification(
        self_evaluation_feedback=feedback
    )

    return Response({"success": True})


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def get_self_evaluation_feedback_as_provider(request, learning_unit_id):
    feedback = get_object_or_404(
        SelfEvaluationFeedback,
        learning_unit_id=learning_unit_id,
        feedback_provider_user=request.user,
    )

    return Response(SelfEvaluationFeedbackSerializer(feedback).data)


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def get_self_evaluation_feedbacks_as_requester(request, course_session_id: int):
    course_session = get_object_or_404(CourseSession, id=course_session_id)

    results = []
    circle_ids = set()

    all_self_assessment_counts = []
    all_feedback_assessment_counts = []

    for learning_unit in LearningUnit.objects.filter(
        course_category__course=course_session.course,
    ):
        # this is not a problem in real life, but in the test environment
        # we have a lot of learning units without self assessment criteria
        # -> just skip those learning units
        if len(learning_unit.performancecriteria_set.all()) == 0:
            continue

        circle = learning_unit.get_parent().specific
        circle_ids.add(circle.id)

        feedback = SelfEvaluationFeedback.objects.filter(
            learning_unit=learning_unit,
            feedback_requester_user=request.user,
        ).first()

        if not feedback:
            # no feedback given yet
            feedback_assessment = None
        else:
            # feedback given
            feedback_counts = get_self_evaluation_feedback_counts(feedback)
            all_feedback_assessment_counts.append(feedback_counts)

            feedback_assessment = {
                "submitted_by_provider": feedback.feedback_submitted,
                "provider_user": UserSerializer(feedback.feedback_provider_user).data,
                "counts": {
                    "pass": feedback_counts.pass_count,
                    "fail": feedback_counts.fail_count,
                    "unknown": feedback_counts.unknown_count,
                },
            }

        self_assessment_counts = get_self_assessment_counts(learning_unit, request.user)
        all_self_assessment_counts.append(self_assessment_counts)

        results.append(
            {
                "id": learning_unit.id,
                "title": learning_unit.title,
                "detail_url": learning_unit.get_evaluate_url(),
                "circle_id": circle.id,
                "circle_title": circle.title,
                "feedback_assessment": feedback_assessment,
                "self_assessment": {
                    "counts": {
                        "pass": self_assessment_counts.pass_count,
                        "fail": self_assessment_counts.fail_count,
                        "unknown": self_assessment_counts.unknown_count,
                    }
                },
            }
        )

    self_assessment_counts_aggregate = AssessmentCounts(
        pass_count=sum(x.pass_count for x in all_self_assessment_counts),
        fail_count=sum(x.fail_count for x in all_self_assessment_counts),
        unknown_count=sum(x.unknown_count for x in all_self_assessment_counts),
    )
    received_feedback_counts_aggregate = AssessmentCounts(
        pass_count=sum(x.pass_count for x in all_feedback_assessment_counts),
        fail_count=sum(x.fail_count for x in all_feedback_assessment_counts),
        unknown_count=sum(x.unknown_count for x in all_feedback_assessment_counts),
    )

    # pad the feedback counts with unknowns for the
    # learning units where we have no feedback yet
    feedback_assessment_counts_aggregate = AssessmentCounts(
        pass_count=received_feedback_counts_aggregate.pass_count,
        fail_count=received_feedback_counts_aggregate.fail_count,
        unknown_count=self_assessment_counts_aggregate.total_count
        - received_feedback_counts_aggregate.total_count
        + received_feedback_counts_aggregate.unknown_count,
    )

    # check if there are any learning units with mentor feedback
    feedback_assessment_visible = (
        LearningUnit.objects.filter(
            feedback_user=LearningUnitPerformanceFeedbackType.MENTOR_FEEDBACK.value,
            course_category__course=course_session.course,
        ).count()
        > 0
    )

    return Response(
        {
            "results": results,
            "circles": list(
                Circle.objects.filter(id__in=circle_ids).values("id", "title")
            ),
            "aggregates": {
                "feedback_assessment_visible": feedback_assessment_visible,
                "feedback_assessment": {
                    "pass": feedback_assessment_counts_aggregate.pass_count,
                    "fail": feedback_assessment_counts_aggregate.fail_count,
                    "unknown": feedback_assessment_counts_aggregate.unknown_count,
                },
                "self_assessment": {
                    "pass": self_assessment_counts_aggregate.pass_count,
                    "fail": self_assessment_counts_aggregate.fail_count,
                    "unknown": self_assessment_counts_aggregate.unknown_count,
                },
            },
        }
    )


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def get_self_evaluation_feedback_as_requester(request, learning_unit_id):
    learning_unit = get_object_or_404(LearningUnit, id=learning_unit_id)

    feedback = get_object_or_404(
        SelfEvaluationFeedback,
        learning_unit=learning_unit,
        feedback_requester_user=request.user,
    )

    return Response(SelfEvaluationFeedbackSerializer(feedback).data)


@api_view(["PUT"])
@permission_classes([IsAuthenticated])
def add_provider_self_evaluation_feedback(request, feedback_id):
    feedback_assessment = request.data.get("feedback_assessment")

    feedback = get_object_or_404(
        SelfEvaluationFeedback, id=feedback_id, feedback_provider_user=request.user
    )

    course_completion = get_object_or_404(
        CourseCompletion,
        id=request.data.get("course_completion_id"),
        user=feedback.feedback_requester_user,
    )

    (
        course_completion_feedback,
        created,
    ) = CourseCompletionFeedback.objects.get_or_create(
        feedback=feedback,
        course_completion=course_completion,
        defaults={"feedback_assessment": feedback_assessment},
    )

    if not created:
        course_completion_feedback.feedback_assessment = feedback_assessment
        course_completion_feedback.save()

    return Response({"success": True})
