import { Module } from "@/interfaces";
import { ExamState } from "../types";
import { SESSION_ACTIONS, SessionActions, sessionReducer } from "./session";
import { Exam, UserSolution } from "@/interfaces/exam";
import { updateExamWithUserSolutions } from "../utils";
import { defaultExamUserSolutions } from "@/utils/exams";
import { Assignment } from "@/interfaces/results";
import { Stat } from "@/interfaces/user";
import { convertToUserSolutions } from "@/utils/stats";

export type RootActions =
    { type: 'INIT_EXAM'; payload: { exams: Exam[], modules: Module[], assignment?: Assignment } } |
    { type: 'INIT_SOLUTIONS'; payload: { exams: Exam[], modules: Module[], stats: Stat[], timeSpent?: number, inactivity?: number } } |
    { type: 'UPDATE_TIMERS'; payload: { timeSpent: number; inactivity: number; timeSpentCurrentModule: number; } } |
    { type: 'FINALIZE_MODULE'; payload: { updateTimers: boolean } } |
    { type: 'FINALIZE_MODULE_SOLUTIONS' } |
    { type: 'UPDATE_EXAMS'}


export type Action = RootActions | SessionActions;

export const rootReducer = (
    state: ExamState,
    action: Action
): Partial<ExamState> => {

    if (SESSION_ACTIONS.includes(action.type as any)) {
        return sessionReducer(action as SessionActions);
    }

    switch (action.type) {
        case 'INIT_EXAM': {
            const { exams, modules, assignment } = action.payload;

            let examAndSolutions = {}

            // A new exam is about to start,
            // fill the first module with defaultUserSolutions
            let defaultSolutions = exams.map(defaultExamUserSolutions).flat();
            examAndSolutions = {
                userSolutions: defaultSolutions,
                exam: updateExamWithUserSolutions(exams[0], defaultSolutions)
            }

            if (assignment) {
                examAndSolutions = { ...examAndSolutions, assignment }
            }

            // now all the modules start at 0 since navigation
            // is now handled at the module page's and no re-renders
            // reset the initial render caused by the timers
            // no need to do all that weird chainning with -1 
            // on some modules and triggering next() to update final solution
            // with hasExamEnded flag
            return {
                moduleIndex: 0,
                partIndex: 0,
                exerciseIndex: 0,
                questionIndex: 0,
                exams: exams,
                selectedModules: modules,
                showSolutions: false,
                ...examAndSolutions
            }
        };
        case 'INIT_SOLUTIONS': {
            const { exams, modules, stats, timeSpent, inactivity } = action.payload;

            let time = {}
            if (timeSpent) time = { timeSpent }
            if (inactivity) time = { ...time, inactivity }

            return {
                moduleIndex: -1,
                partIndex: 0,
                exerciseIndex: 0,
                questionIndex: 0,
                exams: exams,
                selectedModules: modules,
                showSolutions: true,
                userSolutions: convertToUserSolutions(stats),
                ...time
            }
        }
        case 'UPDATE_TIMERS': {
            // Just assigning the timers at once instead of two different calls
            const { timeSpent, inactivity, timeSpentCurrentModule } = action.payload;
            return {
                timeSpentCurrentModule,
                timeSpent,
                inactivity
            }
        };
        case 'FINALIZE_MODULE': {
            const { updateTimers } = action.payload;
            const solutions = state.userSolutions;
            const evaluated = state.evaluated;

            const hasUnevaluatedSolutions = solutions.some(solution =>
                (solution.type === 'speaking' ||
                    solution.type === 'writing' ||
                    solution.type === 'interactiveSpeaking') &&
                !evaluated.some(evaluation => evaluation.exercise === solution.exercise)
            );

            // To finalize a module first flag the timers to be updated
            if (updateTimers) {
                return {
                    flags: { ...state.flags, finalizeModule: true, pendingEvaluation: hasUnevaluatedSolutions }
                }
            } else {
                // then check whether there are more modules in the exam, if there are
                // setup the next module
                if (state.moduleIndex === state.selectedModules.length - 1) {
                    return {
                        showSolutions: true,
                        flags: {
                            ...state.flags,
                            finalizeModule: false,
                            finalizeExam: true,
                            pendingEvaluation: hasUnevaluatedSolutions,
                        }
                    }
                } else if (state.moduleIndex < state.selectedModules.length - 1) {
                    return {
                        moduleIndex: state.moduleIndex + 1,
                        partIndex: 0,
                        exerciseIndex: 0,
                        questionIndex: 0,
                        exam: updateExamWithUserSolutions(state.exams[state.moduleIndex + 1], state.userSolutions),
                        flags: {
                            ...state.flags,
                            finalizeModule: false,
                        }
                    }
                }
            }
        }
        case 'FINALIZE_MODULE_SOLUTIONS': {
            if (state.flags.reviewAll) {
                const notLastModule = state.moduleIndex < state.selectedModules.length - 1;
                const moduleIndex = notLastModule ? state.moduleIndex + 1 : -1;
                
                if (notLastModule) {
                    return {
                        questionIndex: 0,
                        exerciseIndex: 0,
                        partIndex: 0,
                        exam: state.exams[moduleIndex],
                        moduleIndex: moduleIndex
                    }
                } else {
                    return {
                        questionIndex: 0,
                        exerciseIndex: 0,
                        partIndex: 0,
                        moduleIndex: -1
                    }
                }
            } else {
                return {
                    moduleIndex: -1
                }
            }
            
        }
        case 'UPDATE_EXAMS': {
            const exams = state.exams.map((e) => updateExamWithUserSolutions(e, state.userSolutions));
            const exam = updateExamWithUserSolutions(state.exam!, state.userSolutions);
            return {
                exams,
                exam
            }
        }
        default:
            return {};
    }
};