from datetime import datetime, timedelta
from typing import List, Tuple

from django.contrib.auth.hashers import make_password
from django.utils import timezone

from vbv_lernwelt.assignment.models import (
    Assignment,
    AssignmentCompletion,
    AssignmentCompletionStatus,
    AssignmentType,
)
from vbv_lernwelt.assignment.tests.assignment_factories import (
    AssignmentFactory,
    AssignmentListPageFactory,
)
from vbv_lernwelt.competence.factories import (
    ActionCompetenceFactory,
    ActionCompetenceListPageFactory,
    CompetenceCertificateFactory,
    CompetenceCertificateListFactory,
    CompetenceNaviPageFactory,
    PerformanceCriteriaFactory,
)
from vbv_lernwelt.competence.models import CompetenceCertificate, PerformanceCriteria
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.factories import CoursePageFactory
from vbv_lernwelt.course.models import (
    Course,
    CourseCategory,
    CoursePage,
    CourseSession,
    CourseSessionUser,
)
from vbv_lernwelt.course.utils import get_wagtail_default_site
from vbv_lernwelt.course_session.models import (
    CourseSessionAssignment,
    CourseSessionAttendanceCourse,
    CourseSessionEdoniqTest,
)
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
from vbv_lernwelt.duedate.models import DueDate
from vbv_lernwelt.learnpath.models import (
    Circle,
    LearningContentAssignment,
    LearningContentEdoniqTest,
    LearningPath,
)
from vbv_lernwelt.learnpath.tests.learning_path_factories import (
    CircleFactory,
    LearningContentAssignmentFactory,
    LearningContentAttendanceCourseFactory,
    LearningContentEdoniqTestFactory,
    LearningPathFactory,
    LearningUnitFactory,
    TopicFactory,
)


def create_course(title: str, _id=None) -> Tuple[Course, CoursePage]:
    course = Course.objects.create(id=_id, title=title, category_name="Handlungsfeld")

    course_page = CoursePageFactory(
        title="Test Lehrgang",
        parent=get_wagtail_default_site().root_page,
        course=course,
    )
    course.slug = course_page.slug
    course.save()

    return course, course_page


def create_user(username: str) -> User:
    return User.objects.create_user(
        username=username,
        password=make_password("test"),
        email=f"{username}@example.com",
        language="de",
        first_name="Test",
        last_name=username.capitalize(),
    )


def create_course_session(
    course: Course, title: str, generation: str = "2023"
) -> CourseSession:
    return CourseSession.objects.create(
        course=course,
        title=title,
        import_id=title,
        generation=generation,
        start_date=timezone.now(),
    )


def add_course_session_user(
    course_session: CourseSession, user: User, role: CourseSessionUser.Role
) -> CourseSessionUser:
    return CourseSessionUser.objects.create(
        course_session=course_session,
        user=user,
        role=role,
    )


def create_course_session_group(course_session: CourseSession) -> CourseSessionGroup:
    group = CourseSessionGroup.objects.create(
        course=course_session.course,
    )

    group.course_session.add(course_session)

    return group


def add_course_session_group_supervisor(group: CourseSessionGroup, user: User):
    group.supervisor.add(user)


def add_course_session_group_course_session(
    group: CourseSessionGroup, course_session: CourseSession
):
    group.course_session.add(course_session)


def create_circle(
    title: str, course_page: CoursePage, learning_path: LearningPath | None = None
) -> Tuple[Circle, LearningPath]:
    if not learning_path:
        learning_path = LearningPathFactory(title="Test Lernpfad", parent=course_page)

    TopicFactory(title="Circle Test Topic", is_visible=False, parent=learning_path)

    circle = CircleFactory(
        title=title, parent=learning_path, description="Circle Description"
    )

    return circle, learning_path


def create_attendance_course(
    course_session: CourseSession,
    circle: Circle,
    attendance_user_list: List,
    due_date_end: datetime,
) -> CourseSessionAttendanceCourse:
    learning_content_dummy = LearningContentAttendanceCourseFactory(
        title="Lerninhalt Dummy",
        parent=circle,
    )

    return CourseSessionAttendanceCourse.objects.create(
        course_session=course_session,
        learning_content=learning_content_dummy,
        attendance_user_list=attendance_user_list,
        due_date=DueDate.objects.create(
            course_session=course_session,
            start=due_date_end - timedelta(hours=8),
            end=due_date_end,
        ),
    )


