// backend/controllers/attendanceController.js

const db = require("../config/db"); // Needed for complex, multi-join queries (reports)
const PDFDocument = require("pdfkit");

// Import all required models
const Attendance = require("../models/attendanceModel");
const Student = require("../models/studentModel");
const Setting = require("../models/settingModel");
const Institution = require("../models/institutionModel");
const { sendSms } = require("../services/smsService");

/**
 * A shared helper function to process an attendance log.
 * It contains all business logic for validation and background SMS tasks.
 * It is NOT an exported controller method.
 */
const handleAttendance = async (res, institution_id, student, type) => {
  // Security check: If student is null, it means findByQrId/findById failed for this institution.
  if (!student) {
    return res
      .status(404)
      .json({ message: "Student not found for this institution." });
  }

  // Business logic: Check student status
  if (student.status === "inactive") {
    return res
      .status(403)
      .json({ message: `Student '${student.name}' is inactive.` });
  }

  let attendanceRecord;
  try {
    const today = new Date().toISOString().split("T")[0];

    // Check the last action for this student at this institution today
    const lastActionSql = `
      SELECT type FROM attendance 
      WHERE student_id = ? AND institution_id = ? AND DATE(timestamp) = ? 
      ORDER BY timestamp DESC LIMIT 1;
    `;
    const [lastActions] = await db.query(lastActionSql, [
      student.id,
      institution_id,
      today,
    ]);
    const lastActionType = lastActions.length > 0 ? lastActions[0].type : null;

    // --- Business Logic Rules ---
    if (type === "entry" && lastActionType === "entry") {
      return res.status(208).json({
        message: `${student.name} has already entered.`,
        type: "duplicate",
      });
    }
    if (type === "exit" && lastActionType !== "entry") {
      return res.status(409).json({
        message: `Cannot mark exit. ${student.name} has not entered yet.`,
        type: "error",
      });
    }

    // Log the attendance, now scoped to the institution
    attendanceRecord = await Attendance.log(institution_id, {
      student_id: student.id,
      type,
    });
    if (!attendanceRecord || !attendanceRecord.id) {
      throw new Error("Failed to create attendance record.");
    }

    // Immediately send success response for a fast UI
    res.status(200).json({
      message: `Attendance logged for ${student.name}.`,
      student: student.name,
      type: type,
    });

    // --- Handle SMS sending in the background ---
    (async () => {
      try {
        // 1. Fetch the institution's settings. It's okay if this returns null.
        const [settings, institution] = await Promise.all([
          Setting.findByInstitutionId(institution_id),
          Institution.findById(institution_id),
        ]);

        if (!institution) {
          console.warn(
            `[SMS Service] Skipping SMS for student ${student.id} because institution ${institution_id} could not be found.`
          );
          return;
        }

        // 2. Dynamically build the message signature
        const timeString = new Date().toLocaleTimeString("en-US", {
          hour: "2-digit",
          minute: "2-digit",
          hour12: true,
        });
        const action = type === "entry" ? "arrived at" : "left";

        // Main body of the message
        let mainMessage = `Dear Parent, your child ${student.name} has ${action} the institute at ${timeString}.`;

        // Signature part
        let signature = `\n\nThank you,\n${institution.name}`;

        // Append contact number only if it exists in the settings
        if (settings && settings.contact_phone) {
          signature += `\nContact: ${settings.contact_phone}`;
        }

        // Combine them into the final message
        const finalMessage = mainMessage + signature;
        if (student.mobile_number) {
          // 3. Call the sendSms service with the final message and settings
          const smsResult = await sendSms(
            student.mobile_number,
            finalMessage, // Pass the newly constructed message
            settings
          );

          // 3. Update the database based on the result from the service.
          const smsStatus = smsResult.ok ? "success" : "failed";
          await Attendance.updateSmsStatus(
            institution_id,
            attendanceRecord.id,
            smsStatus
          );
        } else {
          await Attendance.updateSmsStatus(
            institution_id,
            attendanceRecord.id,
            "no_number"
          );
        }
      } catch (smsError) {
        console.error(
          `Background SMS task failed for attendance ID ${attendanceRecord.id}:`,
          smsError
        );
        // Ensure we still mark the SMS as failed in case of an unexpected crash
        if (attendanceRecord && attendanceRecord.id) {
          await Attendance.updateSmsStatus(
            institution_id,
            attendanceRecord.id,
            "failed"
          );
        }
      }
    })();
  } catch (error) {
    console.error("Critical error in handleAttendance:", error.message);
    if (!res.headersSent) {
      res
        .status(500)
        .json({ message: "Error logging attendance", error: error.message });
    }
  }
};

