import response from "../../response.js";
import models from "../../models/index.js";
import { autoGenerateLevel } from "../../middlewares/Level/autoGenerateLevel.js";

export const getTopics = async (req, res) => {
  try {
    const topics = await models.Topic.findAll({
      where: {
        IS_DELETED: 0,
      },
    });
    response(200, topics, "Success", res);
  } catch (error) {
    console.log(error);
    response(500, null, "Error retrieving topics data!", res);
  }
};

export const getTopicById = async (req, res) => {
  try {
    const { id } = req.params;
    const topic = await models.Topic.findByPk(id);

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

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

export const getTopicBySectionId = async (req, res) => {
  try {
    const { sectionId } = req.params;
    const userId = req.user.ID;

    const sectionExists = await models.Section.findByPk(sectionId);
    if (!sectionExists) {
      return response(404, null, "Section not found", res);
    }

    const topics = await models.Topic.findAll({
      where: { ID_SECTION: sectionId, IS_DELETED: 0 },
      attributes: ["ID_TOPIC", "NAME_TOPIC", "DESCRIPTION_TOPIC"],
    });

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

    const topicsWithCompletionStatus = await Promise.all(
      topics.map(async (topic) => {
        const level6 = await models.Level.findOne({
          where: {
            NAME_LEVEL: "Level 6",
            ID_TOPIC: topic.ID_TOPIC,
          },
        });

        let isCompleted = 0;
        if (level6) {
          const stdLearning = await models.StdLearning.findOne({
            where: {
              ID: userId,
              ID_LEVEL: level6.ID_LEVEL,
              IS_PASS: 1,
            },
          });
          isCompleted = stdLearning ? 1 : 0;
        }

        return {
          ...topic.get({ plain: true }),
          IS_COMPLETED: isCompleted,
        };
      })
    );

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

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

  try {
    const { count, rows: topics } = await models.Topic.findAndCountAll({
      where: {
        IS_DELETED: 0,
        ...(search && {
          [models.Op.or]: [
            {
              "$topicSection.NAME_SECTION$": {
                [models.Op.like]: `%${search}%`,
              },
            },
            {
              NAME_TOPIC: {
                [models.Op.like]: `%${search}%`,
              },
            },
            {
              DESCRIPTION_TOPIC: {
                [models.Op.like]: `%${search}%`,
              },
            }
          ],
        }),
      },
      attributes: ["ID_TOPIC", "NAME_TOPIC", "DESCRIPTION_TOPIC", "TIME_TOPIC"],
      include: [
        {
          model: models.Section,
          as: "topicSection",
          attributes: ["ID_SECTION", "NAME_SECTION"],
        },
      ],
      distinct: true,
    });

    const formattedTopics = topics.map((topic) => ({
      ID_SECTION: topic.topicSection.ID_SECTION,
      ID_TOPIC: topic.ID_TOPIC,
      NAME_SECTION: topic.topicSection.NAME_SECTION,
      NAME_TOPIC: topic.NAME_TOPIC,
      DESCRIPTION_TOPIC: topic.DESCRIPTION_TOPIC,
      TIME_TOPIC: topic.TIME_TOPIC,
    }));

    if (sort === "section") {
      formattedTopics.sort((a, b) =>
        a.NAME_SECTION.localeCompare(b.NAME_SECTION)
      );
    } else if (sort === "topic") {
      formattedTopics.sort((a, b) => a.NAME_TOPIC.localeCompare(b.NAME_TOPIC));
    } else {
      formattedTopics.sort(
        (a, b) => new Date(b.TIME_TOPIC) - new Date(a.TIME_TOPIC)
      );
    }

    const paginatedTopics = formattedTopics.slice(
      (page - 1) * limit,
      page * limit
    );

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

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

export const createTopic = async (req, res) => {
  const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC } = req.body;

  if (!ID_SECTION) {
    return response(400, null, "Section ID is required", res);
  }

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

  if (!DESCRIPTION_TOPIC) {
    return response(400, null, "Topic description is required", res);
  }

  try {
    const section = await models.Section.findByPk(ID_SECTION);
    if (!section) {
      return response(404, null, "Section not found", res);
    }

    const existingTopic = await models.Topic.findOne({
      where: {
        ID_SECTION,
        NAME_TOPIC,
        IS_DELETED: 0,
      },
    });

    if (existingTopic) {
      return response(
        409,
        null,
        "Topic with the same name already exists",
        res
      );
    }

    const newTopic = await models.Topic.create({
      ID_SECTION,
      NAME_TOPIC,
      DESCRIPTION_TOPIC,
    });

    req.body.ID_TOPIC = newTopic.ID_TOPIC;

    await autoGenerateLevel(req, res, async () => {
      const levels = res.locals.createdLevels || [];

      const payload = {
        topic: newTopic,
        levels,
      };

      response(
        201,
        payload,
        "Topic and related Level created successfully",
        res
      );
    });
  } catch (error) {
    console.log(error);
    response(500, null, "Internal Server Error", res);
  }
};

export const updateTopicById = async (req, res) => {
  const { id } = req.params;
  const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC } = req.body;

  try {
    const topic = await models.Topic.findByPk(id);

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

    if (ID_SECTION) {
      const section = await models.Section.findByPk(ID_SECTION);
      if (!section) {
        return response(404, null, "Section not found", res);
      }

      if (topic.ID_SECTION !== ID_SECTION) {
        await models.Level.update(
          { ID_SECTION },
          {
            where: { ID_TOPIC: topic.ID_TOPIC },
          }
        );
      }

      topic.ID_SECTION = ID_SECTION;
    }

    if (NAME_TOPIC) {
      topic.NAME_TOPIC = NAME_TOPIC;
    }

    if (DESCRIPTION_TOPIC) {
      topic.DESCRIPTION_TOPIC = DESCRIPTION_TOPIC;
    }

    await topic.save();

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

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

  try {
    const topic = await models.Topic.findByPk(id);

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

    topic.IS_DELETED = 1;
    await topic.save();

    await models.Level.update({ IS_DELETED: 1 }, { where: { ID_TOPIC: id } });

    await models.Exercise.update(
      { IS_DELETED: 1 },
      {
        where: {
          ID_LEVEL: {
            [models.Op.in]: models.Sequelize.literal(
              `(SELECT ID_LEVEL FROM level WHERE ID_TOPIC = '${id}')`
            ),
          },
        },
      }
    );

    response(
      200,
      null,
      "Topic, levels, and related exercises soft deleted successfully",
      res
    );
  } catch (error) {
    console.log(error);
    response(500, null, "Internal Server Error", res);
  }
};

export const getCompletedTopicsBySection = async (req, res) => {
  try {
    const user = req.user;
    const userId = user.ID;

    const userLearnings = await models.StdLearning.findAll({
      where: { ID: userId },
      include: [
        {
          model: models.Level,
          as: "level",
          include: [
            {
              model: models.Topic,
              as: "levelTopic",
              attributes: ["ID_TOPIC", "NAME_TOPIC", "DESCRIPTION_TOPIC"],
              include: [
                {
                  model: models.Section,
                  as: "topicSection",
                  attributes: [
                    "ID_SECTION",
                    "NAME_SECTION",
                    "DESCRIPTION_SECTION",
                    "THUMBNAIL",
                  ],
                },
              ],
            },
          ],
        },
      ],
    });

    if (!userLearnings.length) {
      return response(200, null, "No topics found for this user", res);
    }

    const completedSections = {};

    for (const learning of userLearnings) {
      const { level } = learning;
      const { levelTopic: topic } = level;
      const { topicSection: section } = topic;

      const totalTopicsInSection = await models.Topic.count({
        where: { ID_SECTION: section.ID_SECTION },
      });

      if (!completedSections[section.ID_SECTION]) {
        completedSections[section.ID_SECTION] = {
          ID_SECTION: section.ID_SECTION,
          NAME_SECTION: section.NAME_SECTION,
          DESCRIPTION_SECTION: section.DESCRIPTION_SECTION,
          THUMBNAIL: section.THUMBNAIL,
          TOTAL_TOPICS: totalTopicsInSection,
          COMPLETED_TOPICS: 0,
        };
      }

      const level6 = await models.Level.findOne({
        where: {
          NAME_LEVEL: "Level 6",
          ID_TOPIC: topic.ID_TOPIC,
        },
      });

      if (
        level6 &&
        level.ID_LEVEL === level6.ID_LEVEL &&
        learning.IS_PASS === 1
      ) {
        completedSections[section.ID_SECTION].COMPLETED_TOPICS++;
      }
    }

    const result = Object.values(completedSections).map((section) => {
      section.COMPLETED_TOPICS = section.COMPLETED_TOPICS || 0;
      return section;
    });

    response(
      200,
      result,
      "Completed topics by section fetched successfully",
      res
    );
  } catch (error) {
    console.error(error);
    response(500, null, "Internal Server Error", res);
  }
};
