<?php
// ============================================================
// includes/late_penalty_engine.php
// ============================================================
// The actual Free-lates → Permission → Leave → LOP cascade logic,
// extracted so it can be called from BOTH:
//   - modules/attendance/late_processing.php (HR's manual "Calculate
//     & Apply" button, for re-running/spot-checking a specific month)
//   - cron/process_late_penalties.php (fully automatic, runs itself
//     every month with no one needing to click anything)
// Safe to call more than once for the same month — already-processed
// late days are always skipped (see late_penalty_log).
// ============================================================

/**
 * Runs the late-arrival cascade for every active employee of ONE
 * company, for ONE month/year. Returns a per-employee summary array
 * (empty array if nothing new to process).
 *
 * @param Database $db
 * @param int $companyId
 * @param int $month
 * @param int $year
 * @param int|null $processedBy  user id to attribute the run to (null for the automated cron user)
 */
function processLatePenaltiesForCompany(Database $db, int $companyId, int $month, int $year, ?int $processedBy = null): array {
    $policy = $db->fetchOne("SELECT * FROM permission_policy WHERE company_id=? LIMIT 1", [$companyId]);
    if (!$policy) return []; // company hasn't set up a policy yet — nothing to do

    $maxPermMins   = (int)round(($policy['max_hours_per_month'] ?? 4) * 60);
    $freeLates     = (int)($policy['free_lates_per_month'] ?? 4);
    $deductMinsEa  = (int)($policy['late_deduction_minutes'] ?? 30);
    $latesPerHalf  = max(1, (int)($policy['lates_per_half_day'] ?? 2));
    $lopDaysUnit   = ($policy['lop_unit'] ?? 'half_day') === 'full_day' ? 1.0 : 0.5;

    // Which leave type(s) can the late-cascade deduct from, and in what
    // order? Prefer the company's priority list (Companies → Leave
    // Policy — several types, tried in order, whichever has balance
    // wins) and fall back to the older single fixed leave type
    // (permission_policy.late_leave_type_id) if no priority list has
    // been configured yet, so existing setups keep working unchanged.
    $lateLeaveTypeIds = [];
    try {
        $lateLeaveTypeIds = array_column($db->fetchAll(
            "SELECT leave_type_id FROM company_leave_policy WHERE company_id=? AND use_for_late_deduction=1 AND is_active=1 ORDER BY late_deduction_priority ASC",
            [$companyId]
        ), 'leave_type_id');
    } catch (Throwable $e) { /* migration_company_leave_policy.sql not run yet — fall back below */ }
    if (!$lateLeaveTypeIds && !empty($policy['late_leave_type_id'])) {
        $lateLeaveTypeIds = [$policy['late_leave_type_id']];
    }

    $results = [];
    $employees = $db->fetchAll("SELECT id, first_name, last_name, employee_id AS emp_code FROM employees WHERE company_id=? AND status='active'", [$companyId]);

    foreach ($employees as $emp) {
        $empId = $emp['id'];

        $lateDays = $db->fetchAll(
            "SELECT m.muster_date, m.late_minutes
               FROM muster m
              WHERE m.employee_id = ?
                AND MONTH(m.muster_date) = ? AND YEAR(m.muster_date) = ?
                AND m.is_late = 1
                AND NOT EXISTS (
                    SELECT 1 FROM late_regularizations lr
                     WHERE lr.employee_id = m.employee_id AND lr.date = m.muster_date AND lr.status = 'approved'
                )
                AND NOT EXISTS (
                    SELECT 1 FROM late_penalty_log lpl
                     WHERE lpl.employee_id = m.employee_id AND lpl.muster_date = m.muster_date
                )
              ORDER BY m.muster_date",
            [$empId, $month, $year]
        );
        if (!$lateDays) continue;

        $alreadyCount = (int)$db->fetchValue(
            "SELECT COUNT(*) FROM late_penalty_log WHERE employee_id=? AND MONTH(muster_date)=? AND YEAR(muster_date)=?",
            [$empId, $month, $year]
        );
        $postPermissionCount = (int)$db->fetchValue(
            "SELECT COUNT(*) FROM late_penalty_log WHERE employee_id=? AND MONTH(muster_date)=? AND YEAR(muster_date)=? AND action IN ('leave_deduct','lop')",
            [$empId, $month, $year]
        );

        $usedPermMins = (int)($db->fetchValue(
            "SELECT COALESCE(SUM(duration_minutes),0) FROM permission_requests WHERE employee_id=? AND MONTH(permission_date)=? AND YEAR(permission_date)=? AND status!='rejected'",
            [$empId, $month, $year]
        ) ?? 0);

        // Remaining balance for EVERY eligible leave type (not just one) —
        // the deduction step below picks whichever one, in priority
        // order, actually has ≥0.5 remaining that month.
        $leaveBalances = [];
        foreach ($lateLeaveTypeIds as $ltId) {
            $bal = $db->fetchOne("SELECT allocated,used FROM employee_leave_balance WHERE employee_id=? AND leave_type_id=? AND year=?", [$empId, $ltId, $year]);
            $leaveBalances[$ltId] = $bal ? ((float)$bal['allocated'] - (float)$bal['used']) : 0.0;
        }

        $empSummary = ['name' => $emp['first_name'].' '.$emp['last_name'], 'code' => $emp['emp_code'], 'free'=>0, 'permission'=>0, 'leave'=>0.0, 'lop'=>0.0];

        foreach ($lateDays as $ld) {
            $seq = ++$alreadyCount;
            $date = $ld['muster_date'];
            $lateMins = (int)$ld['late_minutes'];

            if ($seq <= $freeLates) {
                $db->execute("INSERT INTO late_penalty_log (employee_id,muster_date,late_minutes,sequence_no,action,processed_by) VALUES (?,?,?,?,?,?)",
                    [$empId,$date,$lateMins,$seq,'free',$processedBy]);
                $empSummary['free']++;

            } elseif ($usedPermMins < $maxPermMins) {
                $deduct = min($deductMinsEa, $maxPermMins - $usedPermMins);
                $db->execute(
                    "INSERT INTO permission_requests (employee_id,permission_date,from_time,to_time,duration_minutes,reason,status,approved_by) VALUES (?,?,'00:00:00','00:00:00',?,?,'approved',?)",
                    [$empId, $date, $deduct, 'Auto-deducted: late arrival', $processedBy]
                );
                $prId = $db->lastInsertId();
                $usedPermMins += $deduct;
                $db->execute("INSERT INTO late_penalty_log (employee_id,muster_date,late_minutes,sequence_no,action,minutes_deducted,permission_request_id,processed_by) VALUES (?,?,?,?,?,?,?,?)",
                    [$empId,$date,$lateMins,$seq,'permission_deduct',$deduct,$prId,$processedBy]);
                $empSummary['permission']++;

            } else {
                $postPermissionCount++;
                if ($postPermissionCount % $latesPerHalf === 0) {
                    // Try each eligible leave type in priority order —
                    // whichever one actually has ≥0.5 remaining wins.
                    $chosenType = null;
                    foreach ($lateLeaveTypeIds as $ltId) {
                        if (($leaveBalances[$ltId] ?? 0.0) >= 0.5) { $chosenType = $ltId; break; }
                    }

                    if ($chosenType) {
                        $db->execute(
                            "INSERT INTO leave_applications (employee_id,leave_type_id,from_date,to_date,days,is_half_day,reason,status,approved_by) VALUES (?,?,?,?,0.5,1,?,'approved',?)",
                            [$empId, $chosenType, $date, $date, 'Auto-deducted: excessive late arrivals', $processedBy]
                        );
                        $laId = $db->lastInsertId();
                        $existingBal = $db->fetchOne("SELECT id FROM employee_leave_balance WHERE employee_id=? AND leave_type_id=? AND year=?", [$empId,$chosenType,$year]);
                        if ($existingBal) {
                            $db->execute("UPDATE employee_leave_balance SET used = used + 0.5 WHERE id=?", [$existingBal['id']]);
                        } else {
                            $db->execute("INSERT INTO employee_leave_balance (employee_id,leave_type_id,year,allocated,used) VALUES (?,?,?,0,0.5)", [$empId,$chosenType,$year]);
                        }
                        $leaveBalances[$chosenType] -= 0.5;
                        $db->execute("INSERT INTO late_penalty_log (employee_id,muster_date,late_minutes,sequence_no,action,days_deducted,leave_type_id,leave_application_id,processed_by) VALUES (?,?,?,?,?,?,?,?,?)",
                            [$empId,$date,$lateMins,$seq,'leave_deduct',0.5,$chosenType,$laId,$processedBy]);
                        $empSummary['leave'] += 0.5;
                        notify($db, $empId, 'Late Arrival Penalty', "Half a day of leave was deducted due to repeated late arrivals on $date.", 'attendance');
                    } else {
                        $existingSummary = $db->fetchOne("SELECT id FROM muster_summary WHERE employee_id=? AND month=? AND year=?", [$empId,$month,$year]);
                        if ($existingSummary) {
                            $db->execute("UPDATE muster_summary SET lop_days = lop_days + ? WHERE id=?", [$lopDaysUnit, $existingSummary['id']]);
                        } else {
                            $db->execute("INSERT INTO muster_summary (employee_id,month,year,lop_days) VALUES (?,?,?,?)", [$empId,$month,$year,$lopDaysUnit]);
                        }
                        $db->execute("INSERT INTO late_penalty_log (employee_id,muster_date,late_minutes,sequence_no,action,days_deducted,processed_by) VALUES (?,?,?,?,?,?,?)",
                            [$empId,$date,$lateMins,$seq,'lop',$lopDaysUnit,$processedBy]);
                        $empSummary['lop'] += $lopDaysUnit;
                        $unitLabel = $lopDaysUnit == 1.0 ? 'a full day' : 'half a day';
                        notify($db, $empId, 'Late Arrival Penalty', "$unitLabel was marked LOP (Loss of Pay) due to repeated late arrivals on $date with no leave balance left.", 'attendance');
                    }
                } else {
                    $db->execute("INSERT INTO late_penalty_log (employee_id,muster_date,late_minutes,sequence_no,action,processed_by) VALUES (?,?,?,?,?,?)",
                        [$empId,$date,$lateMins,$seq,'leave_deduct',$processedBy]);
                }
            }
        }

        if ($empSummary['free'] + $empSummary['permission'] + $empSummary['leave'] + $empSummary['lop'] > 0) {
            $results[] = $empSummary;
        }
    }

    return $results;
}
