const prisma = require("../lib/prisma");
const generateSubjectThumbnail = require("../utils/generateSubjectThumbnail");
const { notifyUser, notifyManyUsers } = require("../services/notification.service");

//create subject (admin only)
exports.createSubject = async (req, res) => {
  try {
    const { title, description, price, teacherId } = req.body;

    // validate teacherId
    const teacherIdNum = parseInt(teacherId);
    if (!teacherIdNum) {
      return res
        .status(400)
        .json({ message: "teacherId is required and must be a valid number" });
    }

    // check teacher existence
    const teacher = await prisma.user.findUnique({
      where: { id: teacherIdNum },
    });

    if (!teacher) {
      return res.status(400).json({ message: "Teacher does not exist" });
    }

    if (teacher.role !== "teacher") {
      return res
        .status(400)
        .json({ message: "Selected user is not a teacher" });
    }

    // ✅ GENERATE THUMBNAIL
    const thumbnailUrl = await generateSubjectThumbnail(
      {
        title,
        teacherName: `${teacher.firstName} ${teacher.lastName}`,
        price: parseInt(price),
        enrollment: 0, // default 0 for new subject
      },
      req,
      { mode: "light" } // or "dark"
    );

    const subject = await prisma.subject.create({
      data: {
        title,
        description,
        price: parseInt(price),
        teacherId: teacherIdNum,
        thumbnailUrl, // ✅ saved
      },
      include: {
        teacher: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            phone: true,
          },
        },
      },
    });

    const message = `You have been assigned a new subject: "${title}"`;
    await notifyUser(req, teacherIdNum, message, null);

    return res.json({ message: "Subject created successfully", subject });
  } catch (err) {
    console.error(err);
    return res.status(500).json({ message: "Server error" });
  }
};

//update subject (admin)
exports.updateSubject = async (req, res) => {
  try {
    const id = parseInt(req.params.id);
    const { name, description, price, teacherId } = req.body;

    // Fetch the old subject first
    const oldSubject = await prisma.subject.findUnique({
      where: { id },
      include: { teacher: true },
    });

    if (!oldSubject) {
      return res.status(404).json({ message: "Subject not found" });
    }

    const data = {};
    if (name) data.title = name;
    if (description) data.description = description;
    if (price) data.price = parseInt(price);

    let newTeacherId = oldSubject.teacherId;

    if (teacherId) {
      const teacher = await prisma.user.findUnique({
        where: { id: parseInt(teacherId) },
      });

      if (!teacher) {
        return res.status(400).json({ message: "Teacher does not exist" });
      }

      if (teacher.role !== "teacher") {
        return res
          .status(400)
          .json({ message: "Selected user is not a teacher" });
      }

      data.teacherId = parseInt(teacherId);
      newTeacherId = parseInt(teacherId);
    }

    // Update the subject
    const updated = await prisma.subject.update({
      where: { id },
      data,
      include: { teacher: true },
    });

    // 🔔 Notifications
    // 1️⃣ Notify new teacher if assigned or changed
    if (oldSubject.teacherId !== newTeacherId) {
      const message = `You have been assigned to the subject "${updated.title}"`;
      await notifyUser(req, newTeacherId, message, null);
    }

    // 2️⃣ Notify old teacher if teacher changed
    if (teacherId && oldSubject.teacherId !== newTeacherId) {
      const message = `You are no longer assigned to the subject "${updated.title}"`;
      await notifyUser(req, oldSubject.teacherId, message, null);
    }

    // 3️⃣ Optional: Notify teacher if other important fields changed
    const otherChanges =
      (name && name !== oldSubject.title) ||
      (description && description !== oldSubject.description) ||
      (price && parseInt(price) !== oldSubject.price);

    if (otherChanges) {
      const message = `Subject "${updated.title}" details have been updated`;
      await notifyUser(req, newTeacherId, message, null);
    }

    return res.json({ message: "Subject updated successfully", updated });
  } catch (err) {
    console.log(err);
    res.status(500).json({ message: "Server error" });
  }
};

