<?php
declare(strict_types=1);

namespace App\Modules\HR\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;
use App\Modules\HR\Models\HrEmployeeProfile;
use App\Modules\HR\Models\HrLeaveBalance;
use App\Modules\HR\Models\HrEmployeeLoan;

/**
 * End of Service Calculation Service
 *
 * Egyptian Labour Law severance:
 *   First 5 years: 0.5 month salary per year
 *   After 5 years: 1.0 month salary per year
 *   Based on last salary
 *
 * Also calculates:
 *   - Notice period compensation (2 months < 10y, 3 months >= 10y)
 *   - Remaining leave payout
 *   - Outstanding loan deductions
 *   - Net settlement
 *
 * Maternity protection: blocks termination during maternity or 1 year after (Art.91)
 *
 * All arithmetic uses bcmath with scale=2.
 */
final class EndOfServiceService
{
    public static function calculate(int $profileId, string $terminationType, string $effectiveDate): array
    {
        $db = App::getInstance()->db();
        $config = self::loadConfig();

        $profile = HrEmployeeProfile::find($profileId);
        if (!$profile) {
            return ['success' => false, 'error' => 'الموظف غير موجود'];
        }

        // Maternity protection check
        if ($terminationType === 'termination') {
            $maternityCheck = self::checkMaternityProtection($profile);
            if ($maternityCheck !== null) {
                return ['success' => false, 'error' => $maternityCheck];
            }
        }

        $hireDate = $profile->hire_date;
        $yearsOfService = (float) HrEmployeeProfile::getYearsOfService($hireDate);
        $lastBasicSalary = $profile->basic_salary ?? '0.00';

        // Calculate total salary (basic + all active allowances)
        $salaryDetails = \App\Modules\HR\Models\HrEmployeeSalaryDetail::getForEmployee((int) $profile->id);
        $totalSalary = $lastBasicSalary;
        foreach ($salaryDetails as $comp) {
            if ($comp['type'] === 'earning' && !$comp['is_system_calculated']) {
                $totalSalary = bcadd($totalSalary, $comp['amount'] ?? '0.00', 2);
            }
        }

        // Severance calculation
        $yearsFirst5 = min($yearsOfService, 5.0);
        $yearsAfter5 = max(0, $yearsOfService - 5.0);

        $severanceFirst5 = bcmul(
            bcmul($lastBasicSalary, $config['severance_first_5y_multiplier'], 4),
            number_format($yearsFirst5, 2, '.', ''),
            2
        );
        $severanceAfter5 = bcmul(
            bcmul($lastBasicSalary, $config['severance_after_5y_multiplier'], 4),
            number_format($yearsAfter5, 2, '.', ''),
            2
        );
        $totalSeverance = bcadd($severanceFirst5, $severanceAfter5, 2);

        // Notice period compensation
        $noticePeriodMonths = $yearsOfService >= 10
            ? (int) $config['notice_period_gte_10y_months']
            : (int) $config['notice_period_under_10y_months'];
        $noticeCompensation = bcmul($totalSalary, (string) $noticePeriodMonths, 2);

        // Remaining leave payout
        $year = (int) date('Y');
        $balances = HrLeaveBalance::getForEmployee((int) $profile->id, $year);
        $remainingLeaveDays = '0.0';
        foreach ($balances as $bal) {
            if ($bal['leave_code'] === 'annual') {
                $remainingLeaveDays = $bal['remaining_days'] ?? '0.0';
                break;
            }
        }
        $dailyRate = bcdiv($totalSalary, '30', 4);
        $leaveCompensation = bcmul($dailyRate, $remainingLeaveDays, 2);

        // Total entitlement
        $totalEntitlement = bcadd(bcadd($totalSeverance, $noticeCompensation, 2), $leaveCompensation, 2);

        // Deductions (outstanding loans)
        $activeLoans = HrEmployeeLoan::getActiveForEmployee((int) $profile->id);
        $totalLoanDeductions = '0.00';
        $deductionsDetail = [];
        foreach ($activeLoans as $loan) {
            $remaining = $loan['remaining_amount'] ?? '0.00';
            $totalLoanDeductions = bcadd($totalLoanDeductions, $remaining, 2);
            $deductionsDetail[] = [
                'type'   => 'loan',
                'loan_id'=> (int) $loan['id'],
                'description' => HrEmployeeLoan::getLoanTypes()[$loan['loan_type']] ?? $loan['loan_type'],
                'amount' => $remaining,
            ];
        }

        $totalDeductions = $totalLoanDeductions;
        $netSettlement = bcsub($totalEntitlement, $totalDeductions, 2);
        if (bccomp($netSettlement, '0', 2) < 0) {
            $netSettlement = '0.00';
        }

        $calculationJson = [
            'years_of_service'             => number_format($yearsOfService, 2),
            'years_first_5'                => number_format($yearsFirst5, 2),
            'years_after_5'                => number_format($yearsAfter5, 2),
            'severance_multiplier_first_5' => $config['severance_first_5y_multiplier'],
            'severance_multiplier_after_5' => $config['severance_after_5y_multiplier'],
            'notice_period_months'         => $noticePeriodMonths,
            'daily_rate'                   => $dailyRate,
            'remaining_leave_days'         => $remainingLeaveDays,
            'deductions'                   => $deductionsDetail,
        ];

        return [
            'success'                    => true,
            'profile_id'                 => $profileId,
            'termination_type'           => $terminationType,
            'effective_date'             => $effectiveDate,
            'years_of_service'           => number_format($yearsOfService, 2),
            'last_basic_salary'          => $lastBasicSalary,
            'last_total_salary'          => $totalSalary,
            'severance_first_5y'         => $severanceFirst5,
            'severance_after_5y'         => $severanceAfter5,
            'total_severance'            => $totalSeverance,
            'notice_period_compensation' => $noticeCompensation,
            'remaining_leave_days'       => $remainingLeaveDays,
            'leave_compensation'         => $leaveCompensation,
            'total_entitlement'          => $totalEntitlement,
            'deductions_json'            => $deductionsDetail,
            'total_deductions'           => $totalDeductions,
            'net_settlement'             => $netSettlement,
            'calculation_json'           => $calculationJson,
        ];
    }

