import response from "../../response.js";
import models from "../../models/index.js";
import fs from "fs";
import path from "path";
import {
  clearFileBuffers,
  saveFileToDisk,
} from "../../middlewares/uploadExercise.js";

export const getExercises = async (req, res) => {
  try {
    const exercises = await models.Exercise.findAll({
      where: { IS_DELETED: 0 },
      include: [
        {
          model: models.MultipleChoices,
          as: "multipleChoices",
        },
        {
          model: models.MatchingPairs,
          as: "matchingPairs",
        },
        {
          model: models.TrueFalse,
          as: "trueFalse",
        },
      ],
    });

    if (exercises.length === 0) {
      return response(404, null, "No exercises found", res);
    }

    const result = exercises.map((exercise) => {
      const exerciseData = { ...exercise.dataValues };
      const questionType = exercise.QUESTION_TYPE;

      if (questionType === "MCQ") {
        delete exerciseData.matchingPairs;
        delete exerciseData.trueFalse;
      } else if (questionType === "MPQ") {
        delete exerciseData.multipleChoices;
        delete exerciseData.trueFalse;
      } else if (questionType === "TFQ") {
        delete exerciseData.multipleChoices;
        delete exerciseData.matchingPairs;
      } else {
        delete exerciseData.multipleChoices;
        delete exerciseData.matchingPairs;
        delete exerciseData.trueFalse;
      }

      return exerciseData;
    });

    response(200, result, "Success", res);
  } catch (error) {
    console.log(error);
    res.status(500).json({ message: "Internal Server Error" });
  }
};

export const getExerciseById = async (req, res) => {
  try {
    const { id } = req.params;

    const exercise = await models.Exercise.findOne({
      where: { ID_ADMIN_EXERCISE: id, IS_DELETED: 0 },
      include: [
        {
          model: models.MultipleChoices,
          as: "multipleChoices",
        },
        {
          model: models.MatchingPairs,
          as: "matchingPairs",
        },
        {
          model: models.TrueFalse,
          as: "trueFalse",
        },
      ],
    });

    if (!exercise) {
      return response(404, null, "Exercise not found", res);
    }

    const exerciseData = { ...exercise.dataValues };
    const questionType = exercise.QUESTION_TYPE;

    if (questionType === "MCQ") {
      if (exerciseData.multipleChoices) {
        exerciseData.multipleChoices = exerciseData.multipleChoices.map(
          (choice) => choice.dataValues
        );
      }
      delete exerciseData.matchingPairs;
      delete exerciseData.trueFalse;
    } else if (questionType === "MPQ") {
      if (exerciseData.matchingPairs) {
        exerciseData.matchingPairs = exerciseData.matchingPairs.map(
          (pair) => pair.dataValues
        );
      }
      delete exerciseData.multipleChoices;
      delete exerciseData.trueFalse;
    } else if (questionType === "TFQ") {
      if (exerciseData.trueFalse) {
        exerciseData.trueFalse = exerciseData.trueFalse.map(
          (tf) => tf.dataValues
        );
      }
      delete exerciseData.multipleChoices;
      delete exerciseData.matchingPairs;
    } else {
      delete exerciseData.multipleChoices;
      delete exerciseData.matchingPairs;
      delete exerciseData.trueFalse;
    }

    response(200, exerciseData, "Success", res);
  } catch (error) {
    console.log(error);
    res.status(500).json({ message: "Internal Server Error" });
  }
};