//get all subject (admin)
exports.getAllSubjects = async (req, res) => {
  try {
    const page = Number(req.query.page) || 0;
    const pageSize = Number(req.query.pageSize) || 10;

    // ===== Sorting =====
    let orderBy = [];
    if (req.query.sort) {
      try {
        const sortModel = JSON.parse(req.query.sort);
        orderBy = sortModel
          .filter(({ field }) =>
            [
              "id",
              "title",
              "description",
              "price",
              "totalLessons",
              "sno",
            ].includes(field)
          )
          .map(({ field, sort }) => {
            if (field === "sno") return { id: sort.toLowerCase() }; // sort by id for sno
            return { [field]: sort.toLowerCase() };
          });
      } catch (err) {
        console.error("Invalid sort JSON", err);
      }
    }
    if (orderBy.length === 0) orderBy = [{ id: "asc" }];

    // ===== Filtering =====
    const where = {};
    if (req.query.filter) {
      try {
        const filterModel = JSON.parse(req.query.filter);
        const AND = [];

        filterModel.items?.forEach(({ field, operator, value }) => {
          if (!value) return;

          switch (field) {
            case "title":
            case "description":
              if (operator === "contains") {
                AND.push({ [field]: { contains: value, mode: "insensitive" } });
              }
              break;
            case "sno":
              if (!isNaN(Number(value))) {
                // sno corresponds to index in paginated results
                const snoNum = Number(value);
                AND.push({ id: snoNum }); // filter by id for sno
              }
              break;
            case "price":
              if (operator === "equals") {
                AND.push({ price: Number(value) });
              }
              break;
          }
        });

        if (AND.length > 0) where.AND = AND;
      } catch (err) {
        console.error("Invalid filter JSON", err);
      }
    }

    // ===== Fetch total count and paginated subjects =====
    const [subjects, total] = await Promise.all([
      prisma.subject.findMany({
        where,
        skip: page * pageSize,
        take: pageSize,
        orderBy,
        include: {
          teacher: {
            select: {
              firstName: true,
              lastName: true,
            },
          },
          lessons: true,
        },
      }),
      prisma.subject.count({ where }),
    ]);

    // ===== Format results =====
    const formatted = subjects.map((s, index) => ({
      ...s,
      sno: page * pageSize + index + 1,
      teacherName: s.teacher
        ? `${s.teacher.firstName} ${s.teacher.lastName}`
        : "",
      totalLessons: s.lessons ? s.lessons.length : 0,
      thumbnailUrl: s.thumbnailUrl || "",
    }));

    res.json({ rows: formatted, total });
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: "Server error" });
  }
};

exports.getSubjectById = async (req, res) => {
  const { id } = req.params;

  try {
    const subject = await prisma.subject.findUnique({
      where: { id: Number(id) },
      include: {
        teacher: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
          },
        },
      },
    });

    if (!subject) {
      return res.status(404).json({ message: "Subject not found" });
    }

    // Include teacherName in response
    const subjectWithTeacherName = {
      ...subject,
      teacherName: `${subject.teacher.firstName} ${subject.teacher.lastName}`,
    };

    return res.json(subjectWithTeacherName);
  } catch (err) {
    console.error(err);
    return res.status(500).json({ message: "Server error" });
  }
};

//get subject for teacher
exports.getTeacherSubjects = async (req, res) => {
  try {
    const page = parseInt(req.query.page) || 0;
    const pageSize = parseInt(req.query.pageSize) || 10;

    // ===== Sorting =====
    let orderBy = [];

    if (req.query.sort) {
      try {
        const sortModel = JSON.parse(req.query.sort);

        orderBy = sortModel
          .filter(({ field }) =>
            [
              "id",
              "title",
              "description",
              "price",
              "totalLessons",
              "sno",
            ].includes(field)
          )
          .map(({ field, sort }) => {
            if (field === "sno") {
              return { id: sort.toLowerCase() };
            }
            return { [field]: sort.toLowerCase() };
          });
      } catch (err) {
        console.error("Invalid sort JSON", err);
      }
    }

    if (orderBy.length === 0) orderBy = [{ id: "asc" }];

    // ===== Filtering =====
    const where = { teacherId: req.user.id };
    if (req.query.filter) {
      try {
        const filterModel = JSON.parse(req.query.filter); // MUI filter object
        const AND = [];

        filterModel.items?.forEach((filter) => {
          const { field, operator, value } = filter;
          if (!value) return;

          if (!["title"].includes(field)) return;

          switch (operator) {
            case "contains":
              AND.push({ [field]: { contains: value, mode: "insensitive" } });
              break;
          }
        });

        if (AND.length > 0) where.AND = AND;
      } catch (err) {
        console.error("Invalid filter JSON", err);
      }
    }

    // ===== Count total for pagination =====
    const total = await prisma.subject.count({ where });

    // ===== Fetch paginated subjects =====
    const subjects = await prisma.subject.findMany({
      where,
      skip: page * pageSize,
      take: pageSize,
      orderBy,
      include: {
        lessons: true,
      },
    });

    const formatted = subjects.map((s, index) => ({
      ...s,
      sno: page * pageSize + index + 1,
      totalLessons: s.lessons ? s.lessons.length : 0,
    }));

    res.json({ rows: formatted, total });
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: "Server error" });
  }
};

