<?php
class PayrollEngine
{
    private PDO $pdo;

    const OT_WEEKDAY = 1.5;
    const OT_WEEKEND = 2.0;
    const OT_HOLIDAY = 3.0;

    const PIT_BRACKETS = [
        [5_000_000,   0.05],
        [10_000_000,  0.10],
        [18_000_000,  0.15],
        [32_000_000,  0.20],
        [52_000_000,  0.25],
        [80_000_000,  0.30],
        [PHP_INT_MAX, 0.35],
    ];

    const SI_EMPLOYEE_RATE    = 0.105;
    const SI_COMPANY_RATE     = 0.215;
    const PERSONAL_DEDUCTION  = 15_500_000;
    const DEPENDANT_DEDUCTION = 6_200_000;
    const LATE_GRACE_MINUTES  = 5;
    const LATE_BLOCK_MINUTES  = 30;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    // ── Tính ngày công chuẩn ────────────────────────────────────────────
    public function calcWorkingDays(string $from, string $to): int
    {
        $stmt = $this->pdo->prepare(
            "SELECT holiday_date FROM holidays WHERE holiday_date BETWEEN ? AND ?"
        );
        $stmt->execute([$from, $to]);
        $holidays = $stmt->fetchAll(PDO::FETCH_COLUMN);

        $count   = 0;
        $current = new DateTime($from);
        $end     = new DateTime($to);
        while ($current <= $end) {
            if ($current->format('N') != 7 && !in_array($current->format('Y-m-d'), $holidays))
                $count++;
            $current->modify('+1 day');
        }
        return $count;
    }

