const mongoose = require("mongoose");
const Attendance = require("../models/attendanceModel");
const User = require("../models/userModel");
const Shift = require("../models/admin/shiftModel"); // if exists
const { errorResponse } = require("../utils/errorResponseHandler");
const { logUserAction } = require("../utils/userActionLogger");
const { timeToMinutes, formatTime, minutesToHHMM, calculateMinutes } = require("../utils/time");
/**
 * ======================================================
 * UTILITIES
 * ======================================================
 */




// const SHIFT_RULES = {
//   startTime: "09:30",
//   graceMinutes: 15,
//   fullDayMinutes: 6 * 60,
//   halfDayMinutes: 4 * 60,
//   expectedMinutes: 9 * 60,
// };

/**
 * ======================================================
 * 1️ GET DAILY ATTENDANCE (MAIN SCREEN)
 * ======================================================
 * GET /attendance?date=YYYY-MM-DD
 */
const getDailyAttendance = async (req, res) => {
    try {
        const { date } = req.query;
        if (!date) {
            return errorResponse(res, "Date is required", 400);
        }

        // 🔐 Role-based user filter
        let userFilter = { status: "Approved", isDeleted: false };

        if (req.user.role === "manager") {
            userFilter.reportingManager = req.user._id;
        }

        // 1️⃣ Fetch users
        const users = await User.find(userFilter)
            .populate("shift", "shiftName startTime endTime")
            .select("firstName lastName shift workingHours");

        // 2️⃣ Fetch attendance for the date
        const attendanceRecords = await Attendance.find({
            date,
            orgId: req.user.orgId,
            isDeleted: false,
        })
            .populate("user", "firstName lastName")
            .populate("shift", "shiftName")
            .populate("createdBy", "firstName lastName")
            .populate("updatedBy", "firstName lastName");

        // 3️⃣ Map attendance by userId
        const attendanceMap = {};
        attendanceRecords.forEach((a) => {
            attendanceMap[a.user._id.toString()] = a;
        });

        // 4️⃣ Merge users + attendance
        const result = users.map((user) => {
            const record = attendanceMap[user._id.toString()];

            return {
                _id: record?._id || null,
                userId: user._id,
                employeeName: `${user.firstName} ${user.lastName}`,
                shift: user.shift?.shiftName || "Morning",
                // checkIn: record?.checkIn || "9:30",
                // checkOut: record?.checkOut || "18:30",
                checkIn: formatTime(record?.checkIn) || "09:30",
                checkOut: formatTime(record?.checkOut) || "18:30",
                totalHours: record
                    ? minutesToHHMM(record.totalMinutes)
                    : "0:00",
                overtime: record
                    ? minutesToHHMM(record.overtimeMinutes)
                    : "0:00",
                status: record?.status || "Absent",
            };
        });

        return res.json({ success: true, data: result });
    } catch (err) {
        console.error("getDailyAttendance error:", err);
        return errorResponse(res, "Failed to fetch attendance", 500);
    }
};

/**
 * ======================================================
 * 2️ MARK / UPDATE ATTENDANCE (MANUAL ENTRY)
 * ======================================================
 * POST /attendance/mark
 */
