const fs = require('fs');

const path = 'd:\\Development\\BELAJAR\\APPS\\SAPPS-BACKEND\\apps\\sapps\\masters\\notifications\\clockReminderScheduler.js';
let content = fs.readFileSync(path, 'utf8');

// 1. Fix getEmployeesWithShifts unused UTC 'today' variable
content = content.replace(
    'const today = new Date().toISOString().slice(0, 10);\n\n\t\t// Get active employees',
    '// Get active employees'
);

// 2. Fix getTodayAttendance UTC 'today' variable to use local timezone Date
content = content.replace(
    'const today = new Date().toISOString().slice(0, 10);',
    'const nowRaw = new Date();\n\t\tconst today = `${nowRaw.getFullYear()}-${String(nowRaw.getMonth() + 1).padStart(2, "0")}-${String(nowRaw.getDate()).padStart(2, "0")}`;'
);

// 3. Fix wasNotificationSentToday UTC 'today' variable to use local timezone Date
content = content.replace(
    'const today = new Date().toISOString().slice(0, 10);',
    'const nowRaw = new Date();\n\t\tconst today = `${nowRaw.getFullYear()}-${String(nowRaw.getMonth() + 1).padStart(2, "0")}-${String(nowRaw.getDate()).padStart(2, "0")}`;'
);