/**
 * @desc    Log attendance via QR code scan
 * @route   POST /api/attendance/qr
 * @access  Private (Admin, Staff)
 */
exports.logAttendanceByQr = async (req, res) => {
  const { institution_id } = req.user;
  const { qr_id, type } = req.body;
  if (!qr_id || !type) {
    return res.status(400).json({ message: "QR ID and type are required." });
  }

  // Use the tenant-scoped model function
  const student = await Student.findByQrId(institution_id, qr_id);
  await handleAttendance(res, institution_id, student, type);
};

/**
 * @desc    Log attendance manually via student ID
 * @route   POST /api/attendance/manual
 * @access  Private (Admin, Staff)
 */
exports.logAttendanceManually = async (req, res) => {
  const { institution_id } = req.user;
  const { student_id, type } = req.body;
  if (!student_id || !type) {
    return res
      .status(400)
      .json({ message: "Student ID and type are required." });
  }

  // Use the tenant-scoped model function
  const student = await Student.findById(institution_id, student_id);
  await handleAttendance(res, institution_id, student, type);
};

/**
 * @desc    Get attendance history for a specific date
 * @route   GET /api/attendance/history?date=YYYY-MM-DD
 * @access  Private (Admin, Staff)
 */
exports.getHistory = async (req, res) => {
  const { institution_id } = req.user;
  const { date } = req.query;
  if (!date) {
    return res.status(400).json({ message: "Date parameter is required." });
  }

  try {
    const history = await Attendance.getHistoryByDate(institution_id, date);
    res.status(200).json(history);
  } catch (error) {
    res
      .status(500)
      .json({ message: "Error fetching attendance history", error });
  }
};

/**
 * @desc    Get today's entry/exit summary grouped by grade
 * @route   GET /api/attendance/summary/by-grade
 * @access  Private (Admin, Staff)
 */
exports.getTodaySummaryByGrade = async (req, res) => {
  const { institution_id } = req.user;
  try {
    const sql = `
      SELECT 
          s.grade_id,
          g.name as grade_name,
          SUM(CASE WHEN a.type = 'entry' THEN 1 ELSE 0 END) AS entries,
          SUM(CASE WHEN a.type = 'exit' THEN 1 ELSE 0 END) AS exits
      FROM attendance a
      JOIN students s ON a.student_id = s.id
      JOIN grades g ON s.grade_id = g.id
      WHERE a.institution_id = ? AND DATE(a.timestamp) = CURDATE()
      GROUP BY s.grade_id, g.name
      ORDER BY CAST(g.name AS UNSIGNED) ASC;
    `;
    const [rows] = await db.query(sql, [institution_id]);
    res.status(200).json(rows);
  } catch (error) {
    res.status(500).json({
      message: "Error fetching summary by grade",
      error: error.message,
    });
  }
};

