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

export const createMatchingPairsExercise = async (req, res) => {
  const { ID_LEVEL, TITLE, QUESTION, SCORE_WEIGHT, VIDEO } = req.body;
  let PAIRS = req.body.PAIRS;

  try {
    if (typeof PAIRS === "string") {
      PAIRS = JSON.parse(PAIRS);
    }
  } catch (error) {
    return response(400, null, "Invalid PAIRS format", res);
  }

  const { IMAGE, AUDIO } = req.filesToSave || {};

  if (!ID_LEVEL) return response(400, null, "Level ID is required", res);
  if (!QUESTION) return response(400, null, "Question is required", res);
  if (!PAIRS || !Array.isArray(PAIRS) || PAIRS.length === 0)
    return response(400, null, "At least one pair is required", res);
  if (!SCORE_WEIGHT)
    return response(400, null, "Score weight is required", res);

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

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

    let generatedTitle = TITLE;

    if (!TITLE) {
      const lastExercise = await models.Exercise.findOne({
        where: { ID_LEVEL },
        order: [["TITLE", "DESC"]],
        limit: 1,
      });

      if (lastExercise && lastExercise.TITLE) {
        const lastTitleNumber = parseInt(lastExercise.TITLE.split(" ")[1], 10);
        generatedTitle = `Soal ${lastTitleNumber + 1}`;
      } else {
        generatedTitle = `Soal 1`;
      }
    }

    const existingExercise = await models.Exercise.findOne({
      where: { ID_LEVEL, TITLE: generatedTitle },
    });
    if (existingExercise) {
      clearFileBuffers({ IMAGE, AUDIO });
      return response(
        400,
        null,
        "An exercise with the same title already exists in this level",
        res
      );
    }

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

    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 });

    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 }
      )
    );

    const newMatchingPairs = await Promise.all(matchingPairsPromises);

    await transaction.commit();

    const payload = {
      exercise: newExercise,
      matchingPairs: newMatchingPairs,
    };

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

export const updateMatchingPairsExerciseById = async (req, res) => {
  const { id } = req.params;
  const { ID_LEVEL, QUESTION, SCORE_WEIGHT, VIDEO } = req.body;
  let PAIRS = req.body.PAIRS;

  try {
    if (typeof PAIRS === "string") {
      PAIRS = JSON.parse(PAIRS);
    }
  } catch (error) {
    return response(400, null, "Invalid PAIRS format", res);
  }

  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);
    }

    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 });

    if (PAIRS && Array.isArray(PAIRS)) {
      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,
        });
      }
    }

    await transaction.commit();

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

    const payload = {
      exercise,
      matchingPairs: updatedPairs,
    };

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