<?php
// PayrollProcessor.php
declare(strict_types=1);

require_once __DIR__ . DIRECTORY_SEPARATOR . 'DisbursementsManager.php';

class PayrollProcessor
{
    private PDO $pdo;
    private DisbursementsManager $disbursementsManager;

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

    /**
     * Processes payroll for a given period, region, and optional transaction type/employee.
     *
     * @param string $payPeriodMonth
     * @param int $payPeriodYear
     * @param string $region 'all' or specific region name
     * @param string|null $transactionTypeFilter 'Salary', 'Wage', or null for all types.
     * @param string|null $empNoToProcess Optional: specific employee number to process
     * @param array $customAdditions Global custom additions (if applicable)
     * @param array $customDeductions Global custom deductions (if applicable)
     * @return int Number of employees for whom payroll was processed.
     * @throws Exception If payroll processing fails.
     */
    public function processPayroll(
        string $payPeriodMonth,
        int $payPeriodYear,
        string $region = 'all',
        ?string $transactionTypeFilter = null,
        $empNoToProcess = null,
        array $customAdditions = [],
        array $customDeductions = []
    ): int {
        $processedCount = 0;

        error_log("PayrollProcessor: Entered processPayroll method.");
        error_log("PayrollProcessor: customAdditions received: " . print_r($customAdditions, true));
        error_log("PayrollProcessor: customDeductions received: " . print_r($customDeductions, true));
        error_log("PayrollProcessor: transactionTypeFilter received: " . ($transactionTypeFilter ?? 'null'));


        $this->pdo->beginTransaction();
        try {
            // --- 1. Delete Existing Disbursements for the specific criteria ---
            $deleteSql = "DELETE d FROM disbursements d
                          JOIN employee_details ed ON d.emp_no = ed.emp_no
                          WHERE d.pay_period_month = :pay_period_month
                          AND d.pay_period_year = :pay_period_year";
            $deleteParams = [
                ':pay_period_month' => $payPeriodMonth,
                ':pay_period_year' => $payPeriodYear
            ];

            if ($region !== 'all' && !empty($region)) {
                $deleteSql .= " AND ed.region = :region";
                $deleteParams[':region'] = $region;
            }

            if ($transactionTypeFilter !== null) {
                $deleteSql .= " AND d.transaction_type = :transaction_type_filter";
                $deleteParams[':transaction_type_filter'] = $transactionTypeFilter;
            }

            if ($empNoToProcess !== null) {
                $deleteSql .= " AND d.emp_no = :emp_no_delete";
                $deleteParams[':emp_no_delete'] = $empNoToProcess;
            }

            error_log("PayrollProcessor Delete SQL: " . $deleteSql);
            error_log("PayrollProcessor Delete Params: " . print_r($deleteParams, true));

            $stmtDelete = $this->pdo->prepare($deleteSql);
            $stmtDelete->execute($deleteParams);
            error_log("PayrollProcessor: Deleted " . $stmtDelete->rowCount() . " existing disbursement records.");
            // --- End Delete Logic ---

            // --- 2. Fetch Employees for Payroll Calculation ---
            $sql = "
                SELECT
                    emp_no, basic_salary, transport_allowance, facilitation_allowance, medical_allowance, housing_allowance, allowances, nssf_no, tin_no,
                    surname, given_name, othername, email, role,
                    date_of_hire, contract_period, bank_name, ac_name, attendance_days, attendance_hours, ac_no, phone, nin,
                    region, payroll_type, savings
                FROM
                    employee_details
                WHERE
                    status = 'active'
            ";
            $params = [];

            if ($empNoToProcess !== null) {
                $sql .= " AND emp_no = :emp_no_to_process";
                $params[':emp_no_to_process'] = $empNoToProcess;
                error_log("PayrollProcessor: Processing specific employee with emp_no: " . $empNoToProcess);
            } elseif ($region !== 'all' && !empty($region)) {
                $sql .= " AND region = :region";
                $params[':region'] = $region;
                error_log("PayrollProcessor: Processing employees in region: " . $region);
            } else {
                error_log("PayrollProcessor: Processing all active employees.");
            }

            if ($transactionTypeFilter !== null) {
                $sql .= " AND payroll_type = :payroll_type_filter";
                $params[':payroll_type_filter'] = $transactionTypeFilter;
                error_log("PayrollProcessor: Filtering by payroll_type: " . $transactionTypeFilter);
            }

            error_log("PayrollProcessor Employee Fetch SQL: " . $sql);
            error_log("PayrollProcessor Employee Fetch Params: " . print_r($params, true));

            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            $employees = $stmt->fetchAll(PDO::FETCH_ASSOC);

            if (empty($employees)) {
                $this->pdo->rollBack();
                error_log("PayrollProcessor: No employees found for the given criteria (emp_no: " . ($empNoToProcess ?? 'N/A') . ", region: " . $region . ", transactionTypeFilter: " . ($transactionTypeFilter ?? 'N/A') . ").");
                return 0;
            }

            error_log("PayrollProcessor: Found " . count($employees) . " active employees to process.");

            $firstDayOfMonth = date("Y-m-01", strtotime("1 {$payPeriodMonth} {$payPeriodYear}"));
            $lastDayOfMonth = date("Y-m-t", strtotime("1 {$payPeriodMonth} {$payPeriodYear}"));
            $currentDate = date('Y-m-d');
            $currentTimestamp = date('Y-m-d H:i:s');

            foreach ($employees as $employee) {
                error_log("Processing employee: " . $employee['emp_no'] . " with payroll_type: " . ($employee['payroll_type'] ?? 'N/A'));
                try {
                    $emp_no = $employee['emp_no'];
                    $employee_full_name = trim($employee['surname'] . ' ' . $employee['given_name'] . ' ' . $employee['othername']);
                    $employee_payroll_type = $employee['payroll_type'] ?? 'Salary'; // Default to 'Salary' if not set

                    // --- Earnings ---
                    $basic_salary = (float)($employee['basic_salary'] ?? 0.00);
                    $allowances_from_db = (float)($employee['allowances'] ?? 0.00);
                    $facilitation_allowance = (float)($employee['facilitation_allowance'] ?? 0.00);
                    $housing_allowance = (float)($employee['housing_allowance'] ?? 0.00);
                    $transport_allowance = (float)($employee['transport_allowance'] ?? 0.00);
                    $medical_allowance = (float)($employee['medical_allowance'] ?? 0.00);

                    // Calculate total custom additions
                    $totalCustomAdditions = array_sum(array_column($customAdditions, 'amount'));
                    error_log("Employee {$emp_no}: Total Custom Additions: " . $totalCustomAdditions);

                    // Calculate non-taxable custom additions (e.g., 'reimbursement')
                    $non_taxable_reimbursements = 0.00;
                    foreach ($customAdditions as $addition) {
                        if (isset($addition['description']) && strtolower($addition['description']) === 'reimbursement') {
                            $non_taxable_reimbursements += (float)($addition['amount'] ?? 0.00);
                        }
                    }
                    error_log("Employee {$emp_no}: Non-taxable Reimbursements: " . $non_taxable_reimbursements);


                    $other_allowances_total = $allowances_from_db + $facilitation_allowance + $housing_allowance +
                                              $transport_allowance + $medical_allowance + $totalCustomAdditions;

                    $gross_pay = $basic_salary + $other_allowances_total;

                    // --- Deductions ---
                    $nssf_employee_contribution_rate = 0.05;
                    $nssf_max_wage_cap = 2000000;

                    // Calculate the portion of gross pay subject to NSSF/PAYE
                    // Subtract facilitation_allowance and non_taxable_reimbursements from the gross for NSSF/PAYE calculation base
                    $subject_to_nssf_paye_gross = $gross_pay - $facilitation_allowance - $non_taxable_reimbursements;
                    $subject_to_nssf_paye_gross = max(0.00, $subject_to_nssf_paye_gross); // Ensure it doesn't go negative

                    $actual_wage_for_nssf = min($subject_to_nssf_paye_gross, $nssf_max_wage_cap);

                    $nssf_employee_contribution = 0.00;
                    // NSSF Employee Contribution is based on payroll_type
                    if ($employee_payroll_type === 'Salary') {
                        $nssf_employee_contribution = round($actual_wage_for_nssf * $nssf_employee_contribution_rate, 2);
                    }
                    error_log("Employee {$emp_no}: NSSF Employee Contribution: " . $nssf_employee_contribution . " (Payroll Type: " . $employee_payroll_type . ")");

                    // Calculate taxable income for PAYE (using the adjusted gross base)
                    // $taxable_income = $subject_to_nssf_paye_gross - $nssf_employee_contribution; // Changed this line if company policy allows NSSF deduction before PAYE
                    $taxable_income = $subject_to_nssf_paye_gross; // Use the gross pay base for PAYE calculation
                    $taxable_income = max(0.00, $taxable_income); // Ensure taxable income doesn't go negative
                    
                    // --- URA Official PAYE Calculation Guidelines ---
                    $paye = 0.00;
                    if ($taxable_income > 235000) {
                    if ($taxable_income <= 335000) {
                    // (Taxable Income - 235,000 UGX) x 10%
                    $paye = ($taxable_income - 235000) * 0.10;
                    } elseif ($taxable_income <= 410000) {
                   // (Taxable Income - 335,000 UGX) x 20% + 10,000 UGX
                   // 10,000 UGX is the tax from the previous bracket (10% of 100,000)
                   $paye = (($taxable_income - 335000) * 0.20) + 10000;
                    } elseif ($taxable_income <= 10000000) {
                   // (Taxable Income - 410,000 UGX) x 30% + 25,000 UGX
                    // 25,000 UGX is the accumulated tax from previous brackets (10k from first + 15k from second)
                   $paye = (($taxable_income - 410000) * 0.30) + 25000;
                    } else {
                     // Above 10,000,000 UGX
                   // (Taxable Income - 410,000 UGX) x 30% + 25,000 UGX + (Taxable Income - 10,000,000 UGX) x 10%
                   $paye = (($taxable_income - 410000) * 0.30) + 25000 + (($taxable_income - 10000000) * 0.10);
    }
}
                    $paye = max(0.00, round($paye, 2));

                   // Calculate Local Service Tax (only for Salary employees in September or October)
$local_service_tax = 0.00;
if (($payPeriodMonth === 'September' || $payPeriodMonth === 'October') && $employee_payroll_type === 'Salary') {
    // Calculate monthly net salary after PAYE but before other deductions for LST calculation
    $monthly_net_for_lst = $taxable_income - $paye;
    
    // Determine Local Service Tax bracket based on monthly net salary
    if ($monthly_net_for_lst > 100000 && $monthly_net_for_lst <= 200000) {
        $local_service_tax = 2500.00;
    } elseif ($monthly_net_for_lst > 200000 && $monthly_net_for_lst <= 300000) {
        $local_service_tax = 5000.00;
    } elseif ($monthly_net_for_lst > 300000 && $monthly_net_for_lst <= 400000) {
        $local_service_tax = 10000.00;
    } elseif ($monthly_net_for_lst > 400000 && $monthly_net_for_lst <= 500000) {
        $local_service_tax = 15000.00;
    } elseif ($monthly_net_for_lst > 500000 && $monthly_net_for_lst <= 600000) {
        $local_service_tax = 20000.00;
    } elseif ($monthly_net_for_lst > 600000 && $monthly_net_for_lst <= 700000) {
        $local_service_tax = 30000.00;
    } elseif ($monthly_net_for_lst > 700000 && $monthly_net_for_lst <= 800000) {
        $local_service_tax = 35000.00;
    } elseif ($monthly_net_for_lst > 800000 && $monthly_net_for_lst <= 900000) {
        $local_service_tax = 40000.00;
    } elseif ($monthly_net_for_lst > 900000 && $monthly_net_for_lst <= 1000000) {
        $local_service_tax = 45000.00;
    } elseif ($monthly_net_for_lst > 1000000) {
        $local_service_tax = 50000.00;
    }
    
    // Ensure the tax doesn't exceed the employee's remaining pay
    // if ($local_service_tax > ($gross_pay - $total_deductions + $local_service_tax)) {
    //     $local_service_tax = max(0, $gross_pay - $total_deductions);
    // }
    
    error_log("Employee {$emp_no}: Local Service Tax applied. Net for LST: {$monthly_net_for_lst}, Tax: {$local_service_tax} UGX");
}
// End of local service tax modification

                    $advance_deduction = 0.00;
                    $stmt_advance = $this->pdo->prepare("
                        SELECT id, monthly_deduction_amount, outstanding_balance
                        FROM employee_advances
                        WHERE emp_no = :emp_no AND status = 'active'
                        AND repayment_start_date <= :current_date
                        AND outstanding_balance > 0
                        ORDER BY id ASC LIMIT 1
                    ");
                    $stmt_advance->execute([
                        ':emp_no' => $emp_no,
                        ':current_date' => $currentDate
                    ]);
                    $active_advance = $stmt_advance->fetch(PDO::FETCH_ASSOC);

                    if ($active_advance) {
                        $deduct_amount = min((float)$active_advance['monthly_deduction_amount'], (float)$active_advance['outstanding_balance']);
                        $advance_deduction = $deduct_amount;

                        $new_balance = (float)$active_advance['outstanding_balance'] - $deduct_amount;
                        $update_advance_status = ($new_balance <= 0) ? 'fully_repaid' : 'active';

                        $stmt_update_advance = $this->pdo->prepare("
                            UPDATE employee_advances
                            SET outstanding_balance = :new_balance, status = :new_status
                            WHERE id = :id
                        ");
                        $stmt_update_advance->execute([
                            ':new_balance' => $new_balance,
                            ':new_status' => $update_advance_status,
                            ':id' => (int)$active_advance['id']
                        ]);
                        error_log("Employee {$emp_no}: Advance updated. Deducted: {$deduct_amount}, New Balance: {$new_balance}, Status: {$update_advance_status}");
                    }

                    $totalCustomDeductions = array_sum(array_column($customDeductions, 'amount'));
                    error_log("Employee {$emp_no}: Total Custom Deductions: " . $totalCustomDeductions);


                    $other_deductions_total = $totalCustomDeductions;

                    $total_deductions = $nssf_employee_contribution + $paye + $local_service_tax +
                                         $advance_deduction + $other_deductions_total;

                    $net_salary = $gross_pay - $total_deductions;

                    // --- Employer Contributions ---
                    $nssf_employer_contribution = 0.00; // Initialize to 0
                    // Apply NSSF Employer Contribution ONLY if employee payroll_type is 'Salary'
                    if ($employee_payroll_type === 'Salary') {
                        $nssf_employer_contribution_rate = 0.10;
                        // Use the same $actual_wage_for_nssf base as for employee contribution
                        $nssf_employer_contribution = round($actual_wage_for_nssf * $nssf_employer_contribution_rate, 2);
                    }
                    error_log("Employee {$emp_no}: NSSF Employer Contribution: " . $nssf_employer_contribution . " (Payroll Type: " . $employee_payroll_type . ")");


                    // --- Attendance Data (from employee_details table) ---
                    $attendance_hours = (float)($employee['attendance_hours'] ?? 0.00);
                    $attendance_days = (float)($employee['attendance_days'] ?? 0.00);
                    $attendance_amount = 0.00;

                    // --- Prepare data for insertion/update into disbursements table ---
                    $disbursementData = [
                        'emp_no'                   => $emp_no,
                        'employee_name'            => $employee_full_name,
                        'transaction_date'         => $currentDate,
                        'transaction_type'         => $employee_payroll_type, // Use the resolved payroll type
                        'disbursed_by'             => 'System (Payroll Processor)',
                        'pay_period_start'         => $firstDayOfMonth,
                        'pay_period_end'           => $lastDayOfMonth,
                        'pay_period_month'         => $payPeriodMonth,
                        'pay_period_year'          => $payPeriodYear,
                        'computed_on'              => $currentTimestamp,
                        'attendance_hours'         => $attendance_hours,
                        'attendance_days'          => $attendance_days,
                        'attendance_amount'        => $attendance_amount,
                        'basic_salary'             => $basic_salary,
                        'facilitation_allowance'   => $facilitation_allowance,
                        'medical_allowance'        => $medical_allowance,
                        'housing_allowance'        => $housing_allowance,
                        'transport_allowance'      => $transport_allowance,
                        'other_allowances_total'   => $other_allowances_total,
                        'custom_additions_json'    => json_encode(array_values($customAdditions)),
                        'gross_pay'                => $gross_pay,
                        'nssf_employee_contribution' => $nssf_employee_contribution,
                        'paye'                     => $paye,
                        'local_service_tax'        => $local_service_tax,
                        'advance_deduction'        => $advance_deduction,
                        'other_deductions_total'   => $other_deductions_total,
                        'custom_deductions_json'   => json_encode(array_values($customDeductions)),
                        'total_deductions'         => $total_deductions,
                        'taxable_income'           => $taxable_income,
                        'net_salary'               => $net_salary,
                        'nssf_employer_contribution' => $nssf_employer_contribution, // This will now be 0 for 'Wage' if the condition holds
                        'status'                   => 'pending',
                        'savings' => (float)($employee['savings'] ?? 0.00),  // ADD this line
                        'remarks'                  => null
                    ];

                    error_log("Employee {$emp_no}: Final Disbursement Data (transaction_type): " . $disbursementData['transaction_type']);
                    error_log("Employee {$emp_no}: Final Disbursement Data (nssf_employee_contribution): " . $disbursementData['nssf_employee_contribution']);
                    error_log("Employee {$emp_no}: Final Disbursement Data (nssf_employer_contribution): " . $disbursementData['nssf_employer_contribution']);
                    error_log("Employee {$emp_no}: Final Disbursement Data (total_deductions): " . $disbursementData['total_deductions']);
                    error_log("Employee {$emp_no}: Final Disbursement Data (net_salary): " . $disbursementData['net_salary']);
                    error_log("Employee {$emp_no}: Final Disbursement Data (custom_additions_json): " . $disbursementData['custom_additions_json']);
                    error_log("Employee {$emp_no}: Final Disbursement Data (custom_deductions_json): " . $disbursementData['custom_deductions_json']);


                    // Check for existing disbursement only for the specific employee, period, and transaction type
                    $stmt_check_disbursement = $this->pdo->prepare("
                        SELECT id
                        FROM disbursements
                        WHERE emp_no = :emp_no
                        AND pay_period_month = :pay_period_month
                        AND pay_period_year = :pay_period_year
                        AND transaction_type = :transaction_type
                    ");
                    $stmt_check_disbursement->execute([
                        ':emp_no'           => $emp_no,
                        ':pay_period_month' => $payPeriodMonth,
                        ':pay_period_year'  => $payPeriodYear,
                        ':transaction_type' => $employee_payroll_type
                    ]);
                    $existing_disbursement = $stmt_check_disbursement->fetch(PDO::FETCH_ASSOC);

                    if ($existing_disbursement) {
                        $this->disbursementsManager->updateDisbursement((int)$existing_disbursement['id'], $disbursementData);
                        error_log("Updated disbursement record for employee: " . $emp_no . " (Type: " . $employee_payroll_type . ")");
                    } else {
                        $this->disbursementsManager->insertDisbursement($disbursementData);
                        error_log("Inserted new disbursement record for employee: " . $emp_no . " (Type: " . $employee_payroll_type . ")");
                    }
                    $processedCount++;

                } catch (Exception $e) {
                    error_log("Error processing payroll for employee {$employee['emp_no']} ({$employee_full_name}): " . $e->getMessage());
                }
            }

            $this->pdo->commit();
            return $processedCount;

        } catch (Exception $e) {
            $this->pdo->rollBack();
            error_log("Critical payroll processing error: " . $e->getMessage() . " - " . $e->getTraceAsString());
            throw new Exception("Payroll calculation failed due to a critical error: " . $e->getMessage());
        }
    }
}
// End of PayrollProcessor.php