const markAttendance = async (req, res) => {
    try {
        const { userId, date, checkIn, checkOut } = req.body;

        if (!userId || !date) {
            return errorResponse(res, "User and date are required", 400);
        }

        if (!mongoose.Types.ObjectId.isValid(userId)) {
            return errorResponse(res, "Invalid user", 400);
        }

        // 🔐 Role restriction
        if (req.user.role === "employee") {
            return errorResponse(res, "Not allowed", 403);
        }

        const user = await User.findById(userId).populate("shift");
        if (!user) {
            return errorResponse(res, "User not found", 404);
        }

        // ⏱ Calculate minutes
        const totalMinutes = calculateMinutes(checkIn, checkOut);
        const expectedMinutes = (user.workingHours || 8) * 60;
        const overtimeMinutes = Math.max(0, totalMinutes - expectedMinutes);

        // 🔁 UPSERT attendance
        const attendance = await Attendance.findOneAndUpdate(
            { user: userId, date },
            {
                user: userId,
                date,
                shift: user.shift || null,
                checkIn,
                checkOut,
                totalMinutes,
                overtimeMinutes,
                status: "Present",
                isLocked: true,
                source: "manual",
                orgId: req.user.orgId,
                updatedBy: req.user._id,
            },
            {
                upsert: true,
                new: true,
                setDefaultsOnInsert: true,
            }
        );

        // Set createdBy only once
        if (!attendance.createdBy) {
            attendance.createdBy = req.user._id;
            await attendance.save();
        }

        // 🧾 AUDIT LOG
        await logUserAction({
            userId: req.user._id,
            action: "MARK_ATTENDANCE",
            model: "Attendance",
            modelId: attendance._id,
            details: {
                employeeId: userId,
                date,
                checkIn,
                checkOut,
                status: "Present",
            },
            req,
        });

        return res.json({
            success: true,
            message: "Attendance marked successfully",
            data: attendance,
        });
    } catch (err) {
        console.error("markAttendance error:", err);
        return errorResponse(res, "Failed to mark attendance", 500);
    }
};

/**
 * ======================================================
 * 3️ RESET ATTENDANCE (MAKE ABSENT AGAIN)
 * ======================================================
 * PUT /attendance/reset
 */
const resetAttendance = async (req, res) => {
    try {
        const { userId, date } = req.body;

        if (!userId || !date) {
            return errorResponse(res, "User and date are required", 400);
        }

        const attendance = await Attendance.findOneAndUpdate(
            { user: userId, date },
            {
                checkIn: null,
                checkOut: null,
                totalMinutes: 0,
                overtimeMinutes: 0,
                status: "Absent",
                updatedBy: req.user._id,
            },
            { new: true }
        );

        // AUDIT LOG
        await logUserAction({
            userId: req.user._id,
            action: "RESET_ATTENDANCE",
            model: "Attendance",
            modelId: attendance?._id,
            details: {
                employeeId: userId,
                date,
                status: "Absent",
            },
            req,
        });

        return res.json({
            success: true,
            message: "Attendance reset to Absent",
        });
    } catch (err) {
        console.error("resetAttendance error:", err);
        return errorResponse(res, "Failed to reset attendance", 500);
    }
};

/**
 * ======================================================
 * 4️ MONTHLY ATTENDANCE SUMMARY (PER USER)
 * ======================================================
 * GET /attendance/monthly?userId=ID&month=YYYY-MM
 */
const getMonthlyAttendance = async (req, res) => {
    try {
        const { userId, month } = req.query;

        if (!userId || !month) {
            return errorResponse(res, "User and month are required", 400);
        }

        const records = await Attendance.find({
            user: userId,
            date: { $regex: `^${month}` },
            isDeleted: false,
        });

        const summary = {
            present: 0,
            absent: 0,
            halfDay: 0,
            leave: 0,
            totalMinutes: 0,
            overtimeMinutes: 0,
        };

        records.forEach((r) => {
            summary.totalMinutes += r.totalMinutes;
            summary.overtimeMinutes += r.overtimeMinutes;

            if (r.status === "Present") summary.present++;
            if (r.status === "Absent") summary.absent++;
            if (r.status === "Half-Day") summary.halfDay++;
            if (r.status === "Leave") summary.leave++;
        });

        return res.json({ success: true, data: summary });
    } catch (err) {
        console.error("getMonthlyAttendance error:", err);
        return errorResponse(res, "Failed to fetch monthly attendance", 500);
    }
};


// const bulkUpsertAttendance = async (req, res) => {
//   try {
//     const { records } = req.body;

//     if (!Array.isArray(records) || records.length === 0) {
//       return errorResponse(res, "No attendance data", 400);
//     }

