import { LearningPathNode } from '../../entities/content/learning-path-node.entity.js';
import { Student } from '../../entities/users/student.entity.js';
import { Group } from '../../entities/assignments/group.entity.js';
import { Submission } from '../../entities/assignments/submission.entity.js';
import { getClassRepository, getGroupRepository, getStudentRepository, getSubmissionRepository } from '../../data/repositories.js';
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js';
import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity.js';
import { JSONPath } from 'jsonpath-plus';

export type PersonalizationTarget = { type: 'student'; student: Student } | { type: 'group'; group: Group };

/**
 * Shortcut function to easily create a PersonalizationTarget object for a student by his/her username.
 * @param username Username of the student we want to generate a personalized learning path for.
 *                 If there is no student with this username, return undefined.
 */
export async function personalizedForStudent(username: string): Promise<PersonalizationTarget | undefined> {
    const student = await getStudentRepository().findByUsername(username);
    if (student) {
        return {
            type: 'student',
            student: student,
        };
    }
    return undefined;
}

/**
 * Shortcut function to easily create a PersonalizationTarget object for a group by class name, assignment number and
 * group number.
 * @param classId Id of the class in which this group was created
 * @param assignmentNumber Number of the assignment for which this group was created
 * @param groupNumber Number of the group for which we want to personalize the learning path.
 */
export async function personalizedForGroup(
    classId: string,
    assignmentNumber: number,
    groupNumber: number
): Promise<PersonalizationTarget | undefined> {
    const clazz = await getClassRepository().findById(classId);
    if (!clazz) {
        return undefined;
    }
    const group = await getGroupRepository().findOne({
        assignment: {
            within: clazz,
            id: assignmentNumber,
        },
        groupNumber: groupNumber,
    });
    if (group) {
        return {
            type: 'group',
            group: group,
        };
    }
    return undefined;
}

/**
 * Returns the last submission for the learning object associated with the given node and for the student or group
 */
export async function getLastSubmissionForCustomizationTarget(node: LearningPathNode, pathFor: PersonalizationTarget): Promise<Submission | null> {
    const submissionRepo = getSubmissionRepository();
    const learningObjectId: LearningObjectIdentifier = {
        hruid: node.learningObjectHruid,
        language: node.language,
        version: node.version,
    };
    if (pathFor.type === 'group') {
        return await submissionRepo.findMostRecentSubmissionForGroup(learningObjectId, pathFor.group);
    }
    return await submissionRepo.findMostRecentSubmissionForStudent(learningObjectId, pathFor.student);
}

/**
 * Checks whether the condition of the given transaction is fulfilled by the given submission.
 * @param transition
 * @param submitted
 */
export function isTransitionPossible(transition: LearningPathTransition, submitted: object | null): boolean {
    if (transition.condition === 'true' || !transition.condition) {
        return true; // If the transition is unconditional, we can go on.
    }
    if (submitted === null) {
        return false; // If the transition is not unconditional and there was no submission, the transition is not possible.
    }
    const match = JSONPath({ path: transition.condition, json: { submission: submitted } });
    return match.length === 1;
}
