import json

import graphene
import structlog
from rest_framework.exceptions import PermissionDenied
from wagtail.models import Page

from vbv_lernwelt.assignment.graphql.types import AssignmentCompletionObjectType
from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatus
from vbv_lernwelt.assignment.services import update_assignment_completion
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
from vbv_lernwelt.iam.permissions import can_evaluate_assignments, has_course_access

logger = structlog.get_logger(__name__)


class AssignmentCompletionMutation(graphene.Mutation):
    assignment_completion = graphene.Field(AssignmentCompletionObjectType)

    class Arguments:
        assignment_id = graphene.ID(required=True)
        course_session_id = graphene.ID(required=True)
        learning_content_page_id = graphene.ID()
        assignment_user_id = graphene.UUID()

        completion_status = graphene.Argument(
            graphene.Enum.from_enum(AssignmentCompletionStatus)
        )
        completion_data_string = graphene.String()

        evaluation_points = graphene.Float()
        evaluation_passed = graphene.Boolean()
        evaluation_user_id = graphene.ID(required=False)
        initialize_completion = graphene.Boolean(required=False)

    @classmethod
    def mutate(
        cls,
        root,
        info,
        assignment_id,
        course_session_id,
        learning_content_page_id=None,
        assignment_user_id=None,
        completion_status: AssignmentCompletionStatus = AssignmentCompletionStatus.IN_PROGRESS,
        completion_data_string="{}",
        evaluation_points=None,
        evaluation_passed=None,
        evaluation_user_id=None,
        initialize_completion=False,
    ):
        if assignment_user_id is None:
            assignment_user_id = info.context.user.id

        assignment_user = User.objects.get(id=assignment_user_id)
        assignment = Assignment.objects.get(id=assignment_id)

        if not has_course_access(
            info.context.user,
            assignment.get_course().id,
        ):
            raise PermissionDenied()

        course_session = CourseSession.objects.get(id=course_session_id)

        learning_content_page = None
        if learning_content_page_id:
            learning_content_page = Page.objects.get(id=learning_content_page_id)
        if learning_content_page is None:
            learning_content_page = assignment.find_attached_learning_content()

        assignment_data = {
            "assignment_user": assignment_user,
            "assignment": assignment,
            "course_session": course_session,
            "completion_data": json.loads(completion_data_string),
            "completion_status": completion_status,
            "learning_content_page": learning_content_page,
        }

        if completion_status == AssignmentCompletionStatus.SUBMITTED:
            if evaluation_user_id:
                assignment_data["evaluation_user"] = User.objects.get(
                    id=evaluation_user_id
                )
            else:
                # TODO: determine proper way to assign evaluation user
                experts = CourseSessionUser.objects.filter(
                    course_session=course_session, role="EXPERT"
                )
                if not experts:
                    raise PermissionDenied()
                assignment_data["evaluation_user"] = experts[0].user

        evaluation_data = {}

        if completion_status in (
            AssignmentCompletionStatus.EVALUATION_SUBMITTED,
            AssignmentCompletionStatus.EVALUATION_IN_PROGRESS,
        ):
            if not can_evaluate_assignments(
                evaluation_user=info.context.user,
                assignment_user_id=assignment_user_id,
                course_session_id=course_session_id,
            ):
                raise PermissionDenied()

            evaluation_data = {
                "evaluation_user": info.context.user,
                "evaluation_points": evaluation_points,
                "evaluation_passed": evaluation_passed,
            }

        ac, created = update_assignment_completion(
            copy_task_data=False,
            initialize_completion=initialize_completion,
            **assignment_data,
            **evaluation_data,
        )

        logger.debug(
            "AssignmentCompletionMutation successful",
            label="assignment_api",
            assignment_id=assignment.id,
            assignment_title=assignment.title,
            assignment_user_id=assignment_user_id,
            course_session_id=course_session_id,
            completion_status=completion_status,
            created=created,
        )

        return AssignmentCompletionMutation(assignment_completion=ac)


class AssignmentMutation:
    upsert_assignment_completion = AssignmentCompletionMutation.Field()