//     const ops = records.map(r => ({
//       updateOne: {
//         filter: { user: r.userId, date: r.date },
//         update: {
//           $set: {
//             user: r.userId,
//             date: r.date,
//             checkIn: r.checkIn,
//             checkOut: r.checkOut,
//             totalMinutes: r.totalMinutes,
//             overtimeMinutes: r.overtimeMinutes,
//             status: r.status,
//             orgId: req.user.orgId,
//             updatedBy: req.user._id,
//           },
//           $setOnInsert: {
//             createdBy: req.user._id,
//             source: "manual",
//           },
//         },
//         upsert: true,
//       },
//     }));

//     await Attendance.bulkWrite(ops);

//     return res.json({
//       success: true,
//       message: "Attendance saved",
//     });
//   } catch (err) {
//     console.error("bulkUpsertAttendance error:", err);
//     return errorResponse(res, "Failed to save attendance", 500);
//   }
// };


// const bulkUpsertAttendance = async (req, res) => {
//     try {
//         const { records } = req.body;

//         if (!Array.isArray(records) || records.length === 0) {
//             return errorResponse(res, "No attendance data", 400);
//         }

//         const ops = [];

//         for (const r of records) {
//             if (!r.userId || !r.date) continue;

//             const inMin = timeToMinutes(r.checkIn);
//             const outMin = timeToMinutes(r.checkOut);

//             let totalMinutes = 0;
//             let overtimeMinutes = 0;
//             let status = "Absent";

//             // ✅ VALID TIME
//             if (outMin > inMin) {
//                 totalMinutes = outMin - inMin;

//                 const expectedMinutes = 9 * 60; // OR fetch from shift later
//                 overtimeMinutes = Math.max(0, totalMinutes - expectedMinutes);

//                 // ✅ STATUS RULES (FINAL AUTHORITY)
//                 if (totalMinutes >= 6 * 60) {
//                     status = "Present";
//                 } else if (totalMinutes >= 4 * 60) {
//                     status = "Half-Day";
//                 }
//             }

//             ops.push({
//                 updateOne: {
//                     filter: {
//                         user: r.userId,
//                         date: r.date,
//                         orgId: req.user.orgId,
//                     },
//                     update: {
//                         $set: {
//                             user: r.userId,
//                             date: r.date,
//                             checkIn: r.checkIn || null,
//                             checkOut: r.checkOut || null,
//                             totalMinutes,
//                             overtimeMinutes,
//                             status,
//                             updatedBy: req.user._id,
//                         },
//                         $setOnInsert: {
//                             createdBy: req.user._id,
//                             source: "manual",
//                             orgId: req.user.orgId,
//                         },
//                     },
//                     upsert: true,
//                 },
//             });
//         }

//         if (ops.length === 0) {
//             return errorResponse(res, "No valid records to save", 400);
//         }

//         await Attendance.bulkWrite(ops);

//         return res.json({
//             success: true,
//             message: "Attendance saved successfully",
//         });
//     } catch (err) {
//         console.error("bulkUpsertAttendance error:", err);
//         return errorResponse(res, "Failed to save attendance", 500);
//     }
// };