export const getExercisesForAdmin = async (req, res) => {
  const { page = 1, limit = 10, search = "", sort = "time" } = req.query;

  try {
    const { rows: exercises } = await models.Exercise.findAndCountAll({
      where: {
        IS_DELETED: 0,
        ...(search && {
          [models.Op.or]: [
            {
              "$levelExercise->levelTopic->topicSection.NAME_SECTION$": {
                [models.Op.like]: `%${search}%`,
              },
            },
            {
              "$levelExercise->levelTopic.NAME_TOPIC$": {
                [models.Op.like]: `%${search}%`,
              },
            },
            {
              "$levelExercise.NAME_LEVEL$": { [models.Op.like]: `%${search}%` },
            },
            { QUESTION: { [models.Op.like]: `%${search}%` } },
          ],
        }),
      },
      distinct: true,
      include: [
        {
          model: models.Level,
          as: "levelExercise",
          attributes: ["NAME_LEVEL"],
          include: [
            {
              model: models.Topic,
              as: "levelTopic",
              attributes: ["NAME_TOPIC"],
              include: [
                {
                  model: models.Section,
                  as: "topicSection",
                  attributes: ["NAME_SECTION"],
                },
              ],
            },
          ],
        },
        {
          model: models.MultipleChoices,
          as: "multipleChoices",
          attributes: ["ANSWER_KEY"],
        },
        {
          model: models.MatchingPairs,
          as: "matchingPairs",
          attributes: ["LEFT_PAIR", "RIGHT_PAIR"],
        },
        {
          model: models.TrueFalse,
          as: "trueFalse",
          attributes: ["IS_TRUE"],
        },
      ],
    });

    const formattedExercises = exercises.map((exercise) => {
      const questionType = exercise.QUESTION_TYPE;
      let answerKey = null;

      const nameSection =
        exercise.levelExercise?.levelTopic?.topicSection?.NAME_SECTION ||
        "Unknown Section";
      const nameTopic =
        exercise.levelExercise?.levelTopic?.NAME_TOPIC || "Unknown Topic";

      if (questionType === "MCQ" && exercise.multipleChoices.length > 0) {
        answerKey = exercise.multipleChoices[0].ANSWER_KEY;
      } else if (questionType === "MPQ" && exercise.matchingPairs.length > 0) {
        answerKey = exercise.matchingPairs
          .map((pair) => `${pair.LEFT_PAIR}-${pair.RIGHT_PAIR}`)
          .join(", ");
      } else if (questionType === "TFQ" && exercise.trueFalse.length > 0) {
        answerKey = exercise.trueFalse[0].IS_TRUE === 1 ? "true" : "false";
      }

      return {
        ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE,
        NAME_SECTION: nameSection,
        NAME_TOPIC: nameTopic,
        NAME_LEVEL: exercise.levelExercise.NAME_LEVEL,
        TITLE: exercise.TITLE,
        QUESTION: exercise.QUESTION,
        QUESTION_TYPE: questionType,
        ANSWER_KEY: answerKey,
        TIME_ADMIN_EXC: exercise.TIME_ADMIN_EXC,
      };
    });

    const filteredExercises = formattedExercises.filter(
      (exercise) =>
        exercise.NAME_SECTION !== "Unknown Section" &&
        exercise.NAME_TOPIC !== "Unknown Topic"
    );

    if (sort === "section") {
      filteredExercises.sort((a, b) => {
        return a.NAME_SECTION.localeCompare(b.NAME_SECTION);
      });
    } else if (sort === "topic") {
      filteredExercises.sort((a, b) => {
        return a.NAME_TOPIC.localeCompare(b.NAME_TOPIC);
      });
    } else if (sort === "level") {
      filteredExercises.sort((a, b) => {
        return a.NAME_LEVEL.localeCompare(b.NAME_LEVEL);
      });
    } else if (sort === "question") {
      filteredExercises.sort((a, b) => {
        return a.QUESTION.localeCompare(b.QUESTION);
      });
    } else if (sort === "answer") {
      filteredExercises.sort((a, b) => {
        return (a.ANSWER_KEY || "").localeCompare(b.ANSWER_KEY || "");
      });
    } else {
      filteredExercises.sort((a, b) => {
        return new Date(b.TIME_ADMIN_EXC) - new Date(a.TIME_ADMIN_EXC);
      });
    }

    const formattedSortedExercises = filteredExercises.map((exercise) => {
      const formattedTimeAdminExc = new Date(exercise.TIME_ADMIN_EXC)
        .toLocaleString("en-GB", {
          hour12: false,
          hour: "2-digit",
          minute: "2-digit",
          day: "2-digit",
          month: "2-digit",
          year: "numeric",
        })
        .replace(/(\d{2}\/\d{2}\/\d{4}), (\d{2}:\d{2})/, "$2 $1");

      return {
        ...exercise,
        TIME_ADMIN_EXC: formattedTimeAdminExc,
      };
    });

    const paginatedExercises = formattedSortedExercises.slice(
      (page - 1) * limit,
      page * limit
    );

    const totalPages = Math.ceil(formattedSortedExercises.length / limit);

    response(
      200,
      {
        exercises: paginatedExercises,
        currentPage: parseInt(page),
        totalPages: totalPages,
        totalExercises: formattedSortedExercises.length,
      },
      "Success",
      res
    );
  } catch (error) {
    console.log(error);
    response(500, null, "Error retrieving exercises data!", res);
  }
};

