import csv
from dataclasses import dataclass
from io import StringIO

import structlog
from django.db.models import Q

from vbv_lernwelt.assignment.models import 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.learnpath.models import LearningContentEdoniqTest

logger = structlog.get_logger(__name__)


def get_distinct_course_release_ids():
    distinct_course_release_ids = LearningContentEdoniqTest.objects.values_list(
        "edoniq_course_release_id", flat=True
    ).distinct()

    distinct_course_release_extended_ids = (
        LearningContentEdoniqTest.objects.values_list(
            "edoniq_extended_course_release_id", flat=True
        ).distinct()
    )

    # filter out empty values
    return {
        seq_id
        for seq_id in list(distinct_course_release_ids)
        + list(distinct_course_release_extended_ids)
        if seq_id
    }


def filter_relevant_rows(csv_data):
    release_ids = get_distinct_course_release_ids()
    csv_reader = csv.reader(StringIO(csv_data), delimiter=";")

    # read headers
    headers = next(csv_reader)
    headers = [header.strip() for header in headers]

    # Read the rest of the lines and create a list of dictionaries
    records = []
    for row in csv_reader:
        # strip all whitespace around the column entries
        row = [item.strip() for item in row]
        records.append(dict(zip(headers, row)))

    relevant_rows = [
        row for row in records if row.get("Kursfreigaben ID") in release_ids
    ]

    return relevant_rows


@dataclass
class EdoniqCsvTestResult:
    user_id: str
    user_points: float
    max_points: float
    passed: bool
    edoniq_course_release_id: str
    unfinished: bool = False


def create_edoniq_csv_test_result(edoniq_csv_row_dict) -> EdoniqCsvTestResult:
    user_id = edoniq_csv_row_dict["Benutzer Login"]
    user_points = float(edoniq_csv_row_dict["erreichte Punkte absolut"])
    max_points = float(edoniq_csv_row_dict["erreichbares Punktetotal absolut"])
    passed = edoniq_csv_row_dict["Bestanden-/Nicht-Bestanden Status"] == "Bestanden"
    unfinished = edoniq_csv_row_dict["Bestanden-/Nicht-Bestanden Status"] == "Begonnen"
    edoniq_course_release_id = edoniq_csv_row_dict["Kursfreigaben ID"]

    return EdoniqCsvTestResult(
        user_id=user_id,
        user_points=user_points,
        max_points=max_points,
        passed=passed,
        edoniq_course_release_id=edoniq_course_release_id,
        unfinished=unfinished,
    )


def upsert_edoniq_test_result(
    edoniq_csv_test_result: EdoniqCsvTestResult, fail_silently=False
):
    try:
        u = User.objects.get(id=edoniq_csv_test_result.user_id)

        # because the same edoniq test is used in multiple languages,
        # we have to consider multiple learning contents
        learning_content_qs = LearningContentEdoniqTest.objects.filter(
            Q(edoniq_course_release_id=edoniq_csv_test_result.edoniq_course_release_id)
            | Q(
                edoniq_extended_course_release_id=edoniq_csv_test_result.edoniq_course_release_id
            )
        )

        user_course_session_ids = set(
            CourseSessionUser.objects.filter(
                user_id=u.id,
                course_session__course_id__in=[
                    lc.get_course().id for lc in learning_content_qs
                ],
            ).values_list("course_session__id", flat=True)
        )

        learning_content = learning_content_qs.first()

        for course_session_id in user_course_session_ids:
            course_session = CourseSession.objects.get(id=course_session_id)

            # find the assignment which is attached to this language course
            assignment = (
                learning_content_qs.descendant_of(course_session.course.coursepage)
                .first()
                .content_assignment
            )

            completion_status = AssignmentCompletionStatus.EVALUATION_SUBMITTED
            if edoniq_csv_test_result.unfinished:
                completion_status = AssignmentCompletionStatus.SUBMITTED

            edoniq_extended_time_flag = False
            if (
                learning_content.edoniq_extended_course_release_id
                == edoniq_csv_test_result.edoniq_course_release_id
            ):
                edoniq_extended_time_flag = True

            return update_assignment_completion(
                assignment_user=u,
                assignment=assignment,
                course_session=course_session,
                learning_content_page=assignment.find_attached_learning_content(),
                completion_data={},
                completion_status=completion_status,
                evaluation_user=User.objects.get(username="admin"),
                evaluation_points=edoniq_csv_test_result.user_points,
                evaluation_passed=edoniq_csv_test_result.passed,
                evaluation_max_points=edoniq_csv_test_result.max_points,
                additional_json_data={
                    "edoniq_course_release_id": edoniq_csv_test_result.edoniq_course_release_id,
                },
                edoniq_extended_time_flag=edoniq_extended_time_flag,
                validate_submission_update=False,
            )
        return None, False

    except Exception as e:
        logger.warning(
            "edoniq_test_result_import_fail",
            label="edoniq_import_results",
            error="user or learning_content not found",
            user_id=edoniq_csv_test_result.user_id,
            edoniq_course_release_id=edoniq_csv_test_result.edoniq_course_release_id,
            exc_info=True,
        )
        if not fail_silently:
            raise e
        return None, False