const bulkUpsertAttendance = async (req, res) => {
    try {
        const { records } = req.body;

        if (!Array.isArray(records) || records.length === 0) {
            return errorResponse(res, "No attendance data", 400);
        }

        /* ================= SHIFT RULES (ORG LEVEL) ================= */
        const SHIFT_RULES = {
            startTime: "09:30",
            graceMinutes: 15,
            fullDayMinutes: 6 * 60,   // 6 hrs = present
            halfDayMinutes: 4 * 60,   // 4 hrs = half-day
            expectedMinutes: 9 * 60,  // OT after this
        };

        const ops = [];

        for (const r of records) {
            if (!r.userId || !r.date) continue;

            const inMin = timeToMinutes(r.checkIn);
            const outMin = timeToMinutes(r.checkOut);

            let totalMinutes = 0;
            let overtimeMinutes = 0;
            let lateMinutes = 0;
            let status = "Absent";

            /* ================= TIME CALCULATION ================= */
            if (inMin && outMin && outMin > inMin) {
                totalMinutes = outMin - inMin;

                overtimeMinutes = Math.max(
                    0,
                    totalMinutes - SHIFT_RULES.expectedMinutes
                );

                /* ================= LATE LOGIC ================= */
                const shiftStartMin = timeToMinutes(SHIFT_RULES.startTime);
                const isLate = inMin > shiftStartMin + SHIFT_RULES.graceMinutes;

                if (isLate) {
                    lateMinutes = inMin - shiftStartMin;
                }

                /* ================= FINAL STATUS (PRIORITY BASED) ================= */
                if (totalMinutes >= SHIFT_RULES.fullDayMinutes) {
                    status = isLate ? "Late" : "Present";
                } else if (totalMinutes >= SHIFT_RULES.halfDayMinutes) {
                    status = "Half-Day";
                } else {
                    status = "Absent";
                }
            }

            /* ================= BULK OPERATION ================= */
            ops.push({
                updateOne: {
                    filter: {
                        user: r.userId,
                        date: r.date,
                        orgId: req.user.orgId,
                    },
                    update: {
                        $set: {
                            user: r.userId,
                            date: r.date,
                            checkIn: r.checkIn || null,
                            checkOut: r.checkOut || null,
                            totalMinutes,
                            overtimeMinutes,
                            lateMinutes,
                            status,
                            updatedBy: req.user._id,
                        },
                        $setOnInsert: {
                            createdBy: req.user._id,
                            source: "manual",
                            orgId: req.user.orgId,
                        },
                    },
                    upsert: true,
                },
            });
        }

        if (ops.length === 0) {
            return errorResponse(res, "No valid attendance records", 400);
        }

        await Attendance.bulkWrite(ops);

        return res.json({
            success: true,
            message: "Attendance saved successfully",
        });
    } catch (err) {
        console.error("bulkUpsertAttendance error:", err);
        return errorResponse(res, "Failed to save attendance", 500);
    }
};


const updateAttendance = async (req, res) => {
    try {
        const { checkIn, checkOut, statusOverride } = req.body;
        const { id } = req.params;

        const attendance = await Attendance.findById(id);
        if (!attendance) {
            return errorResponse(res, "Attendance record not found", 404);
        }

        /* ================= SHIFT RULES ================= */
        const SHIFT_RULES = {
            startTime: "09:30",
            graceMinutes: 15,
            fullDayMinutes: 6 * 60,
            halfDayMinutes: 4 * 60,
            expectedMinutes: 9 * 60,
        };

        const inMin = timeToMinutes(checkIn);
        const outMin = timeToMinutes(checkOut);

        let totalMinutes = 0;
        let overtimeMinutes = 0;
        let lateMinutes = 0;
        let status = "Absent";

        /* ================= TIME CALCULATION ================= */
        if (inMin && outMin && outMin > inMin) {
            totalMinutes = outMin - inMin;

            overtimeMinutes = Math.max(
                0,
                totalMinutes - SHIFT_RULES.expectedMinutes
            );

            const shiftStartMin = timeToMinutes(SHIFT_RULES.startTime);
            const isLate = inMin > shiftStartMin + SHIFT_RULES.graceMinutes;

            if (isLate) {
                lateMinutes = inMin - shiftStartMin;
            }

            /* ================= STATUS PRIORITY ================= */
            if (totalMinutes >= SHIFT_RULES.fullDayMinutes) {
                status = isLate ? "Late" : "Present";
            } else if (totalMinutes >= SHIFT_RULES.halfDayMinutes) {
                status = "Half-Day";
            }
        }

        /* ================= ADMIN OVERRIDE ================= */
        if (statusOverride === "Absent") {
            status = "Absent";
            lateMinutes = 0;
            overtimeMinutes = 0;
        }

        // let shiftId = attendance.shift;
        // // If shift missing, derive from user
        // if (!shiftId) {
        //     const user = await User.findById(attendance.user).populate("shift");

        //     if (!user?.shift) {
        //         return errorResponse(
        //             res,
        //             "User has no shift assigned. Cannot update attendance.",
        //             400
        //         );
        //     }
        //     shiftId = user.shift._id;
        // }

        /* ================= SAVE ================= */
        // attendance.shift = shiftId;
        attendance.checkIn = checkIn || null;
        attendance.checkOut = checkOut || null;
        attendance.totalMinutes = totalMinutes;
        attendance.overtimeMinutes = overtimeMinutes;
        attendance.lateMinutes = lateMinutes;
        attendance.status = status;
        attendance.updatedBy = req.user._id;

        await attendance.save();

        /* ================= AUDIT LOG ================= */
        await logUserAction({
            userId: req.user._id,
            action: "UPDATE_ATTENDANCE",
            model: "Attendance",
            modelId: attendance._id,
            details: {
                checkIn,
                checkOut,
                status,
            },
            req,
        });

        return res.json({
            success: true,
            message: "Attendance updated successfully",
            data: attendance,
        });
    } catch (err) {
        console.error("updateAttendance error:", err);
        return errorResponse(res, "Failed to update attendance", 500);
    }
};