export const getExerciseByLevelId = async (req, res) => {
  try {
    const { idLevel } = req.params;

    const levelExists = await models.Level.findByPk(idLevel, {
      include: [
        {
          model: models.Topic,
          as: "levelTopic",
          attributes: ["NAME_TOPIC"],
          include: [
            {
              model: models.Section,
              as: "topicSection",
              attributes: ["NAME_SECTION"],
            },
          ],
        },
      ],
      attributes: ["NAME_LEVEL"],
    });

    if (!levelExists) {
      return response(404, null, "Level not found", res);
    }

    const exercises = await models.Exercise.findAll({
      where: { ID_LEVEL: idLevel, IS_DELETED: 0 },
      include: [
        { model: models.MultipleChoices, as: "multipleChoices" },
        { model: models.MatchingPairs, as: "matchingPairs" },
        { model: models.TrueFalse, as: "trueFalse" },
      ],
    });

    if (!exercises || exercises.length === 0) {
      return response(404, null, "No exercises found for this level", res);
    }

    const formattedExercises = exercises.map((exercise) => {
      const exerciseData = { ...exercise.dataValues };
      const questionType = exercise.QUESTION_TYPE;

      if (questionType === "MCQ") {
        if (exerciseData.multipleChoices) {
          exerciseData.multipleChoices = exerciseData.multipleChoices.map(
            (choice) => {
              const { ANSWER_KEY, ...rest } = choice.dataValues;
              return rest;
            }
          );
        }
        delete exerciseData.matchingPairs;
        delete exerciseData.trueFalse;
      } else if (questionType === "MPQ") {
        if (exerciseData.matchingPairs) {
          exerciseData.matchingPairs = exerciseData.matchingPairs.map(
            (pair) => pair.dataValues
          );
        }
        delete exerciseData.multipleChoices;
        delete exerciseData.trueFalse;
      } else if (questionType === "TFQ") {
        if (exerciseData.trueFalse) {
          exerciseData.trueFalse = exerciseData.trueFalse.map((tf) => {
            const { IS_TRUE, ...rest } = tf.dataValues;
            return rest;
          });
        }
        delete exerciseData.multipleChoices;
        delete exerciseData.matchingPairs;
      } else {
        delete exerciseData.multipleChoices;
        delete exerciseData.matchingPairs;
        delete exerciseData.trueFalse;
      }

      return exerciseData;
    });

    const responsePayload = {
      NAME_SECTION: levelExists.levelTopic.topicSection.NAME_SECTION,
      NAME_TOPIC: levelExists.levelTopic.NAME_TOPIC,
      NAME_LEVEL: levelExists.NAME_LEVEL,
      EXERCISES: formattedExercises,
    };

    response(200, responsePayload, "Success", res);
  } catch (error) {
    console.log(error);
    res.status(500).json({ message: "Internal Server Error" });
  }
};