def create_competence_certificate(course: Course) -> CompetenceCertificate:
    navi = CompetenceNaviPageFactory(parent=course.coursepage)
    certificate_list = CompetenceCertificateListFactory(parent=navi)
    return CompetenceCertificateFactory(parent=certificate_list)


def create_assignment(
    course: Course,
    assignment_type: AssignmentType,
) -> Assignment:
    return AssignmentFactory(
        parent=AssignmentListPageFactory(
            parent=course.coursepage,
        ),
        assignment_type=assignment_type.name,
        title=f"Dummy Assignment ({assignment_type.name})",
        effort_required=":)",
        intro_text=":)",
        performance_objectives=[],
    )


def create_assignment_completion(
    user: User,
    assignment: Assignment,
    course_session: CourseSession,
    has_passed: bool | None = None,
    max_points: int = 0,
    achieved_points: int = 0,
    status: AssignmentCompletionStatus = AssignmentCompletionStatus.EVALUATION_SUBMITTED,
) -> AssignmentCompletion:
    return AssignmentCompletion.objects.create(
        completion_status=status.value,
        assignment_user=user,
        assignment=assignment,
        evaluation_passed=has_passed,
        course_session=course_session,
        completion_data={},
        evaluation_max_points=max_points,
        evaluation_points=achieved_points,
    )


def create_assignment_learning_content(
    circle: Circle,
    assignment: Assignment,
) -> LearningContentAssignment | LearningContentEdoniqTest:
    if AssignmentType(assignment.assignment_type) == AssignmentType.EDONIQ_TEST:
        return LearningContentEdoniqTestFactory(
            title="Learning Content (EDONIQ_TEST)",
            parent=circle,
            content_assignment=assignment,
        )

    return LearningContentAssignmentFactory(
        title=f"Learning Content ({assignment.assignment_type})",
        parent=circle,
        content_assignment=assignment,
    )


def create_course_session_assignment(
    course_session: CourseSession,
    learning_content_assignment: LearningContentAssignment,
    deadline_at: datetime | None = None,
) -> CourseSessionAssignment:
    cas = CourseSessionAssignment.objects.create(
        course_session=course_session,
        learning_content=learning_content_assignment,
    )

    if deadline_at:
        # the save on the course_session_assignment already sets a lot
        # of due date fields, so it's easier to just overwrite the this
        cas.submission_deadline.start = timezone.make_aware(deadline_at)
        cas.submission_deadline.save()

    return cas


def create_course_session_edoniq_test(
    course_session: CourseSession,
    learning_content_edoniq_test: LearningContentEdoniqTest,
    deadline_at: datetime,
) -> CourseSessionEdoniqTest:
    cset = CourseSessionEdoniqTest.objects.create(
        course_session=course_session,
        learning_content=learning_content_edoniq_test,
    )

    # same as above (see create_course_session_assignment)
    cset.deadline.start = timezone.make_aware(deadline_at)
    cset.deadline.save()

    return cset


def create_performance_criteria_page(
    course: Course,
    course_page: CoursePage,
    circle: Circle,
) -> PerformanceCriteria:
    competence_navi_page = CompetenceNaviPageFactory(
        title="Competence Navi",
        parent=course_page,
    )

    competence_profile_page = ActionCompetenceListPageFactory(
        title="Action Competence Page",
        parent=competence_navi_page,
    )

    action_competence = ActionCompetenceFactory(
        parent=competence_profile_page,
        competence_id="X1",
        title="Action Competence",
        items=[("item", "Action Competence Item")],
    )

    cat, _ = CourseCategory.objects.get_or_create(
        course=course, title="Course Category"
    )

    lu = LearningUnitFactory(title="Learning Unit", parent=circle, course_category=cat)

    return PerformanceCriteriaFactory(
        parent=action_competence,
        competence_id="X1.1",
        title="Performance Criteria",
        learning_unit=lu,
    )


def create_circle_expert(
    course_session: CourseSession, circle: Circle, username: str
) -> CourseSessionUser:
    expert_user = create_user(username)
    course_session_expert_user = add_course_session_user(
        course_session=course_session,
        user=expert_user,
        role=CourseSessionUser.Role.EXPERT,
    )
    course_session_expert_user.expert.add(circle)

    return course_session_expert_user
