import os

from django.test import TestCase
from django.utils.timezone import make_naive
from openpyxl.reader.excel import load_workbook

from vbv_lernwelt.assignment.models import AssignmentType
from vbv_lernwelt.core.constants import TEST_TRAINER1_USER_ID
from vbv_lernwelt.core.create_default_users import create_default_users
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.creators.test_course import create_test_course
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
from vbv_lernwelt.course_session.models import (
    CourseSessionAssignment,
    CourseSessionAttendanceCourse,
    CourseSessionEdoniqTest,
)
from vbv_lernwelt.duedate.models import DueDate
from vbv_lernwelt.importer.services import (
    create_or_update_course_session,
    create_or_update_course_session_edoniq_test,
    DataImportError,
    validate_row_data,
)
from vbv_lernwelt.importer.utils import (
    calc_header_tuple_list_from_pyxl_sheet,
    try_parse_datetime,
)
from vbv_lernwelt.learnpath.models import Circle

test_dir = os.path.dirname(os.path.abspath(__file__))


class ImportCourseSessionTestCase(TestCase):
    def setUp(self):
        self.course = create_test_course(include_vv=False)

    def test_import_excel_file(self):
        workbook = load_workbook(
            filename=f"{test_dir}/Schulungen_Durchfuehrung_Trainer.xlsx"
        )
        sheet = workbook["Schulungen Durchführung"]

        tuple_list = calc_header_tuple_list_from_pyxl_sheet(sheet)
        for row in tuple_list:
            create_or_update_course_session(
                self.course, dict(row), language="de", circle_keys=["Kickoff"]
            )

        self.assertEqual(CourseSession.objects.count(), 6)


class CreateOrUpdateCourseSessionTestCase(TestCase):
    def setUp(self):
        create_default_users()
        self.course = create_test_course(include_vv=False)

    def test_create_course_session(self):
        row = [
            ("ID", "AG 2023 A"),
            ("Generation", 2023),
            ("Region", "Aargau"),
            ("Sprache", "de"),
            ("Klasse", "A"),
            ("Fahrzeug Start", "06.06.2023, 13:30"),
            ("Fahrzeug Ende", "06.06.2023, 15:00"),
            (
                "Fahrzeug Raum",
                "E64",
            ),
            ("Fahrzeug Standort", "HKV Aarau"),
            ("Fahrzeug Adresse", "Bahnhofstrasse 460, 5001, Aarau"),
        ]

        data = dict(row)

        cs = create_or_update_course_session(
            self.course, data, language="de", circle_keys=["Fahrzeug"]
        )

        self.assertEqual(cs.import_id, "AG 2023 A")
        self.assertEqual(cs.title, "Aargau 2023 A")
        self.assertEqual(cs.generation, "2023")
        self.assertEqual(cs.region, "Aargau")
        self.assertEqual(cs.group, "A")

        attendance_course = CourseSessionAttendanceCourse.objects.first()
        self.assertEqual(
            attendance_course.due_date.start.isoformat(), "2023-06-06T11:30:00+00:00"
        )
        self.assertEqual(
            attendance_course.due_date.end.isoformat(), "2023-06-06T13:00:00+00:00"
        )
        self.assertEqual(
            f"E64, HKV Aarau, Bahnhofstrasse 460, 5001, Aarau",
            attendance_course.location,
        )
        self.assertEqual("", attendance_course.trainer)
        self.assertEqual(4, DueDate.objects.count())

        casework = CourseSessionAssignment.objects.filter(
            learning_content__assignment_type=AssignmentType.CASEWORK.value
        ).first()
        self.assertEqual(
            casework.submission_deadline.start.isoformat(), "2023-07-06T11:30:00+00:00"
        )

    def test_update_course_session(self):
        row = [
            ("ID", "AG 2023 A"),
            ("Generation", 2023),
            ("Region", "Aargau"),
            ("Sprache", "de"),
            ("Klasse", "A"),
            ("Fahrzeug Start", "06.06.2023, 13:30"),
            ("Fahrzeug Ende", "06.06.2023, 15:00"),
            (
                "Fahrzeug Raum",
                "E64",
            ),
            ("Fahrzeug Standort", "HKV Aarau"),
            ("Fahrzeug Adresse", "Bahnhofstrasse 460, 5001, Aarau"),
        ]

        data = dict(row)

        cs = create_or_update_course_session(
            self.course, data, language="de", circle_keys=["Fahrzeug"]
        )

        trainer1 = User.objects.get(id=TEST_TRAINER1_USER_ID)
        csu = CourseSessionUser.objects.create(
            course_session=cs,
            user=trainer1,
            role=CourseSessionUser.Role.EXPERT,
        )
        csu.expert.add(Circle.objects.get(slug="test-lehrgang-lp-circle-fahrzeug"))

        update_row = [
            ("ID", "AG 2023 A"),
            ("Generation", 2023),
            ("Region", "Aargau"),
            ("Sprache", "de"),
            ("Klasse", "A"),
            ("Fahrzeug Start", "06.06.2023, 14:30"),
            ("Fahrzeug Ende", "06.06.2023, 17:00"),
            (
                "Fahrzeug Raum",
                "E666",
            ),
            ("Fahrzeug Standort", "HKV Aarau2"),
            ("Fahrzeug Adresse", "Bahnhofstrasse 460, 5002, Aarau"),
        ]

        data = dict(update_row)

        cs = create_or_update_course_session(
            self.course, data, language="de", circle_keys=["Fahrzeug"]
        )

        self.assertEqual(1, CourseSession.objects.count())

        self.assertEqual(cs.import_id, "AG 2023 A")
        self.assertEqual(cs.title, "Aargau 2023 A")
        self.assertEqual(cs.generation, "2023")
        self.assertEqual(cs.region, "Aargau")
        self.assertEqual(cs.group, "A")

        attendance_course = CourseSessionAttendanceCourse.objects.first()
        self.assertEqual(
            attendance_course.due_date.start.isoformat(), "2023-06-06T12:30:00+00:00"
        )
        self.assertEqual(
            attendance_course.due_date.end.isoformat(), "2023-06-06T15:00:00+00:00"
        )
        self.assertEqual(
            f"E666, HKV Aarau2, Bahnhofstrasse 460, 5002, Aarau",
            attendance_course.location,
        )
        self.assertEqual(
            f"{trainer1.first_name} {trainer1.last_name}", attendance_course.trainer
        )
        self.assertEqual(4, DueDate.objects.count())

        casework = CourseSessionAssignment.objects.filter(
            learning_content__assignment_type=AssignmentType.CASEWORK.value
        ).first()
        self.assertEqual(
            casework.submission_deadline.start.isoformat(), "2023-07-06T12:30:00+00:00"
        )

    def test_import_course_session_twice(self):
        """
        importing the course session twice should create the
        `CourseSessionAttendanceCourse` only once
        """
        row = [
            ("ID", "AG 2023 A"),
            ("Generation", 2023),
            ("Region", "Aargau"),
            ("Sprache", "de"),
            ("Klasse", "A"),
            ("Fahrzeug Start", "06.06.2023, 13:30"),
            ("Fahrzeug Ende", "06.06.2023, 15:00"),
            (
                "Fahrzeug Raum",
                "E64",
            ),
            ("Fahrzeug Standort", "HKV Aarau"),
            ("Fahrzeug Adresse", "Bahnhofstrasse 460, 5001, Aarau"),
        ]

        data = dict(row)

        cs1 = create_or_update_course_session(
            self.course, data, language="de", circle_keys=["Fahrzeug"]
        )
        attendance_course1 = CourseSessionAttendanceCourse.objects.first()
        self.assertEqual(
            attendance_course1.due_date.start.isoformat(), "2023-06-06T11:30:00+00:00"
        )
        self.assertEqual(
            attendance_course1.due_date.end.isoformat(), "2023-06-06T13:00:00+00:00"
        )

        # import a second time
        data["Fahrzeug Start"] = "06.06.2023, 13:15"
        data["Fahrzeug Ende"] = "06.06.2023, 14:45"
        cs2 = create_or_update_course_session(
            self.course, data, language="de", circle_keys=["Fahrzeug"]
        )

        self.assertEqual(cs1.id, cs2.id)
        attendance_course2 = CourseSessionAttendanceCourse.objects.first()
        self.assertEqual(
            attendance_course2.due_date.start.isoformat(), "2023-06-06T11:15:00+00:00"
        )
        self.assertEqual(
            attendance_course2.due_date.end.isoformat(), "2023-06-06T12:45:00+00:00"
        )
        self.assertEqual(attendance_course1.id, attendance_course2.id)

    def test_raise_exception_if_header_is_missing(self):
        data = [
            ("ID", "DE 2023"),
        ]

        with self.assertRaises(DataImportError):
            validate_row_data(
                dict(data),
                [
                    "ID",
                    "Generation",
                ],
            )

    def test_raise_exception_if_required_field_is_empty(self):
        data = [
            ("ID", "DE 2023"),
            ("Generation", ""),
        ]

        with self.assertRaises(DataImportError):
            validate_row_data(
                dict(data),
                [
                    "ID",
                    "Generation",
                ],
            )


