import response from "../../response.js";
import models from "../../models/index.js";
import { updateMonitoringClass } from "./monitoring.js";

export const getClasses = async (req, res) => {
  try {
    const classes = await models.Class.findAll();
    response(200, classes, "Success", res);
  } catch (error) {
    console.log(error);
    response(500, null, "Error retrieving classes data!", res);
  }
};

export const getClassById = async (req, res) => {
  try {
    const { id } = req.params;
    const classes = await models.Class.findByPk(id);

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

    response(200, classes, "Success", res);
  } catch (error) {
    console.log(error);
    response(500, null, "Internal Server Error", res);
  }
};

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

  try {
    const { count, rows: classes } = await models.Class.findAndCountAll({
      where: {
        ...(search && {
          [models.Op.or]: [
            {
              NAME_CLASS: {
                [models.Op.like]: `%${search}%`,
              },
            },
            models.Sequelize.where(
              models.Sequelize.literal(
                `(SELECT COUNT(*) FROM student WHERE student.ID_CLASS = class.ID_CLASS)`
              ),
              {
                [models.Op.like]: `%${search}%`,
              }
            ),
            {
              TOTAL_STUDENT: {
                [models.Op.like]: `%${search}%`,
              },
            },
          ],
        }),
      },
      attributes: [
        "ID_CLASS",
        "NAME_CLASS",
        "TOTAL_STUDENT",
        "TIME_CLASS",
        [
          models.Sequelize.literal(
            `(SELECT COUNT(*) FROM student WHERE student.ID_CLASS = class.ID_CLASS)`
          ),
          "STUDENTS",
        ],
      ],
      distinct: true,
    });

    const formattedClasses = classes.map((classItem) => ({
      ID_CLASS: classItem.ID_CLASS,
      NAME_CLASS: classItem.NAME_CLASS,
      STUDENTS: classItem.get("STUDENTS"),
      TOTAL_STUDENT: classItem.TOTAL_STUDENT,
      TIME_CLASS: classItem.TIME_CLASS,
    }));

    if (sort === "class") {
      formattedClasses.sort((a, b) => a.NAME_CLASS.localeCompare(b.NAME_CLASS));
    } else if (sort === "total") {
      formattedClasses.sort((a, b) => a.TOTAL_STUDENT - b.TOTAL_STUDENT);
    } else if (sort === "students") {
      formattedClasses.sort((a, b) => a.STUDENTS - b.STUDENTS);
    } else {
      formattedClasses.sort(
        (a, b) => new Date(b.TIME_CLASS) - new Date(a.TIME_CLASS)
      );
    }

    const paginatedClasses = formattedClasses.slice(
      (page - 1) * limit,
      page * limit
    );

    const totalPages = Math.ceil(count / limit);
    const currentPage = parseInt(page);

    response(
      200,
      {
        classes: paginatedClasses,
        currentPage,
        totalPages,
        totalItems: count,
      },
      "Classes retrieved successfully",
      res
    );
  } catch (error) {
    console.log(error);
    response(500, null, "Error retrieving classes data!", res);
  }
};

export const createClass = async (req, res) => {
  const { NAME_CLASS, TOTAL_STUDENT } = req.body;

  if (!NAME_CLASS) {
    return response(400, null, "Class name is required", res);
  }

  if (!TOTAL_STUDENT) {
    return response(400, null, "Total student is required", res);
  }

  try {
    const newClass = await models.Class.create({
      NAME_CLASS,
      TOTAL_STUDENT,
    });

    response(201, newClass, "Class created successfully", res);
  } catch (error) {
    console.log(error);
    response(500, null, "Internal Server Error", res);
  }
};

export const updateClassById = async (req, res) => {
  const { id } = req.params;
  const { NAME_CLASS, TOTAL_STUDENT } = req.body;

  try {
    const classes = await models.Class.findByPk(id);

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

    if (NAME_CLASS) classes.NAME_CLASS = NAME_CLASS;
    if (TOTAL_STUDENT) classes.TOTAL_STUDENT = TOTAL_STUDENT;

    await classes.save();

    response(200, classes, "Class updated successfully", res);
  } catch (error) {
    console.log(error);
    response(500, null, "Internal Server Error", res);
  }
};

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

  try {
    const classes = await models.Class.findByPk(id);

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

    await models.Student.update(
      { ID_CLASS: null },
      {
        where: { ID_CLASS: id },
      }
    );

    await models.Monitoring.update(
      { ID_CLASS: null },
      {
        where: { ID_CLASS: id },
      }
    );

    await classes.destroy();

    response(
      200,
      null,
      "Class deleted successfully and related models updated",
      res
    );
  } catch (error) {
    console.log(error);
    response(500, null, "Internal Server Error", res);
  }
};