    private static function checkMaternityProtection($profile): ?string
    {
        if (($profile->gender ?? '') !== 'female') {
            return null;
        }

        $db = App::getInstance()->db();
        // Check if there's an approved maternity leave in last 15 months
        $cutoff = date('Y-m-d', strtotime('-15 months'));
        $maternity = $db->selectOne(
            "SELECT lr.id FROM hr_leave_requests lr
             JOIN hr_leave_types lt ON lt.id = lr.leave_type_id
             WHERE lr.employee_profile_id = ? AND lt.code = 'maternity'
               AND lr.status = 'approved' AND lr.end_date >= ?
               AND lr.is_archived = 0
             LIMIT 1",
            [(int) $profile->id, $cutoff]
        );

        if ($maternity) {
            return 'لا يمكن إنهاء خدمة الموظفة أثناء إجازة الوضع أو خلال سنة من العودة (مادة 91)';
        }

        return null;
    }

    private static function loadConfig(): array
    {
        $db = App::getInstance()->db();
        $rows = $db->select(
            "SELECT config_key, config_value FROM system_config WHERE config_key LIKE 'hr.eos.%'"
        );

        $cfg = [];
        foreach ($rows as $row) {
            $key = str_replace('hr.eos.', '', $row['config_key']);
            $cfg[$key] = $row['config_value'];
        }

        return [
            'severance_first_5y_multiplier'   => $cfg['severance_first_5y_multiplier'] ?? '0.5',
            'severance_after_5y_multiplier'   => $cfg['severance_after_5y_multiplier'] ?? '1.0',
            'notice_period_under_10y_months'  => $cfg['notice_period_under_10y_months'] ?? '2',
            'notice_period_gte_10y_months'    => $cfg['notice_period_gte_10y_months'] ?? '3',
        ];
    }
}