class CreateOrUpdateEdoniqTestCase(TestCase):
    def setUp(self):
        create_default_users()
        self.course = create_test_course(include_vv=False)
        row = [
            ("ID", "AG 2023 A"),
            ("Generation", 2023),
            ("Region", "Aargau"),
            ("Sprache", "de"),
            ("Klasse", "A"),
            ("Kickoff Start", "06.06.2023, 13:30"),
            ("Kickoff Ende", "06.06.2023, 15:00"),
            (
                "Kickoff Raum",
                "E64",
            ),
            ("Kickoff Standort", "HKV Aarau"),
            ("Kickoff Adresse", "Bahnhofstrasse 460, 5001, Aarau"),
        ]

        data = dict(row)

        self.cs = create_or_update_course_session(
            self.course, data, language="de", circle_keys=["Kickoff"]
        )

    def _create_or_update_edonqi_test(self, date: str):
        start_date = try_parse_datetime(date)[1]

        create_or_update_course_session_edoniq_test(
            self.cs,
            "test-lehrgang",
            "fahrzeug-lc-wissens-und-verständnisfragen",
            make_naive(start_date),
        )

    def test_create_course_session(self):
        self._create_or_update_edonqi_test("2023-06-06T11:30:00+00:00")

        test = CourseSessionEdoniqTest.objects.first()
        self.assertEqual(test.deadline.start.isoformat(), "2023-06-20T11:30:00+00:00")

    def test_update_course_session(self):
        self._create_or_update_edonqi_test("2023-06-06T11:30:00+00:00")
        duedate_count = DueDate.objects.count()
        self._create_or_update_edonqi_test("2023-06-07T11:30:00+00:00")
        self.assertEqual(duedate_count, DueDate.objects.count())

        test = CourseSessionEdoniqTest.objects.first()
        self.assertEqual(test.deadline.start.isoformat(), "2023-07-20T11:30:00+00:00")