//get subjects for student
// (Here all subjects are shown. Later we add enrollments.)

exports.getStudentSubjects = async (req, res) => {
  try {
    const subjects = await prisma.subject.findMany();

    res.json(subjects);
  } catch (err) {
    console.log(err);
    return res.status(500).json({ message: "Server error" });
  }
};

//delete subject (admin)
exports.deleteSubject = async (req, res) => {
  try {
    const id = parseInt(req.params.id);

    const subject = await prisma.subject.findUnique({
      where: { id },
      include: { teacher: true },
    });

    if (!subject) {
      return res.status(404).json({ message: "Subject not found" });
    }
    
    await prisma.subject.delete({
      where: { id },
    });

    if (subject.teacherId) {
      const message = `The subject "${subject.title}" has been deleted.`;
      await notifyUser(req, subject.teacherId, message, null); // senderId = null for system
    }

    return res.json({ message: "Subject deleted successfully" });
  } catch (err) {
    console.log(err);
    return res.status(500).json({ message: "Server error" });
  }
};

exports.getAllStudentSubjectsShow = async (req, res) => {
  try {
    const page = Number(req.query.page) || 0;
    const pageSize = Number(req.query.pageSize) || 10;
    const studentId = req.user.id;

    // ===== Sorting =====
    let orderBy = [];
    if (req.query.sort) {
      try {
        const sortModel = JSON.parse(req.query.sort);
        orderBy = sortModel
          .filter(({ field }) =>
            [
              "id",
              "title",
              "description",
              "price",
              "totalLessons",
              "sno",
            ].includes(field)
          )
          .map(({ field, sort }) => {
            if (field === "sno") return { id: sort.toLowerCase() };
            return { [field]: sort.toLowerCase() };
          });
      } catch (err) {
        console.error("Invalid sort JSON", err);
      }
    }
    if (orderBy.length === 0) orderBy = [{ id: "asc" }];

    // ===== Filtering =====
    const where = {};
    if (req.query.filter) {
      try {
        const filterModel = JSON.parse(req.query.filter);
        const AND = [];

        filterModel.items?.forEach(({ field, operator, value }) => {
          if (!value) return;

          switch (field) {
            case "title":
            case "description":
              if (operator === "contains") {
                AND.push({ [field]: { contains: value, mode: "insensitive" } });
              }
              break;
            case "sno":
              if (!isNaN(Number(value))) {
                const snoNum = Number(value);
                AND.push({ id: snoNum });
              }
              break;
            case "price":
              if (operator === "equals") {
                AND.push({ price: Number(value) });
              }
              break;
          }
        });

        if (AND.length > 0) where.AND = AND;
      } catch (err) {
        console.error("Invalid filter JSON", err);
      }
    }

    // ===== Fetch subjects =====
    const [subjects, total] = await Promise.all([
      prisma.subject.findMany({
        where,
        skip: page * pageSize,
        take: pageSize,
        orderBy,
        include: {
          teacher: { select: { firstName: true, lastName: true } },
          lessons: true,
          enrollment: studentId ? { where: { studentId } } : false, // fetch only for the logged-in student
        },
      }),
      prisma.subject.count({ where }),
    ]);

    // ===== Format results =====
    const formatted = subjects.map((s, index) => ({
      ...s,
      sno: page * pageSize + index + 1,
      teacherName: s.teacher
        ? `${s.teacher.firstName} ${s.teacher.lastName}`
        : "",
      totalLessons: s.lessons ? s.lessons.length : 0,
      isEnrolled: studentId
        ? s.enrollment.some(
            (e) => e.studentId === studentId && e.status === "active"
          )
        : false, // guests are never enrolled
    }));

    res.json({ rows: formatted, total });
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: "Server error" });
  }
};

exports.getSubjectsByTeacherId = async (req, res) => {
  try {
    const teacherId = parseInt(req.params.teacherId);
    if (isNaN(teacherId)) {
      return res.status(400).json({ message: "Invalid teacher ID" });
    }

    const subjects = await prisma.subject.findMany({
      where: { teacherId },
      select: {
        id: true,
        title: true,
      },
    });

    return res.json(subjects); // frontend will receive an array of subjects
  } catch (err) {
    console.error(err);
    return res.status(500).json({ message: "Server error" });
  }
};