export const getExerciseByLevelIdForAdmin = async (req, res) => {
  try {
    const { idLevel } = req.params;

    const levelExists = await models.Level.findByPk(idLevel, {
      include: [
        {
          model: models.Topic,
          as: "levelTopic",
          attributes: ["NAME_TOPIC"],
          include: [
            {
              model: models.Section,
              as: "topicSection",
              attributes: ["NAME_SECTION"],
            },
          ],
        },
      ],
      attributes: ["NAME_LEVEL"],
    });

    if (!levelExists) {
      return response(404, null, "Level not found", res);
    }

    const exercises = await models.Exercise.findAll({
      where: { ID_LEVEL: idLevel, IS_DELETED: 0 },
      include: [
        { model: models.MultipleChoices, as: "multipleChoices" },
        { model: models.MatchingPairs, as: "matchingPairs" },
        { model: models.TrueFalse, as: "trueFalse" },
      ],
      order: [
        [
          models.Sequelize.literal(
            "CAST(SUBSTRING_INDEX(TITLE, ' ', -1) AS UNSIGNED)"
          ),
          "ASC",
        ],
      ],
    });

    let formattedExercises = [];

    if (exercises && exercises.length > 0) {
      formattedExercises = exercises.map((exercise) => {
        const exerciseData = { ...exercise.dataValues };
        const questionType = exercise.QUESTION_TYPE;

        if (questionType === "MCQ") {
          if (exerciseData.multipleChoices) {
            exerciseData.multipleChoices = exerciseData.multipleChoices.map(
              (choice) => choice.dataValues
            );
          }
          delete exerciseData.matchingPairs;
          delete exerciseData.trueFalse;
        } else if (questionType === "MPQ") {
          if (exerciseData.matchingPairs) {
            exerciseData.matchingPairs = exerciseData.matchingPairs.map(
              (pair) => pair.dataValues
            );
          }
          delete exerciseData.multipleChoices;
          delete exerciseData.trueFalse;
        } else if (questionType === "TFQ") {
          if (exerciseData.trueFalse) {
            exerciseData.trueFalse = exerciseData.trueFalse.map(
              (tf) => tf.dataValues
            );
          }
          delete exerciseData.multipleChoices;
          delete exerciseData.matchingPairs;
        } else {
          delete exerciseData.multipleChoices;
          delete exerciseData.matchingPairs;
          delete exerciseData.trueFalse;
        }

        return exerciseData;
      });
    }

    const responsePayload = {
      NAME_SECTION: levelExists.levelTopic.topicSection.NAME_SECTION,
      NAME_TOPIC: levelExists.levelTopic.NAME_TOPIC,
      NAME_LEVEL: levelExists.NAME_LEVEL,
      EXERCISES: formattedExercises,
    };

    response(200, responsePayload, "Success", res);
  } catch (error) {
    console.log(error);
    res.status(500).json({ message: "Internal Server Error" });
  }
};