// 4. Overhaul runClockReminderCheck to remove loop (N+1 query problem)
const newRunClockReminderCheck = `async function runClockReminderCheck(options = {}) {
\ttry {
\t\tconsole.log(\`[Clock Reminder Scheduler] Running check at \${new Date().toISOString()}\`);

\t\tlet companiesId = options.companiesId || null;

\t\t// If companiesId not provided, get enabled companies
\t\tif (!companiesId) {
\t\t\tcompaniesId = await getEnabledCompanies();
\t\t\tif (companiesId.length === 0) {
\t\t\t\tconsole.log("[Clock Reminder Scheduler] No companies with scheduler enabled, skipping check");
\t\t\t\treturn {
\t\t\t\t\tsuccess: true,
\t\t\t\t\tmessage: "No companies with scheduler enabled",
\t\t\t\t\tnotificationsCreated: 0,
\t\t\t\t\temployeesChecked: 0,
\t\t\t\t};
\t\t\t}
\t\t\tconsole.log(\`[Clock Reminder Scheduler] Found \${companiesId.length} companies with scheduler enabled\`);
\t\t}

\t\tconst modifiedBy = options.modified_by || 1;
\t\tconst owner = options.owner || 1;

\t\t// Get employees with shifts
\t\tconst employees = await getEmployeesWithShifts(companiesId);
\t\tconsole.log(\`[Clock Reminder Scheduler] Checking \${employees.length} employees\`);

\t\tlet notificationsCreated = 0;
\t\tlet checksPerformed = 0;
\t\tconst errors = [];

\t\tconst now = new Date();
\t\tconst currentMinutes = now.getHours() * 60 + now.getMinutes();
\t\tconst todayDay = getTodayDayOfWeek();

\t\t// Optimization: Pre-fetch all configs
\t\tconst uniqueCompanyIds = [...new Set(employees.map(e => e.company_id))];
\t\tconst companyConfigs = {};
\t\tfor (const cid of uniqueCompanyIds) {
\t\t\tcompanyConfigs[cid] = await getCompanySchedulerConfig(cid);
\t\t}

\t\t// Optimization: Pre-fetch all today attendances
\t\tconst employeeIds = employees.map(e => e.id);
\t\tconst todayStr = \`\${now.getFullYear()}-\${String(now.getMonth() + 1).padStart(2, '0')}-\${String(now.getDate()).padStart(2, '0')}\`;
\t\t
\t\tconst attendances = await dbSAPPS.Attendance.findAll({
\t\t\twhere: {
\t\t\t\temployee_id: { [Op.in]: employeeIds },
\t\t\t\tdate: todayStr
\t\t\t},
\t\t\tattributes: ["id", "employee_id", "in_time", "out_time", "in_status", "out_status"],
\t\t});
\t\tconst attendanceMap = {};
\t\tattendances.forEach(a => { attendanceMap[a.employee_id] = a; });

\t\t// Optimization: Pre-fetch sent notifications for today
\t\tif (!dbSAPPS.Notification) {
\t\t\tdbSAPPS = require("../../helpers/db");
\t\t\tif (!dbSAPPS.Notification) {
\t\t\t\tthrow new Error("Notification model is not loaded in dbSAPPS");
\t\t\t}
\t\t}
\t\tconst todayStart = new Date(now);
\t\ttodayStart.setHours(0, 0, 0, 0);
\t\t
\t\tconst notifications = await dbSAPPS.Notification.findAll({
\t\t\twhere: {
\t\t\t\temployee_id: { [Op.in]: employeeIds },
\t\t\t\tcreation: { [Op.gte]: todayStart }
\t\t\t},
\t\t\tattributes: ["employee_id", "notification_type"]
\t\t});
\t\tconst notificationMap = {};
\t\tnotifications.forEach(n => {
\t\t\tif (!notificationMap[n.employee_id]) {
\t\t\t\tnotificationMap[n.employee_id] = new Set();
\t\t\t}
\t\t\tnotificationMap[n.employee_id].add(n.notification_type);
\t\t});

\t\tconst newNotificationsToInsert = [];

\t\tfor (const employee of employees) {
\t\t\ttry {
\t\t\t\t// Get company config from cache
\t\t\t\tconst config = companyConfigs[employee.company_id];

\t\t\t\t// Skip if scheduler disabled for this company
\t\t\t\tif (!config || !config.enabled) {
\t\t\t\t\tcontinue;
\t\t\t\t}

\t\t\t\t// Skip if today is not a working day
\t\t\t\tif (!isWorkingDay(config)) {
\t\t\t\t\tchecksPerformed++;
\t\t\t\t\tcontinue;
\t\t\t\t}

\t\t\t\t// Skip if no shift assigned
\t\t\t\tif (!employee.shifttype) {
\t\t\t\t\tcontinue;
\t\t\t\t}

\t\t\t\tconst effectiveShift = getEffectiveShiftTimes(employee.shifttype);
\t\t\t\tconst shiftStartMinutes = timeToMinutes(effectiveShift.startTime);
\t\t\t\tconst shiftEndMinutes = timeToMinutes(effectiveShift.endTime);

\t\t\t\tif (shiftStartMinutes === null || shiftEndMinutes === null) {
\t\t\t\t\tcontinue;
\t\t\t\t}

\t\t\t\t// Get today's attendance from cache
\t\t\t\tconst todayAttendance = attendanceMap[employee.id] || null;
\t\t\t\tconst sentNotifications = notificationMap[employee.id] || new Set();

\t\t\t\t// Base Notification Info
\t\t\t\tconst baseNotif = {
\t\t\t\t\trecipient_emails: null,
\t\t\t\t\tis_sent: false,
\t\t\t\t\tis_read: false,
\t\t\t\t\tworkflow_id: 0,
\t\t\t\t\temployee_id: employee.id,
\t\t\t\t\tcreation: new Date(),
\t\t\t\t\tmodified: new Date(),
\t\t\t\t\tmodified_by: modifiedBy,
\t\t\t\t\towner: owner,
\t\t\t\t};

\t\t\t\t// Check 1: Clock In Reminder
\t\t\t\tif (config.clockInReminderEnabled && !todayAttendance?.in_time) {
\t\t\t\t\tconst reminderTime = shiftStartMinutes - config.clockInReminderMinutesBefore;
\t\t\t\t\tif (currentMinutes >= reminderTime && currentMinutes < shiftStartMinutes) {
\t\t\t\t\t\tif (!sentNotifications.has(NOTIFICATION_TYPES.CLOCK_IN_REMINDER)) {
\t\t\t\t\t\t\tnewNotificationsToInsert.push({
\t\t\t\t\t\t\t\t...baseNotif,
\t\t\t\t\t\t\t\tnotification_type: NOTIFICATION_TYPES.CLOCK_IN_REMINDER
\t\t\t\t\t\t\t});
\t\t\t\t\t\t\tsentNotifications.add(NOTIFICATION_TYPES.CLOCK_IN_REMINDER);
\t\t\t\t\t\t\tnotificationsCreated++;
\t\t\t\t\t\t}
\t\t\t\t\t}
\t\t\t\t}

\t\t\t\t// Check 2: Missed Clock In Alert
\t\t\t\tif (config.missedClockInEnabled && !todayAttendance?.in_time) {
\t\t\t\t\tconst missedTime = shiftStartMinutes + config.missedClockInMinutesAfter;
\t\t\t\t\tif (currentMinutes >= missedTime) {
\t\t\t\t\t\tif (!sentNotifications.has(NOTIFICATION_TYPES.MISSED_CLOCK_IN)) {
\t\t\t\t\t\t\tnewNotificationsToInsert.push({
\t\t\t\t\t\t\t\t...baseNotif,
\t\t\t\t\t\t\t\tnotification_type: NOTIFICATION_TYPES.MISSED_CLOCK_IN
\t\t\t\t\t\t\t});
\t\t\t\t\t\t\tsentNotifications.add(NOTIFICATION_TYPES.MISSED_CLOCK_IN);
\t\t\t\t\t\t\tnotificationsCreated++;
\t\t\t\t\t\t}
\t\t\t\t\t}
\t\t\t\t}

\t\t\t\t// Check 3: Clock Out Reminder
\t\t\t\tif (config.clockOutReminderEnabled && todayAttendance?.in_time && !todayAttendance?.out_time) {
\t\t\t\t\tconst reminderTime = shiftEndMinutes - config.clockOutReminderMinutesBefore;
\t\t\t\t\tif (currentMinutes >= reminderTime && currentMinutes < shiftEndMinutes) {
\t\t\t\t\t\tif (!sentNotifications.has(NOTIFICATION_TYPES.CLOCK_OUT_REMINDER)) {
\t\t\t\t\t\t\tnewNotificationsToInsert.push({
\t\t\t\t\t\t\t\t...baseNotif,
\t\t\t\t\t\t\t\tnotification_type: NOTIFICATION_TYPES.CLOCK_OUT_REMINDER
\t\t\t\t\t\t\t});
\t\t\t\t\t\t\tsentNotifications.add(NOTIFICATION_TYPES.CLOCK_OUT_REMINDER);
\t\t\t\t\t\t\tnotificationsCreated++;
\t\t\t\t\t\t}
\t\t\t\t\t}
\t\t\t\t}

\t\t\t\tchecksPerformed++;
\t\t\t} catch (empError) {
\t\t\t\terrors.push({
\t\t\t\t\temployee_id: employee.id,
\t\t\t\t\temployee_name: employee.employee_name,
\t\t\t\t\terror: empError.message,
\t\t\t\t});
\t\t\t}
\t\t}

\t\t// Bulk insert all valid new notifications
\t\tif (newNotificationsToInsert.length > 0) {
\t\t\tawait dbSAPPS.Notification.bulkCreate(newNotificationsToInsert);
\t\t}

\t\tconsole.log(\`[Clock Reminder Scheduler] Completed: \${notificationsCreated} notifications created, \${checksPerformed} employees checked\`);

\t\tif (errors.length > 0) {
\t\t\tconsole.warn(\`[Clock Reminder Scheduler] \${errors.length} errors occurred\`);
\t\t}

\t\treturn {
\t\t\tsuccess: true,
\t\t\tnotificationsCreated,
\t\t\temployeesChecked: checksPerformed,
\t\t\terrors,
\t\t};
\t} catch (error) {
\t\tconsole.error("[Clock Reminder Scheduler] Fatal error:", error);
\t\treturn {
\t\t\tsuccess: false,
\t\t\tmessage: error.message || "Failed to run clock reminder check",
\t\t};
\t}
}`;

const oldStart = content.indexOf('async function runClockReminderCheck(options = {}) {');
const remainingStr = content.slice(oldStart);
const oldEnd = oldStart + remainingStr.indexOf('\\n}\\n\\n/**\\n * Get all companies') + 2;

content = content.slice(0, oldStart) + newRunClockReminderCheck + content.slice(oldEnd);

fs.writeFileSync(path, content, 'utf8');
console.log('Patch complete. New length:', content.length);