    // ── Tính lương 1 nhân viên ──────────────────────────────────────────
    public function calculate(int $periodId, int $userId): array
    {
        $period  = $this->getPeriod($periodId);
        $profile = $this->getProfile($userId);
        $salary  = $this->getSalaryComponents($userId);

        // ── Các khoản cố định theo code ───────────────────────────────
        $basicSalary    = (int)($salary['basic']            ?? 0);
        $mealAllow      = (int)($salary['meal']             ?? 0);
        $clothesAllow   = (int)($salary['clothes']          ?? 0);
        $phoneAllow     = (int)($salary['phone']            ?? 0);
        $transportAllow = (int)($salary['transport']        ?? 0);
        $performBonus   = (int)($salary['performance']      ?? 0);
        $attendBonus    = (int)($salary['attendance_bonus'] ?? 0);

        // ── Tổng tất cả khoản earning+bonus, TRỪ chuyên cần ──────────
        $totalSalaryNoAttend = 0;
        foreach ($salary['all_components'] as $comp) {
            if ($comp['component_code'] === 'attendance_bonus') continue;
            if (in_array($comp['component_type'], ['earning', 'bonus']))
                $totalSalaryNoAttend += (int)$comp['amount'];
        }

        $workingDays = (int)$period['working_days'];

        // ── Lương 1 ngày theo TỔNG (trừ chuyên cần) ──────────────────
        $salaryPerDay = $workingDays > 0
            ? round($totalSalaryNoAttend / $workingDays) : 0;

        // ── Lương 1 giờ tổng (trừ chuyên cần) — dùng trừ muộn ───────
        $totalPerHour = round($salaryPerDay / 8);

        // ── Lương 1 giờ CƠ BẢN — chỉ dùng cho OT ────────────────────
        $basicPerDay   = $workingDays > 0 ? round($basicSalary / $workingDays) : 0;
        $salaryPerHour = round($basicPerDay / 8);

        // ── Dữ liệu chấm công / nghỉ phép / OT ───────────────────────
        $att = $this->getAttendanceData($userId, $period['period_from'], $period['period_to']);
        $lv  = $this->getLeaveData($userId, $period['period_from'], $period['period_to']);
        $ot  = $this->getOTData($userId, $period['period_from'], $period['period_to']);

        $actualWorkdays     = (float)($att['actual_workdays']      ?? 0);
        $paidLeaveDays      = (float)($lv['paid_leave_days']        ?? 0);
        $otherPaidLeaveDays = (float)($lv['other_paid_leave_days']  ?? 0);
        $unpaidLeaveDays    = (float)($lv['unpaid_leave_days']      ?? 0);

        $totalLateMinutes  = (float)($att['total_late_minutes']  ?? 0);
        $totalEarlyMinutes = (float)($att['total_early_minutes'] ?? 0);

        // ✅ FIX 1: Tổng phút trừ = trễ + về sớm
        $lateMinutes = $totalLateMinutes + $totalEarlyMinutes;

        $otWeekdayHours = (float)($ot['ot_weekday_hours'] ?? 0);
        $otWeekendHours = (float)($ot['ot_weekend_hours'] ?? 0);
        $otHolidayHours = (float)($ot['ot_holiday_hours'] ?? 0);

        // ✅ FIX 1: is_late_warning = có trễ HOẶC về sớm vượt ngưỡng
        $isLateWarning   = ($lateMinutes > self::LATE_GRACE_MINUTES);
        $lateWarningNote = (string)($att['late_early_dates'] ?? '');

        // ── Tổng ngày hưởng lương ─────────────────────────────────────
        $totalPaidDays = $actualWorkdays + $paidLeaveDays + $otherPaidLeaveDays;

        // ── Đi muộn/về sớm — tính theo lương TỔNG/giờ ────────────────
        [$lateHours, $lateDeduction] = $this->calcLateDeduction($lateMinutes, $totalPerHour);

        // ── KPI ───────────────────────────────────────────────────────
        $kpiData      = $this->getKpiData($userId, $period['period_from'], $period['period_to']);
        $kpiDaysCount = count($kpiData);
        $kpiDeduction = 0;
        foreach ($kpiData as $kDay) {
            if ((int)$kDay['is_deducted'] === 1) {
                $diff = (float)$kDay['salary_per_day'] - (float)$kDay['salary_actual'];
                $kpiDeduction += max(0, $diff);
            }
        }
        $kpiDeduction = (int)round($kpiDeduction);

        // ── Lương cơ bản thực nhận — theo tỷ lệ ngày công ────────────
        $basicReceived = $workingDays > 0
            ? round($basicSalary * ($totalPaidDays / $workingDays)) : 0;

        // ── Tỷ lệ ngày công chung ─────────────────────────────────────
        $allowanceRatio = $workingDays > 0
            ? min(1.0, $totalPaidDays / $workingDays) : 0;

        // ── Tỷ lệ riêng cho ĂN CA (chỉ ngày đi làm thực tế) ──────────
        $mealRatio = $workingDays > 0
            ? min(1.0, $actualWorkdays / $workingDays) : 0;

        // ── Trợ cấp thực nhận ─────────────────────────────────────────
        $mealReceived      = round($mealAllow      * $mealRatio);
        $clothesReceived   = round($clothesAllow   * $allowanceRatio);
        $phoneReceived     = round($phoneAllow     * $allowanceRatio);
        $transportReceived = round($transportAllow * $allowanceRatio);

        // ── Performance bonus ─────────────────────────────────────────
        $performReceived = round($performBonus * $allowanceRatio);

        // ── Các khoản earning+bonus khác ─────────────────────────────
        $otherComponentsReceived = 0;
        foreach ($salary['all_components'] as $comp) {
            $code = $comp['component_code'];
            if (in_array($code, [
                'basic', 'meal', 'clothes', 'phone',
                'transport', 'performance', 'attendance_bonus'
            ])) continue;
            if (!in_array($comp['component_type'], ['earning', 'bonus'])) continue;
            $otherComponentsReceived += round((int)$comp['amount'] * $allowanceRatio);
        }

        // ── Chuyên cần ────────────────────────────────────────────────
        $kpiHasDeduction = $kpiDeduction > 0;
        $attendEligible  = ($allowanceRatio >= 1.0 && !$kpiHasDeduction);
        $attendReceived  = ($attendEligible && $attendBonus > 0) ? $attendBonus : 0;

        // ── OT ────────────────────────────────────────────────────────
        $otWeekdayAmt = round($otWeekdayHours * $salaryPerHour * self::OT_WEEKDAY);
        $otWeekendAmt = round($otWeekendHours * $salaryPerHour * self::OT_WEEKEND);
        $otHolidayAmt = round($otHolidayHours * $salaryPerHour * self::OT_HOLIDAY);
        $totalOtAmt   = $otWeekdayAmt + $otWeekendAmt + $otHolidayAmt;

        // ── Phép tồn ──────────────────────────────────────────────────
        $leaveTotal     = $this->calcAnnualLeave($userId, (int)$period['period_year']);
        $leaveUsed      = $this->getLeaveUsed($userId, (int)$period['period_year']);
        $leaveRemaining = max(0, $leaveTotal - $leaveUsed);
        $leavePayout    = 0;

        // ── BHXH ──────────────────────────────────────────────────────
        $hasInsurance = (int)($profile['has_insurance'] ?? 0);
        $siEmployee   = $hasInsurance ? round($basicSalary * self::SI_EMPLOYEE_RATE) : 0;
        $siCompany    = $hasInsurance ? round($basicSalary * self::SI_COMPANY_RATE)  : 0;

        // ── Thuế TNCN ─────────────────────────────────────────────────
        $dependants         = (int)($profile['dependants'] ?? 0);
        $dependantDeduction = $dependants * self::DEPENDANT_DEDUCTION;

        $grossForTax = $basicReceived
                     + $mealReceived
                     + $clothesReceived
                     + $phoneReceived
                     + $transportReceived
                     + $performReceived
                     + $otherComponentsReceived
                     + $attendReceived
                     + $totalOtAmt;

        $otExcludePit = 0;

        $taxableIncome = max(0,
            $grossForTax - $siEmployee
            - self::PERSONAL_DEDUCTION
            - $dependantDeduction
        );
        $pitAmount = $this->calcPIT($taxableIncome);

        // ── Gross & Net ───────────────────────────────────────────────
        $grossSalary = $grossForTax + $leavePayout;
        $netSalary   = max(0,
            $grossSalary - $siEmployee - $pitAmount - $lateDeduction - $kpiDeduction
        );

        // ── Ghi chú tự động ───────────────────────────────────────────
        $remarkParts = [];
        if ($kpiDeduction > 0)
            $remarkParts[] = "Trừ KPI: " . number_format($kpiDeduction) . " đ ($kpiDaysCount ngày không đạt)";
        if ($lateDeduction > 0) {
            $deductNote = "Trừ muộn/sớm: " . number_format($lateDeduction) . " đ";
            if ($totalLateMinutes > 0)  $deductNote .= " (trễ: {$totalLateMinutes}p";
            if ($totalEarlyMinutes > 0) $deductNote .= ($totalLateMinutes > 0 ? ", " : " (") . "về sớm: {$totalEarlyMinutes}p";
            if ($totalLateMinutes > 0 || $totalEarlyMinutes > 0) $deductNote .= ")";
            $remarkParts[] = $deductNote;
        }

        return [
            'period_id'                  => $periodId,
            'user_id'                    => $userId,
            'basic_salary'               => $basicSalary,
            'working_days_standard'      => $workingDays,
            'salary_per_day'             => $salaryPerDay,
            'salary_per_hour'            => $totalPerHour,
            'basic_salary_per_hour'      => $salaryPerHour,
            'actual_workdays'            => $actualWorkdays,
            'paid_leave_days'            => $paidLeaveDays,
            'other_paid_leave_days'      => $otherPaidLeaveDays,
            'unpaid_leave_days'          => $unpaidLeaveDays,
            'late_early_hours'           => $lateHours,
            'late_early_deduction'       => $lateDeduction,
            'total_paid_days'            => $totalPaidDays,
            'basic_salary_received'      => $basicReceived,
            'meal_allowance'             => $mealAllow,
            'meal_received'              => $mealReceived,
            'clothes_allowance'          => $clothesAllow,
            'clothes_received'           => $clothesReceived,
            'phone_allowance'            => $phoneAllow,
            'phone_received'             => $phoneReceived,
            'transport_allowance'        => $transportAllow,
            'transport_received'         => $transportReceived,
            'performance_bonus'          => $performReceived,
            'attendance_bonus'           => $attendReceived,
            'attendance_bonus_eligible'  => $attendEligible ? 1 : 0,
            'ot_weekday_hours'           => $otWeekdayHours,
            'ot_weekend_hours'           => $otWeekendHours,
            'ot_holiday_hours'           => $otHolidayHours,
            'ot_weekday_amount'          => $otWeekdayAmt,
            'ot_weekend_amount'          => $otWeekendAmt,
            'ot_holiday_amount'          => $otHolidayAmt,
            'total_ot_amount'            => $totalOtAmt,
            'annual_leave_total'         => $leaveTotal,
            'annual_leave_used'          => $leaveUsed,
            'annual_leave_remaining'     => $leaveRemaining,
            'annual_leave_payout'        => $leavePayout,
            'other_income'               => 0,
            'adjustment'                 => 0,
            'other_bonus'                => 0,
            'has_social_insurance'       => $hasInsurance,
            'si_employee'                => $siEmployee,
            'si_company'                 => $siCompany,
            'dependants'                 => $dependants,
            'personal_deduction'         => self::PERSONAL_DEDUCTION,
            'dependant_deduction'        => $dependantDeduction,
            'ot_exclude_pit'             => $otExcludePit,
            'taxable_income'             => $taxableIncome,
            'pit_amount'                 => $pitAmount,
            'late_deduction'             => $lateDeduction,
            'kpi_deduction'              => $kpiDeduction,
            'gross_salary'               => $grossSalary,
            'advance_payment'            => 0,
            'net_salary'                 => $netSalary,
            'pit_adjustment'             => 0,
            'bank_transfer'              => $netSalary,
            'remark'                     => implode('; ', $remarkParts),
            'is_late_warning'            => $isLateWarning ? 1 : 0,
            'late_warning_note'          => $lateWarningNote,
            'manually_adjusted'          => 0,
        ];
    }