/*
 * GET /attendance/stats?date=YYYY-MM-DD
*/
const getAttendanceStats = async (req, res) => {
  try {
    const { date } = req.query;

    if (!date) {
      return errorResponse(res, "Date is required", 400);
    }

    /* ================= ROLE BASED USER FILTER ================= */
    // let userFilter = {
    // //   status: "Approved",
    //   isDeleted: false,
    //   orgId: req.user.orgId,
    // };
    let userFilter = {status: "Approved", isDeleted: false };

    if (req.user.role === "manager") {
      userFilter.reportingManager = req.user._id;
    }

    /* ================= TOTAL EMPLOYEES ================= */
    const totalEmployees = await User.countDocuments(userFilter);

    /* ================= ATTENDANCE AGGREGATION ================= */
    const stats = await Attendance.aggregate([
      {
        $match: {
          date,
          orgId: req.user.orgId,
          isDeleted: false,
        },
      },
      {
        $lookup: {
          from: "users",
          localField: "user",
          foreignField: "_id",
          as: "user",
        },
      },
      { $unwind: "$user" },

      //  Apply same role filter here
      {
        $match: {
          "user.status": "Approved",
          "user.isDeleted": false,
          ...(req.user.role === "manager"
            ? { "user.reportingManager": req.user._id }
            : {}),
        },
      },

      {
        $group: {
          _id: "$status",
          count: { $sum: 1 },
        },
      },
    ]);

    /* ================= MAP RESULTS ================= */
    const result = {
      total: totalEmployees,
      present: 0,
      late: 0,
      absent: 0,
    };

    stats.forEach((s) => {
      if (s._id === "Present") result.present = s.count;
      if (s._id === "Late") result.late = s.count;
      if (s._id === "Absent") result.absent = s.count;
    });

    /* ================= ABSENT = NO RECORD USERS ================= */
    const markedCount = stats.reduce((sum, s) => sum + s.count, 0);
    result.absent = totalEmployees - (result.present + result.late);

    return res.json({
      success: true,
      data: result,
    });
  } catch (err) {
    console.error("getAttendanceStats error:", err);
    return errorResponse(res, "Failed to fetch attendance stats", 500);
  }
};







module.exports = {
    getDailyAttendance,
    markAttendance,
    resetAttendance,
    getMonthlyAttendance,
    bulkUpsertAttendance,
    updateAttendance,
    getAttendanceStats,
}