export const createExercises = async (req, res) => {
  const { exercises } = req.body;

  if (!exercises || !Array.isArray(exercises) || exercises.length === 0) {
    return response(400, null, "Exercises array is required", res);
  }

  const transaction = await models.db.transaction();

  try {
    const createdExercises = [];

    const levelId = exercises[0]?.ID_LEVEL;
    let lastExercise = await models.Exercise.findOne({
      where: { ID_LEVEL: levelId },
      order: [
        [
          models.Sequelize.literal(
            "CAST(SUBSTRING_INDEX(TITLE, ' ', -1) AS UNSIGNED)"
          ),
          "DESC",
        ],
      ],
      limit: 1,
    });

    let lastTitleNumber = 0;

    if (lastExercise && lastExercise.TITLE) {
      const lastTitleParts = lastExercise.TITLE.split(" ");
      lastTitleNumber = parseInt(lastTitleParts[1], 10) || 0;
    }

    for (let i = 0; i < exercises.length; i++) {
      const exerciseData = exercises[i];
      const { ID_LEVEL, TITLE, QUESTION, SCORE_WEIGHT, VIDEO, QUESTION_TYPE } =
        exerciseData;

      if (!ID_LEVEL) throw new Error("Level ID is required");
      if (!QUESTION) throw new Error("Question is required");
      if (!SCORE_WEIGHT) throw new Error("Score weight is required");

      const level = await models.Level.findByPk(ID_LEVEL);
      if (!level) throw new Error("Level not found");

      let generatedTitle = TITLE;
      if (!TITLE) {
        lastTitleNumber++;
        generatedTitle = `Question ${lastTitleNumber}`;
      }

      const existingExercise = await models.Exercise.findOne({
        where: { ID_LEVEL, TITLE: generatedTitle },
      });
      if (existingExercise) {
        throw new Error(
          `An exercise with the TITLE ${generatedTitle} already exists`
        );
      }

      const newExercise = await models.Exercise.create(
        {
          ID_LEVEL,
          TITLE: generatedTitle,
          QUESTION,
          SCORE_WEIGHT,
          QUESTION_TYPE,
          AUDIO: null,
          VIDEO: VIDEO || null,
          IMAGE: null,
        },
        { transaction }
      );

      const AUDIO = req.filesToSave[`AUDIO[${i}]`] || null;
      const IMAGE = req.filesToSave[`IMAGE[${i}]`] || null;

      const audioFilename = AUDIO
        ? saveFileToDisk(
            AUDIO,
            "AUDIO",
            ID_LEVEL,
            newExercise.ID_ADMIN_EXERCISE
          )
        : null;
      const imageFilename = IMAGE
        ? saveFileToDisk(
            IMAGE,
            "IMAGE",
            ID_LEVEL,
            newExercise.ID_ADMIN_EXERCISE
          )
        : null;

      newExercise.AUDIO = audioFilename;
      newExercise.IMAGE = imageFilename;
      await newExercise.save({ transaction });

      let questionDetails = null;
      switch (QUESTION_TYPE) {
        case "MCQ":
          const {
            OPTION_A,
            OPTION_B,
            OPTION_C,
            OPTION_D,
            OPTION_E,
            ANSWER_KEY,
          } = exerciseData;
          if (
            !OPTION_A ||
            !OPTION_B ||
            !OPTION_C ||
            !OPTION_D ||
            !OPTION_E ||
            !ANSWER_KEY
          ) {
            throw new Error(
              "All options and answer key are required for Multiple Choice"
            );
          }

          questionDetails = await models.MultipleChoices.create(
            {
              ID_ADMIN_EXERCISE: newExercise.ID_ADMIN_EXERCISE,
              OPTION_A,
              OPTION_B,
              OPTION_C,
              OPTION_D,
              OPTION_E,
              ANSWER_KEY,
            },
            { transaction }
          );
          break;

        case "MPQ":
          const { PAIRS } = exerciseData;
          if (!PAIRS || !Array.isArray(PAIRS) || PAIRS.length === 0) {
            throw new Error("At least one pair is required for Matching Pairs");
          }

          const matchingPairsPromises = PAIRS.map((pair) =>
            models.MatchingPairs.create(
              {
                ID_ADMIN_EXERCISE: newExercise.ID_ADMIN_EXERCISE,
                LEFT_PAIR: pair.LEFT_PAIR,
                RIGHT_PAIR: pair.RIGHT_PAIR,
              },
              { transaction }
            )
          );
          await Promise.all(matchingPairsPromises);
          questionDetails = PAIRS;
          break;

        case "TFQ":
          const { IS_TRUE } = exerciseData;
          if (typeof IS_TRUE === "undefined") {
            throw new Error("IS_TRUE is required for True/False");
          }

          questionDetails = await models.TrueFalse.create(
            {
              ID_ADMIN_EXERCISE: newExercise.ID_ADMIN_EXERCISE,
              IS_TRUE,
            },
            { transaction }
          );
          break;

        default:
          throw new Error("Unsupported question type");
      }

      newExercise.dataValues.questionDetails = questionDetails;
      createdExercises.push(newExercise);
    }

    await transaction.commit();

    response(201, createdExercises, "Exercises created successfully", res);
  } catch (error) {
    console.error(error);
    await transaction.rollback();
    response(500, null, error.message || "Internal Server Error", res);
  }
};

