<?php
declare(strict_types=1);

namespace App\Modules\HR\Services;

use App\Core\App;

/**
 * Overtime Calculation Service
 *
 * Egyptian Labour Law Art. 85:
 *   Day overtime: 135% of hourly rate
 *   Night overtime (7PM-7AM): 170% of hourly rate
 *
 * Hourly rate = (basic_salary + overtime-affecting allowances) / (working_days * hours_per_day)
 */
final class OvertimeService
{
    /**
     * Calculate overtime amount for given hours.
     *
     * @param string $basicSalary       Employee basic salary
     * @param string $overtimeBase      Total of salary components that affect overtime
     * @param int    $workingDaysMonth  Working days in the month
     * @param string $hoursPerDay       Expected hours per day
     * @param string $dayOvertimeHours  Day overtime hours
     * @param string $nightOvertimeHours Night overtime hours
     */
    public static function calculate(
        string $basicSalary,
        string $overtimeBase,
        int $workingDaysMonth,
        string $hoursPerDay,
        string $dayOvertimeHours,
        string $nightOvertimeHours
    ): array {
        $config = self::loadConfig();

        // Hourly rate = overtimeBase / (workingDays * hoursPerDay)
        $totalMonthlyHours = bcmul((string) $workingDaysMonth, $hoursPerDay, 2);
        if (bccomp($totalMonthlyHours, '0', 2) <= 0) {
            return [
                'hourly_rate'           => '0.00',
                'day_overtime_amount'   => '0.00',
                'night_overtime_amount' => '0.00',
                'total_overtime_amount' => '0.00',
                'day_hours'             => $dayOvertimeHours,
                'night_hours'           => $nightOvertimeHours,
            ];
        }

        $hourlyRate = bcdiv($overtimeBase, $totalMonthlyHours, 4);

        // Day overtime = hours * hourlyRate * dayMultiplier
        $dayRate = bcmul($hourlyRate, $config['day_multiplier'], 4);
        $dayAmount = bcmul($dayOvertimeHours, $dayRate, 2);

        // Night overtime = hours * hourlyRate * nightMultiplier
        $nightRate = bcmul($hourlyRate, $config['night_multiplier'], 4);
        $nightAmount = bcmul($nightOvertimeHours, $nightRate, 2);

        $totalAmount = bcadd($dayAmount, $nightAmount, 2);

        return [
            'hourly_rate'           => $hourlyRate,
            'day_rate'              => $dayRate,
            'night_rate'            => $nightRate,
            'day_overtime_amount'   => $dayAmount,
            'night_overtime_amount' => $nightAmount,
            'total_overtime_amount' => $totalAmount,
            'day_hours'             => $dayOvertimeHours,
            'night_hours'           => $nightOvertimeHours,
            'day_multiplier'        => $config['day_multiplier'],
            'night_multiplier'      => $config['night_multiplier'],
        ];
    }

    public static function submitRequest(array $data): array
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $typeId = (int) ($data['overtime_type_id'] ?? 0);
        $type = $db->selectOne("SELECT * FROM hr_overtime_types WHERE id = ?", [$typeId]);
        if (!$type) {
            return ['success' => false, 'error' => 'نوع العمل الإضافي غير موجود'];
        }

        $hours = (string) ($data['hours'] ?? '0');
        if (bccomp($hours, '0', 2) <= 0) {
            return ['success' => false, 'error' => 'يجب تحديد عدد ساعات صحيح'];
        }

        if (bccomp($hours, (string) $type['max_hours_per_day'], 2) > 0) {
            return ['success' => false, 'error' => 'تجاوز الحد الأقصى اليومي: ' . $type['max_hours_per_day'] . ' ساعات'];
        }