    // ── Lấy dữ liệu KPI trong kỳ lương ──────────────────────────────────
    private function getKpiData(int $userId, string $from, string $to): array
    {
        try {
            $stmt = $this->pdo->prepare("
                SELECT kr.salary_actual, kr.salary_per_day,
                       kr.is_deducted,  ka.assign_date
                FROM kpi_results kr
                JOIN kpi_assignments ka ON kr.kpi_assignment_id = ka.id
                WHERE ka.user_id = ? AND ka.assign_date BETWEEN ? AND ?
            ");
            $stmt->execute([$userId, $from, $to]);
            return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
        } catch (\Throwable $e) {
            error_log("getKpiData error uid=$userId: " . $e->getMessage());
            return [];
        }
    }

    // ── Tính số ngày phép theo tháng làm việc thực tế ───────────────────
    private function calcAnnualLeave(int $userId, int $periodYear): int
    {
        $stmt = $this->pdo->prepare("
            SELECT date_joined, annual_leave_total
            FROM employee_profiles WHERE user_id = ?
        ");
        $stmt->execute([$userId]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        $leavePerYear = (int)($row['annual_leave_total'] ?? 12);
        $dateJoined   = $row['date_joined'] ?? null;
        if (!$dateJoined) return $leavePerYear;

        $joined   = new DateTime($dateJoined);
        $joinYear = (int)$joined->format('Y');
        if ($joinYear < $periodYear) return $leavePerYear;
        if ($joinYear > $periodYear) return 0;

        $joinMonth    = (int)$joined->format('m');
        $joinDay      = (int)$joined->format('d');
        $startMonth   = ($joinDay > 1) ? $joinMonth + 1 : $joinMonth;
        $monthsWorked = max(0, 12 - $startMonth + 1);
        return (int)round($monthsWorked * $leavePerYear / 12);
    }

    // ── Chấm công từ attendance_logs ─────────────────────────────────────
    // ✅ FIX 2: loại Chủ nhật + ngày lễ khỏi actual_workdays
    // ✅ FIX 1: gom cả late_dates + early_dates vào late_early_dates
    private function getAttendanceData(int $userId, string $from, string $to): array
    {
        try {
            // Lấy ngày lễ trong kỳ để loại trừ
            $hStmt = $this->pdo->prepare(
                "SELECT holiday_date FROM holidays WHERE holiday_date BETWEEN ? AND ?"
            );
            $hStmt->execute([$from, $to]);
            $holidays = $hStmt->fetchAll(PDO::FETCH_COLUMN);
            $holidayList = empty($holidays)
                ? "'0000-00-00'"
                : implode(',', array_map(fn($d) => $this->pdo->quote($d), $holidays));

            $stmt = $this->pdo->prepare("
                SELECT
                    -- ✅ FIX 2: loại CN (DAYOFWEEK=1) và ngày lễ
                    COUNT(
                        CASE WHEN check_in IS NOT NULL
                             AND DAYOFWEEK(work_date) != 1
                             AND work_date NOT IN ($holidayList)
                        THEN 1 END
                    ) AS actual_workdays,

                    COALESCE(SUM(
                        CASE WHEN is_late = 1
                             AND DAYOFWEEK(work_date) != 1
                             AND work_date NOT IN ($holidayList)
                        THEN late_minutes ELSE 0 END
                    ), 0) AS total_late_minutes,

                    COALESCE(SUM(
                        CASE WHEN early_leave = 1
                             AND DAYOFWEEK(work_date) != 1
                             AND work_date NOT IN ($holidayList)
                        THEN early_leave_minutes ELSE 0 END
                    ), 0) AS total_early_minutes,

                    -- ✅ FIX 1: gom ngày trễ + ngày về sớm
                    GROUP_CONCAT(
                        CASE WHEN (is_late = 1 OR early_leave = 1)
                             AND DAYOFWEEK(work_date) != 1
                             AND work_date NOT IN ($holidayList)
                        THEN CONCAT(
                            DATE_FORMAT(work_date, '%%d/%%m'),
                            CASE WHEN is_late = 1 AND early_leave = 1 THEN '(T+S)'
                                 WHEN is_late = 1   THEN '(T)'
                                 ELSE '(S)' END
                        ) END
                        ORDER BY work_date SEPARATOR ', '
                    ) AS late_early_dates

                FROM attendance_logs
                WHERE user_id = ? AND work_date BETWEEN ? AND ?
            ");
            $stmt->execute([$userId, $from, $to]);
            return $stmt->fetch(PDO::FETCH_ASSOC) ?: $this->emptyAttendance();
        } catch (\Throwable $e) {
            error_log("getAttendanceData error uid=$userId: " . $e->getMessage());
            return $this->emptyAttendance();
        }
    }

    // ── Nghỉ phép từ leave_requests ──────────────────────────────────────
    // ✅ FIX 3: dùng giao kỳ thay vì chỉ start_date BETWEEN
    private function getLeaveData(int $userId, string $from, string $to): array
    {
        try {
            $stmt = $this->pdo->prepare("
                SELECT
                    -- Số ngày thực tế giao với kỳ lương (tránh đếm vượt biên)
                    COALESCE(SUM(
                        CASE WHEN leave_type = 'annual' THEN
                            DATEDIFF(
                                LEAST(end_date,    :to1),
                                GREATEST(start_date, :from1)
                            ) + 1
                        ELSE 0 END
                    ), 0) AS paid_leave_days,

                    COALESCE(SUM(
                        CASE WHEN leave_type IN ('sick', 'other') THEN
                            DATEDIFF(
                                LEAST(end_date,    :to2),
                                GREATEST(start_date, :from2)
                            ) + 1
                        ELSE 0 END
                    ), 0) AS other_paid_leave_days,

                    COALESCE(SUM(
                        CASE WHEN leave_type = 'unpaid' THEN
                            DATEDIFF(
                                LEAST(end_date,    :to3),
                                GREATEST(start_date, :from3)
                            ) + 1
                        ELSE 0 END
                    ), 0) AS unpaid_leave_days

                FROM leave_requests
                WHERE user_id = :uid
                  AND status  = 'approved'
                  AND start_date <= :to4
                  AND end_date   >= :from4
            ");
            $stmt->execute([
                ':uid'   => $userId,
                ':from1' => $from, ':to1' => $to,
                ':from2' => $from, ':to2' => $to,
                ':from3' => $from, ':to3' => $to,
                ':from4' => $from, ':to4' => $to,
            ]);
            return $stmt->fetch(PDO::FETCH_ASSOC) ?: $this->emptyLeave();
        } catch (\Throwable $e) {
            error_log("getLeaveData error uid=$userId: " . $e->getMessage());
            return $this->emptyLeave();
        }
    }

    // ── OT từ overtime_requests ───────────────────────────────────────────
    private function getOTData(int $userId, string $from, string $to): array
    {
        try {
            $stmt = $this->pdo->prepare("
                SELECT
                    COALESCE(SUM(CASE WHEN ot_type = 'weekday'
                        THEN hours ELSE 0 END), 0) AS ot_weekday_hours,
                    COALESCE(SUM(CASE WHEN ot_type = 'weekend'
                        THEN hours ELSE 0 END), 0) AS ot_weekend_hours,
                    COALESCE(SUM(CASE WHEN ot_type = 'holiday'
                        THEN hours ELSE 0 END), 0) AS ot_holiday_hours
                FROM overtime_requests
                WHERE user_id = ? AND status = 'approved'
                  AND ot_date BETWEEN ? AND ?
            ");
            $stmt->execute([$userId, $from, $to]);
            return $stmt->fetch(PDO::FETCH_ASSOC) ?: $this->emptyOT();
        } catch (\Throwable $e) {
            error_log("getOTData error uid=$userId: " . $e->getMessage());
            return $this->emptyOT();
        }
    }

    // ── Số ngày phép annual đã dùng trong năm ───────────────────────────
    private function getLeaveUsed(int $userId, int $year): float
    {
        try {
            $stmt = $this->pdo->prepare("
                SELECT COALESCE(SUM(total_days), 0)
                FROM leave_requests
                WHERE user_id = ? AND status = 'approved'
                  AND leave_type = 'annual' AND YEAR(start_date) = ?
            ");
            $stmt->execute([$userId, $year]);
            return (float)$stmt->fetchColumn();
        } catch (\Throwable $e) {
            error_log("getLeaveUsed error uid=$userId: " . $e->getMessage());
            return 0;
        }
    }

    // ── Block đi muộn/về sớm ────────────────────────────────────────────
    private function calcLateDeduction(float $totalMinutes, int $salaryPerHour): array
    {
        if ($totalMinutes <= self::LATE_GRACE_MINUTES) return [0.0, 0];
        $over      = $totalMinutes - self::LATE_GRACE_MINUTES;
        $blocks    = (int)ceil($over / self::LATE_BLOCK_MINUTES);
        $lateHours = $blocks * (self::LATE_BLOCK_MINUTES / 60);
        $deduction = (int)round($blocks * ($salaryPerHour / 2));
        return [$lateHours, $deduction];
    }

    // ── Thuế TNCN 7 bậc lũy tiến ────────────────────────────────────────
    private function calcPIT(int $taxableIncome): int
    {
        if ($taxableIncome <= 0) return 0;
        $tax = 0; $prev = 0;
        foreach (self::PIT_BRACKETS as [$limit, $rate]) {
            if ($taxableIncome <= $prev) break;
            $tax += (min($taxableIncome, $limit) - $prev) * $rate;
            $prev = $limit;
            if ($taxableIncome <= $limit) break;
        }
        return (int)round($tax);
    }

    // ── Helpers: empty arrays ────────────────────────────────────────────
    private function emptyAttendance(): array
    {
        return [
            'actual_workdays'     => 0,
            'total_late_minutes'  => 0,
            'total_early_minutes' => 0,
            'late_early_dates'    => '',
        ];
    }

    private function emptyLeave(): array
    {
        return [
            'paid_leave_days'       => 0,
            'other_paid_leave_days' => 0,
            'unpaid_leave_days'     => 0,
        ];
    }

    private function emptyOT(): array
    {
        return [
            'ot_weekday_hours' => 0,
            'ot_weekend_hours' => 0,
            'ot_holiday_hours' => 0,
        ];
    }

    // ── DB helpers ───────────────────────────────────────────────────────
    private function getPeriod(int $id): array
    {
        $stmt = $this->pdo->prepare("SELECT * FROM payroll_periods WHERE id = ?");
        $stmt->execute([$id]);
        return $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
    }

    private function getProfile(int $userId): array
    {
        $stmt = $this->pdo->prepare("SELECT * FROM employee_profiles WHERE user_id = ?");
        $stmt->execute([$userId]);
        $profile = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$profile) return [];
        if (!isset($profile['annual_leave_total'])) $profile['annual_leave_total'] = 12;
        if (!isset($profile['dependants']))          $profile['dependants']         = 0;
        if (!isset($profile['has_insurance']))       $profile['has_insurance']      = 0;
        return $profile;
    }

    // ── Lấy TẤT CẢ khoản lương — động, không hardcode ──────────────────
    private function getSalaryComponents(int $userId): array
    {
        $stmt = $this->pdo->prepare("
            SELECT sc.component_code, sc.component_type,
                   sc.component_name,  es.amount
            FROM employee_salaries es
            JOIN salary_components sc ON es.component_id = sc.id
            WHERE es.user_id = ? AND es.is_active = 1
        ");
        $stmt->execute([$userId]);
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

        $map = [];
        foreach ($rows as $r) {
            $map[$r['component_code']] = (int)$r['amount'];
        }
        $map['all_components'] = $rows;
        return $map;
    }
}