export const updateExerciseById = async (req, res) => {
  const { id } = req.params;
  const { ID_LEVEL, QUESTION, SCORE_WEIGHT, VIDEO, PAIRS } = req.body;
  const { IMAGE, AUDIO } = req.filesToSave || {};

  const transaction = await models.db.transaction();

  try {
    const exercise = await models.Exercise.findByPk(id, { transaction });

    if (!exercise) {
      clearFileBuffers({ IMAGE, AUDIO });
      await transaction.rollback();
      return response(404, null, "Exercise not found", res);
    }

    const { QUESTION_TYPE } = exercise;

    if (ID_LEVEL) {
      const level = await models.Level.findByPk(ID_LEVEL, { transaction });
      if (!level) {
        clearFileBuffers({ IMAGE, AUDIO });
        await transaction.rollback();
        return response(404, null, "Level not found", res);
      }
      exercise.ID_LEVEL = ID_LEVEL;
    }
    if (QUESTION) exercise.QUESTION = QUESTION;
    if (SCORE_WEIGHT) exercise.SCORE_WEIGHT = SCORE_WEIGHT;
    if (VIDEO) exercise.VIDEO = VIDEO;

    if (AUDIO) {
      if (exercise.AUDIO) {
        const oldAudioPath = path.join(
          "public/uploads/exercise/audio",
          exercise.AUDIO
        );
        if (fs.existsSync(oldAudioPath)) {
          fs.unlinkSync(oldAudioPath);
        }
      }
      exercise.AUDIO = saveFileToDisk(
        AUDIO,
        "AUDIO",
        ID_LEVEL || exercise.ID_LEVEL,
        exercise.ID_ADMIN_EXERCISE
      );
    }

    if (IMAGE) {
      if (exercise.IMAGE) {
        const oldImagePath = path.join(
          "public/uploads/exercise/image",
          exercise.IMAGE
        );
        if (fs.existsSync(oldImagePath)) {
          fs.unlinkSync(oldImagePath);
        }
      }
      exercise.IMAGE = saveFileToDisk(
        IMAGE,
        "IMAGE",
        ID_LEVEL || exercise.ID_LEVEL,
        exercise.ID_ADMIN_EXERCISE
      );
    }

    await exercise.save({ transaction });

    let payload = {
      ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE,
      ID_LEVEL: exercise.ID_LEVEL,
      QUESTION: exercise.QUESTION,
      SCORE_WEIGHT: exercise.SCORE_WEIGHT,
      QUESTION_TYPE: exercise.QUESTION_TYPE,
      AUDIO: exercise.AUDIO,
      VIDEO: exercise.VIDEO,
      IMAGE: exercise.IMAGE,
      IS_DELETED: exercise.IS_DELETED,
      TIME_ADMIN_EXC: exercise.TIME_ADMIN_EXC,
    };

    if (QUESTION_TYPE === "MPQ") {
      if (!Array.isArray(PAIRS) || PAIRS.length === 0) {
        await transaction.rollback();
        return response(
          400,
          null,
          "At least one pair is required for Matching Pairs",
          res
        );
      }

      const existingPairs = await models.MatchingPairs.findAll({
        where: { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE },
        transaction,
      });

      const pairsToDelete = new Set(
        existingPairs.map((pair) => pair.ID_MATCHING_PAIRS)
      );

      for (const pair of PAIRS) {
        if (pair.ID_MATCHING_PAIRS) {
          const existingPair = existingPairs.find(
            (p) => p.ID_MATCHING_PAIRS === pair.ID_MATCHING_PAIRS
          );
          if (existingPair) {
            existingPair.LEFT_PAIR = pair.LEFT_PAIR;
            existingPair.RIGHT_PAIR = pair.RIGHT_PAIR;
            await existingPair.save({ transaction });
            pairsToDelete.delete(existingPair.ID_MATCHING_PAIRS);
          }
        } else {
          await models.MatchingPairs.create(
            {
              ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE,
              LEFT_PAIR: pair.LEFT_PAIR,
              RIGHT_PAIR: pair.RIGHT_PAIR,
            },
            { transaction }
          );
        }
      }

      for (const pairId of pairsToDelete) {
        await models.MatchingPairs.destroy({
          where: { ID_MATCHING_PAIRS: pairId },
          transaction,
        });
      }

      const updatedPairs = await models.MatchingPairs.findAll({
        where: { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE },
      });
      payload.matchingPairs = updatedPairs;
    } else if (QUESTION_TYPE === "MCQ") {
      const { OPTION_A, OPTION_B, OPTION_C, OPTION_D, OPTION_E, ANSWER_KEY } =
        req.body;
      const multipleChoices = await models.MultipleChoices.findOne({
        where: { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE },
        transaction,
      });

      if (!multipleChoices) {
        await transaction.rollback();
        return response(404, null, "Multiple Choices not found", res);
      }

      if (OPTION_A) multipleChoices.OPTION_A = OPTION_A;
      if (OPTION_B) multipleChoices.OPTION_B = OPTION_B;
      if (OPTION_C) multipleChoices.OPTION_C = OPTION_C;
      if (OPTION_D) multipleChoices.OPTION_D = OPTION_D;
      if (OPTION_E) multipleChoices.OPTION_E = OPTION_E;
      if (ANSWER_KEY) multipleChoices.ANSWER_KEY = ANSWER_KEY;

      await multipleChoices.save({ transaction });
      payload.multipleChoices = multipleChoices;
    } else if (QUESTION_TYPE === "TFQ") {
      const { IS_TRUE } = req.body;
      const trueFalse = await models.TrueFalse.findOne({
        where: { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE },
        transaction,
      });

      if (!trueFalse) {
        await transaction.rollback();
        return response(404, null, "True/False exercise not found", res);
      }

      if (typeof IS_TRUE !== "undefined") {
        trueFalse.IS_TRUE = IS_TRUE;
      }

      await trueFalse.save({ transaction });
      payload.trueFalse = trueFalse;
    }

    await transaction.commit();

    response(200, payload, "Exercise updated successfully", res);
  } catch (error) {
    console.error(error);
    clearFileBuffers({ IMAGE, AUDIO });
    await transaction.rollback();
    response(500, null, "Internal Server Error", res);
  }
};