export const updateStudentClassByName = async (req, res) => {
  const { NAME_CLASS, STUDENTS } = req.body;

  if (!NAME_CLASS || !Array.isArray(STUDENTS) || STUDENTS.length === 0) {
    return response(
      400,
      null,
      "Class name and students data are required",
      res
    );
  }

  try {
    const classRecord = await models.Class.findOne({
      where: { NAME_CLASS },
    });

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

    const updateResults = [];
    let hasError = false;
    let successCount = 0;
    let failureCount = 0;

    for (const { NAME_USERS, NISN } of STUDENTS) {
      if (!NAME_USERS || !NISN) {
        updateResults.push({
          NAME_USERS,
          NISN,
          error: "User name and NISN are required for each student",
        });
        hasError = true;
        failureCount++;
        continue;
      }

      try {
        const student = await models.Student.findOne({
          include: [
            {
              model: models.User,
              as: "studentUser",
              where: { NAME_USERS },
            },
          ],
          where: { NISN },
        });

        if (!student) {
          updateResults.push({
            NAME_USERS,
            NISN,
            error: "Student with the given name and NISN not found",
          });
          hasError = true;
          failureCount++;
          continue;
        }

        if (student.ID_CLASS === classRecord.ID_CLASS) {
          updateResults.push({
            NAME_USERS,
            NISN,
            error: "Student is already in the selected class",
          });
          hasError = true;
          failureCount++;
          continue;
        }

        student.ID_CLASS = classRecord.ID_CLASS;
        await student.save();

        const stdLearningRecords = await models.StdLearning.findAll({
          where: { ID: student.ID },
          include: [
            {
              model: models.Level,
              as: "level",
              where: { NAME_LEVEL: "Level 6" },
            },
          ],
        });

        const studentUpdateResults = [];

        for (const stdLearning of stdLearningRecords) {
          try {
            const result = await updateMonitoringClass({
              ID_STUDENT_LEARNING: stdLearning.ID_STUDENT_LEARNING,
              ID_CLASS: classRecord.ID_CLASS,
            });
            studentUpdateResults.push(result);
          } catch (error) {
            console.error("Error updating monitoring class:", error.message);
            studentUpdateResults.push({ error: error.message });
          }
        }

        updateResults.push({
          NAME_USERS,
          NISN,
          message:
            "Student's class and related monitoring updated successfully",
          studentUpdateResults,
        });

        successCount++;
      } catch (error) {
        console.error("Error processing student:", error.message);
        updateResults.push({
          NAME_USERS,
          NISN,
          error: "Error processing student",
          details: error.message,
        });
        hasError = true;
        failureCount++;
      }
    }

    let responseMessage = "";

    if (failureCount === STUDENTS.length) {
      responseMessage = "Failed to update all students.";
    } else if (successCount > 0 && failureCount > 0) {
      responseMessage =
        "Some students updated successfully, but there were errors with others.";
    } else if (successCount === STUDENTS.length) {
      responseMessage = "All students updated successfully.";
    }

    return response(200, { updateResults }, responseMessage, res);
  } catch (error) {
    console.error(error);
    response(500, null, "Internal Server Error", res);
  }
};

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

    const studentsInClass = await models.User.findAll({
      where: {
        ROLE: "student",
      },
      attributes: ["ID", "NAME_USERS", "EMAIL", "ROLE"],
      include: [
        {
          model: models.Student,
          as: "students",
          attributes: ["NISN", "ID_CLASS"],
          include: [
            {
              model: models.Class,
              as: "studentClass",
              attributes: ["NAME_CLASS"],
            },
          ],
          where: {
            ID_CLASS: classId,
          },
        },
      ],
      raw: true,
      nest: true,
    });

    const formattedStudents = studentsInClass.map((student) => ({
      ID: student.ID,
      NAME_USERS: student.NAME_USERS,
      EMAIL: student.EMAIL,
      NISN: student.students.NISN,
      NAME_CLASS: student.students.studentClass.NAME_CLASS,
      ROLE: student.ROLE,
    }));

    response(200, formattedStudents, "Success", res);
  } catch (error) {
    console.log(error);
    response(500, null, "Error retrieving students by class ID", res);
  }
};

export const getStudentsWithNoClass = async (req, res) => {
  try {
    const studentsWithoutClass = await models.User.findAll({
      where: {
        ROLE: "student",
      },
      attributes: ["ID", "NAME_USERS", "EMAIL", "ROLE"],
      include: [
        {
          model: models.Student,
          as: "students",
          attributes: ["NISN", "ID_CLASS"],
          include: [
            {
              model: models.Class,
              as: "studentClass",
              attributes: ["NAME_CLASS"],
            },
          ],
          where: {
            ID_CLASS: null,
          },
        },
      ],
      raw: true,
      nest: true,
    });

    const formattedStudents = studentsWithoutClass.map((student) => ({
      ID: student.ID,
      NAME_USERS: student.NAME_USERS,
      EMAIL: student.EMAIL,
      NISN: student.students.NISN,
      NAME_CLASS: student.students.studentClass.NAME_CLASS,
      ROLE: student.ROLE,
    }));

    response(200, formattedStudents, "Success", res);
  } catch (error) {
    console.log(error);
    response(500, null, "Error retrieving students with no class", res);
  }
};