exports.generatePdfReport = async (req, res) => {
  try {
    // 1. Get the institution ID from the authenticated user
    const { institution_id } = req.user;
    const { startDate, endDate, gradeId, reportType = "combined" } = req.query;

    if (!startDate || !endDate) {
      return res
        .status(400)
        .json({ message: "Start date and end date are required." });
    }

    // 2. Fetch the institution's name for the PDF header
    const institution = await Institution.findById(institution_id);
    const institutionName = institution
      ? institution.name
      : "Attendance Report"; // Fallback name

    // 3. Set up the PDF document stream
    const doc = new PDFDocument({ margin: 40, size: "A4", layout: "portrait" });
    res.setHeader("Content-Type", "application/pdf");
    res.setHeader(
      "Content-Disposition",
      `attachment; filename=attendance-report_${institution.name.replace(
        /\s+/g,
        "-"
      )}_${Date.now()}.pdf`
    );
    doc.pipe(res);

    // 4. Create the dynamic PDF Header
    doc
      .fontSize(16)
      .font("Helvetica-Bold")
      .text(`${institutionName} - Attendance Report`, { align: "center" });
    doc
      .fontSize(10)
      .font("Helvetica")
      .text(`Date Range: ${startDate} to ${endDate}`, { align: "center" });
    doc.moveDown(2);

    let records = [];

    // --- LOGIC FOR COMBINED (Entry & Exit) REPORT ---
    if (reportType === "combined") {
      // Build the query and params securely
      const query = `
        SELECT
            s.name, g.name AS grade_name,
            DATE(a.timestamp) as attendance_date_utc,
            MIN(CASE WHEN a.type = 'entry' THEN a.timestamp END) AS entry_timestamp,
            MAX(CASE WHEN a.type = 'exit' THEN a.timestamp END) AS exit_timestamp
        FROM students s
        JOIN attendance a ON s.id = a.student_id
        JOIN grades g ON s.grade_id = g.id
        WHERE a.institution_id = ? AND DATE(a.timestamp) BETWEEN ? AND ?
        ${gradeId ? "AND s.grade_id = ?" : ""}
        GROUP BY s.id, s.name, g.name, attendance_date_utc
        ORDER BY attendance_date_utc ASC, s.name ASC;
      `;

      const params = [institution_id, startDate, endDate];
      if (gradeId) {
        params.push(gradeId);
      }

      [records] = await db.query(query, params);

      // PDF Layout for Combined Report (Your existing layout code is good)
      const tableTop = doc.y;
      const nameX = 40,
        gradeX = 190,
        dateX = 260,
        entryX = 350,
        exitX = 450;
      doc.font("Helvetica-Bold").fontSize(10);
      doc.text("Student Name", nameX, tableTop, { width: 140 });
      doc.text("Grade", gradeX, tableTop, { width: 60 });
      doc.text("Date", dateX, tableTop, { width: 80 });
      doc.text("Entry Time", entryX, tableTop, { width: 90, align: "center" });
      doc.text("Exit Time", exitX, tableTop, { width: 90, align: "center" });
      const headerBottom = doc.y + 5;
      doc.moveTo(nameX, headerBottom).lineTo(555, headerBottom).stroke();

      if (records.length > 0) {
        doc.font("Helvetica").fontSize(9);
        let currentY = headerBottom + 10;
        records.forEach((rec) => {
          if (currentY > 750) {
            // Check for page break
            doc.addPage();
            currentY = 40; // Reset Y position on new page
          }
          const formatTime = (timestamp) =>
            timestamp
              ? new Date(timestamp).toLocaleTimeString("en-US", {
                  hour: "2-digit",
                  minute: "2-digit",
                  hour12: true,
                })
              : "---";
          doc.text(rec.name, nameX, currentY, { width: 140 });
          doc.text(rec.grade_name, gradeX, currentY, { width: 60 });
          doc.text(
            new Date(rec.attendance_date_utc).toLocaleDateString("en-CA"),
            dateX,
            currentY,
            { width: 80 }
          );
          doc.text(formatTime(rec.entry_timestamp), entryX, currentY, {
            width: 90,
            align: "center",
          });
          doc.text(formatTime(rec.exit_timestamp), exitX, currentY, {
            width: 90,
            align: "center",
          });
          currentY += 20;
        });
      }

      // --- LOGIC FOR SEPARATE ENTRY/EXIT REPORTS ---
    } else {
      if (!["entry", "exit"].includes(reportType)) {
        return res
          .status(400)
          .json({ message: "Invalid report type specified." });
      }

      const query = `
        SELECT a.timestamp, s.name, g.name AS grade_name
        FROM attendance a
        JOIN students s ON a.student_id = s.id
        JOIN grades g ON s.grade_id = g.id
        WHERE a.institution_id = ? AND DATE(a.timestamp) BETWEEN ? AND ? AND a.type = ?
        ${gradeId ? "AND s.grade_id = ?" : ""}
        ORDER BY a.timestamp ASC;
      `;

      const params = [institution_id, startDate, endDate, reportType];
      if (gradeId) {
        params.push(gradeId);
      }

      [records] = await db.query(query, params);

      // PDF Layout for Separate Reports
      const tableTop = doc.y;
      doc
        .font("Helvetica-Bold")
        .fontSize(11)
        .text("Student Name", 40, tableTop, { width: 200 });
      doc.text("Grade", 250, tableTop);
      doc.text("Timestamp", 350, tableTop, { align: "right", width: 205 });
      doc.moveTo(40, doc.y).lineTo(555, doc.y).stroke();
      doc.font("Helvetica");

      let currentY = doc.y + 10;
      records.forEach((rec) => {
        if (currentY > 750) {
          // Check for page break
          doc.addPage();
          currentY = 40; // Reset Y position on new page
        }
        const formattedTimestamp = new Date(rec.timestamp).toLocaleString(
          "en-US",
          { hour12: true, dateStyle: "medium", timeStyle: "short" }
        );
        doc.fontSize(10).text(rec.name, 40, currentY, { width: 200 });
        doc.text(rec.grade_name, 250, currentY);
        doc.text(formattedTimestamp, 350, currentY, {
          align: "right",
          width: 205,
        });
        currentY += 20;
      });
    }

    // Display a message if no records were found
    if (records.length === 0) {
      doc.moveDown(5);
      doc
        .fontSize(12)
        .font("Helvetica-Oblique")
        .text("No attendance records found for the selected criteria.", {
          align: "center",
        });
    }

    doc.end();
  } catch (error) {
    console.error("Error generating PDF report:", error);
    res
      .status(500)
      .json({ message: "Error generating PDF report", error: error.message });
  }
};