export const updateExerciseTitle = async (req, res) => {
  const { ID_LEVEL, exercises } = req.body;
  const transaction = await models.db.transaction();

  try {
    const existingExercises = await models.Exercise.findAll({
      where: {
        IS_DELETED: 0,
        ID_LEVEL,
      },
      order: [
        [
          models.Sequelize.literal(
            "CAST(SUBSTRING_INDEX(TITLE, ' ', -1) AS UNSIGNED)"
          ),
          "ASC",
        ],
      ],
      transaction,
    });

    if (exercises.length !== existingExercises.length) {
      await transaction.rollback();
      return response(400, null, "Mismatch in exercise count", res);
    }

    const sortedTitles = existingExercises.map((ex) => ex.TITLE);
    const updatedExercises = [];

    for (let i = 0; i < exercises.length; i++) {
      const { ID_ADMIN_EXERCISE } = exercises[i];
      const newTitle = sortedTitles[i];

      const exerciseToUpdate = await models.Exercise.findByPk(
        ID_ADMIN_EXERCISE,
        {
          transaction,
        }
      );

      if (!exerciseToUpdate) {
        await transaction.rollback();
        return response(
          404,
          null,
          `Exercise with ID ${ID_ADMIN_EXERCISE} not found`,
          res
        );
      }

      if (exerciseToUpdate.TITLE !== newTitle) {
        await exerciseToUpdate.update({ TITLE: newTitle }, { transaction });

        updatedExercises.push({
          ID_ADMIN_EXERCISE: exerciseToUpdate.ID_ADMIN_EXERCISE,
          TITLE: newTitle,
          oldTitle: exercises[i].TITLE,
        });
      }
    }

    await transaction.commit();

    response(
      200,
      { updatedExercises },
      "Exercises titles swapped successfully",
      res
    );
  } catch (error) {
    console.error(error);

    await transaction.rollback();
    response(500, null, "Internal Server Error", res);
  }
};

export const deleteExerciseById = async (req, res) => {
  const { id } = req.params;
  const transaction = await models.db.transaction();

  try {
    const exercise = await models.Exercise.findByPk(id);

    if (!exercise) {
      await transaction.rollback();
      return response(404, null, "Exercise not found", res);
    }

    await exercise.update({ IS_DELETED: 1 }, { transaction });

    await transaction.commit();
    response(200, null, "Exercise soft-deleted successfully", res);
  } catch (error) {
    console.log(error);
    await transaction.rollback();
    response(500, null, "Internal Server Error", res);
  }
};

export const deleteExerciseFileById = async (req, res) => {
  const { id } = req.params;
  const { fileType } = req.body;

  if (!["audio", "image", "video"].includes(fileType)) {
    return response(400, null, "Invalid file type specified", res);
  }

  try {
    const exercise = await models.Exercise.findByPk(id);

    if (!exercise) {
      return response(404, null, "Exercise not found", res);
    }

    let filePath;
    let fileName;

    if (fileType === "audio" && exercise.AUDIO) {
      fileName = exercise.AUDIO;
      filePath = path.join("public/uploads/exercise/audio", fileName);
      exercise.AUDIO = null;
    } else if (fileType === "image" && exercise.IMAGE) {
      fileName = exercise.IMAGE;
      filePath = path.join("public/uploads/exercise/image", fileName);
      exercise.IMAGE = null;
    } else if (fileType === "video" && exercise.VIDEO) {
      exercise.VIDEO = null;
    } else {
      return response(
        404,
        null,
        `${
          fileType.charAt(0).toUpperCase() + fileType.slice(1)
        } file not found`,
        res
      );
    }

    if (fs.existsSync(filePath)) {
      fs.unlinkSync(filePath);
    }

    await exercise.save();

    response(
      200,
      exercise,
      `${
        fileType.charAt(0).toUpperCase() + fileType.slice(1)
      } file deleted successfully`,
      res
    );
  } catch (error) {
    console.log(error);
    response(500, null, "Internal Server Error", res);
  }
};
