import json

from django.utils import timezone
from rest_framework.test import APITestCase

from vbv_lernwelt.assignment.creators.create_assignments import (
    create_uk_fahrzeug_casework,
)
from vbv_lernwelt.assignment.models import (
    AssignmentCompletion,
    AssignmentCompletionAuditLog,
)
from vbv_lernwelt.core.create_default_users import create_default_users
from vbv_lernwelt.core.models import User
from vbv_lernwelt.core.utils import find_first
from vbv_lernwelt.course.consts import COURSE_TEST_ID
from vbv_lernwelt.course.creators.test_course import create_test_course
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser


class AssignmentApiTestCase(APITestCase):
    def setUp(self) -> None:
        create_default_users()
        create_test_course(include_vv=False)
        self.assignment = create_uk_fahrzeug_casework(course_id=COURSE_TEST_ID)
        self.assignment_subtasks = self.assignment.filter_user_subtasks()

        self.cs = CourseSession.objects.create(
            course_id=COURSE_TEST_ID,
            title="Test Lehrgang Session",
        )
        self.student = User.objects.get(username="student")
        self.student_csu = CourseSessionUser.objects.create(
            course_session=self.cs,
            user=self.student,
        )

        self.expert = User.objects.get(username="admin")
        self.expert_csu = CourseSessionUser.objects.create(
            course_session=self.cs, user=self.expert, role="EXPERT"
        )

    def test_student_can_upsertAssignmentCompletion(self):
        self.client.login(username="student", password="test")
        url = f"/api/assignment/upsert/"

        user_text_input = find_first(
            self.assignment_subtasks, pred=lambda x: x["type"] == "user_text_input"
        )

        response = self.client.post(
            url,
            {
                "assignment_id": self.assignment.id,
                "course_session_id": self.cs.id,
                "completion_status": "IN_PROGRESS",
                "completion_data": {
                    user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
                },
            },
            format="json",
        )
        response_json = response.json()
        print(json.dumps(response.json(), indent=2))

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response_json["assignment_user"], self.student.id)
        self.assertEqual(response_json["assignment"], self.assignment.id)
        self.assertEqual(response_json["completion_status"], "IN_PROGRESS")
        self.assertDictEqual(
            response_json["completion_data"],
            {
                user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
            },
        )

        db_entry = AssignmentCompletion.objects.get(
            assignment_user=self.student,
            course_session_id=self.cs.id,
            assignment_id=self.assignment.id,
        )
        self.assertEqual(db_entry.completion_status, "IN_PROGRESS")
        self.assertDictEqual(
            db_entry.completion_data,
            {
                user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
            },
        )

        # read data via request api
        response = self.client.get(
            f"/api/assignment/{self.assignment.id}/{self.cs.id}/",
            format="json",
        )
        response_json = response.json()
        print(json.dumps(response.json(), indent=2))
        self.assertDictEqual(
            response_json["completion_data"],
            {
                user_text_input["id"]: {"user_data": {"text": "Hallo via API"}},
            },
        )

        # submit the assignment
        response = self.client.post(
            url,
            {
                "assignment_id": self.assignment.id,
                "course_session_id": self.cs.id,
                "completion_status": "SUBMITTED",
                "completion_data": {
                    user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
                },
            },
            format="json",
        )
        response_json = response.json()
        print(json.dumps(response.json(), indent=2))

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response_json["assignment_user"], self.student.id)
        self.assertEqual(response_json["assignment"], self.assignment.id)
        self.assertEqual(response_json["completion_status"], "SUBMITTED")
        self.assertDictEqual(
            response_json["completion_data"],
            {
                user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
            },
        )

        # second submit will fail
        response = self.client.post(
            url,
            {
                "assignment_id": self.assignment.id,
                "course_session_id": self.cs.id,
                "completion_status": "SUBMITTED",
                "completion_data": {
                    user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}},
                },
            },
            format="json",
        )
        response_json = response.json()
        print(json.dumps(response.json(), indent=2))
        self.assertEqual(response.status_code, 404)
        self.assertTrue("Cannot update completion status" in str(response_json))

    def test_expert_can_gradeAssignmentCompletion(self):
        # setup AssignmentCompletion
        subtasks = self.assignment.filter_user_subtasks(
            subtask_types=["user_text_input"]
        )
        user_text_input = find_first(
            subtasks,
            pred=lambda x: (value := x.get("value"))
            and value.get("text", "").startswith(
                "Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest?"
            ),
        )

        ac = AssignmentCompletion.objects.create(
            assignment_user=self.student,
            assignment=self.assignment,
            course_session=self.cs,
            completion_status="SUBMITTED",
            submitted_at=timezone.now(),
            completion_data={
                user_text_input["id"]: {
                    "user_data": {"text": "Ich würde nichts weiteres empfehlen."}
                },
            },
        )

        # make api call
        self.client.login(username="admin", password="test")
        url = f"/api/assignment/evaluate/"

        response = self.client.post(
            url,
            {
                "assignment_id": self.assignment.id,
                "assignment_user_id": self.student.id,
                "course_session_id": self.cs.id,
                "completion_status": "EVALUATION_IN_PROGRESS",
                "completion_data": {
                    user_text_input["id"]: {
                        "expert_data": {"points": 1, "comment": "Gut gemacht!"}
                    },
                },
            },
            format="json",
        )
        response_json = response.json()
        print(json.dumps(response.json(), indent=2))

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response_json["assignment_user"], self.student.id)
        self.assertEqual(response_json["assignment"], self.assignment.id)
        self.assertEqual(response_json["completion_status"], "EVALUATION_IN_PROGRESS")
        self.assertDictEqual(
            response_json["completion_data"],
            {
                user_text_input["id"]: {
                    "user_data": {"text": "Ich würde nichts weiteres empfehlen."},
                    "expert_data": {"points": 1, "comment": "Gut gemacht!"},
                },
            },
        )

        db_entry = AssignmentCompletion.objects.get(
            assignment_user=self.student,
            course_session_id=self.cs.id,
            assignment_id=self.assignment.id,
        )
        self.assertEqual(db_entry.completion_status, "EVALUATION_IN_PROGRESS")
        self.assertDictEqual(
            db_entry.completion_data,
            {
                user_text_input["id"]: {
                    "user_data": {"text": "Ich würde nichts weiteres empfehlen."},
                    "expert_data": {"points": 1, "comment": "Gut gemacht!"},
                },
            },
        )

        # finish grading
        response = self.client.post(
            url,
            {
                "assignment_id": self.assignment.id,
                "assignment_user_id": self.student.id,
                "course_session_id": self.cs.id,
                "completion_status": "EVALUATION_SUBMITTED",
                "completion_data": {
                    user_text_input["id"]: {
                        "expert_data": {"points": 1, "comment": "Gut gemacht!"}
                    },
                },
                "evaluation_grade": 4.5,
                "evaluation_points": 16,
            },
            format="json",
        )
        response_json = response.json()
        print(json.dumps(response.json(), indent=2))

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response_json["assignment_user"], self.student.id)
        self.assertEqual(response_json["assignment"], self.assignment.id)
        self.assertEqual(response_json["completion_status"], "EVALUATION_SUBMITTED")
        self.assertDictEqual(
            response_json["completion_data"],
            {
                user_text_input["id"]: {
                    "user_data": {"text": "Ich würde nichts weiteres empfehlen."},
                    "expert_data": {"points": 1, "comment": "Gut gemacht!"},
                },
            },
        )

        db_entry = AssignmentCompletion.objects.get(
            assignment_user=self.student,
            course_session_id=self.cs.id,
            assignment_id=self.assignment.id,
        )
        self.assertEqual(db_entry.completion_status, "EVALUATION_SUBMITTED")
        self.assertDictEqual(
            db_entry.completion_data,
            {
                user_text_input["id"]: {
                    "user_data": {"text": "Ich würde nichts weiteres empfehlen."},
                    "expert_data": {"points": 1, "comment": "Gut gemacht!"},
                },
            },
        )

        # `EVALUATION_SUBMITTED` will create a new AssignmentCompletionAuditLog
        acl = AssignmentCompletionAuditLog.objects.get(
            assignment_user=self.student,
            course_session_id=self.cs.id,
            assignment_id=self.assignment.id,
            completion_status="EVALUATION_SUBMITTED",
        )
        self.maxDiff = None
        self.assertDictEqual(
            acl.completion_data,
            {
                user_text_input["id"]: {
                    "id": user_text_input["id"],
                    "type": "user_text_input",
                    "value": {
                        "text": "Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest? Begründe deine Empfehlung",
                    },
                    "user_data": {"text": "Ich würde nichts weiteres empfehlen."},
                    "expert_data": {"points": 1, "comment": "Gut gemacht!"},
                },
            },
        )