        $requestId = $db->insert('hr_overtime_requests', [
            'employee_profile_id' => (int) $data['employee_profile_id'],
            'overtime_type_id' => $typeId,
            'request_date' => $data['request_date'] ?? date('Y-m-d'),
            'start_time' => $data['start_time'] ?? null,
            'end_time' => $data['end_time'] ?? null,
            'hours' => $hours,
            'reason' => $data['reason'] ?? null,
            'status' => 'pending',
            'requested_by' => $employee ? (int) $employee->id : null,
            'created_at' => date('Y-m-d H:i:s'),
        ]);

        return ['success' => true, 'request_id' => $requestId];
    }

    public static function approve(int $requestId): array
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $request = $db->selectOne("SELECT * FROM hr_overtime_requests WHERE id = ?", [$requestId]);
        if (!$request || $request['status'] !== 'pending') {
            return ['success' => false, 'error' => 'الطلب غير موجود أو تم معالجته'];
        }

        $db->update('hr_overtime_requests', [
            'status' => 'approved',
            'approved_by' => $employee ? (int) $employee->id : null,
            'approved_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$requestId]);

        return ['success' => true];
    }

    public static function reject(int $requestId, string $reason = ''): array
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $request = $db->selectOne("SELECT * FROM hr_overtime_requests WHERE id = ?", [$requestId]);
        if (!$request || $request['status'] !== 'pending') {
            return ['success' => false, 'error' => 'الطلب غير موجود أو تم معالجته'];
        }

        $db->update('hr_overtime_requests', [
            'status' => 'rejected',
            'rejection_reason' => $reason,
            'approved_by' => $employee ? (int) $employee->id : null,
            'approved_at' => date('Y-m-d H:i:s'),
        ], 'id = ?', [$requestId]);

        return ['success' => true];
    }

    public static function calculateOvertimeForPayroll(int $profileId, int $year, int $month): array
    {
        $db = App::getInstance()->db();

        $startDate = sprintf('%04d-%02d-01', $year, $month);
        $endDate = date('Y-m-t', strtotime($startDate));

        $requests = $db->select(
            "SELECT r.*, t.rate_multiplier, t.name_en
             FROM hr_overtime_requests r
             JOIN hr_overtime_types t ON t.id = r.overtime_type_id
             WHERE r.employee_profile_id = ? AND r.status = 'approved'
             AND r.request_date BETWEEN ? AND ?",
            [$profileId, $startDate, $endDate]
        );

        $totalHours = '0.00';
        $details = [];
        foreach ($requests as $req) {
            $totalHours = bcadd($totalHours, (string) $req['hours'], 2);
            $details[] = [
                'date' => $req['request_date'],
                'hours' => $req['hours'],
                'multiplier' => $req['rate_multiplier'],
                'type' => $req['name_en'],
            ];
        }

        return ['total_hours' => $totalHours, 'details' => $details];
    }

    private static function loadConfig(): array
    {
        $db = App::getInstance()->db();

        // Try loading from hr_overtime_types table
        $normalType = $db->selectOne("SELECT rate_multiplier FROM hr_overtime_types WHERE name_en = 'Normal Overtime' LIMIT 1");
        $nightType = $db->selectOne("SELECT rate_multiplier FROM hr_overtime_types WHERE name_en = 'Night Overtime' LIMIT 1");

        if ($normalType || $nightType) {
            return [
                'day_multiplier'   => (string) ($normalType['rate_multiplier'] ?? '1.50'),
                'night_multiplier' => (string) ($nightType['rate_multiplier'] ?? '1.75'),
                'night_start_hour' => 19,
                'night_end_hour'   => 7,
            ];
        }

        // Fallback to system_config
        $rows = $db->select(
            "SELECT config_key, config_value FROM system_config WHERE config_key LIKE 'hr.overtime.%'"
        );

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

        return [
            'day_multiplier'   => $cfg['day_rate_multiplier'] ?? '1.35',
            'night_multiplier' => $cfg['night_rate_multiplier'] ?? '1.70',
            'night_start_hour' => (int) ($cfg['night_start_hour'] ?? 19),
            'night_end_hour'   => (int) ($cfg['night_end_hour'] ?? 7),
        ];
    }
}
