<?php
declare(strict_types=1);

namespace App\Modules\HR\Services;

use App\Core\App;
use App\Core\Logger;
use App\Modules\HR\Models\HrAttendance;
use App\Modules\HR\Models\HrHoliday;
use App\Modules\HR\Models\HrEmployeeProfile;

/**
 * Attendance Service
 *
 * Handles:
 *   - Daily attendance recording (check-in/out)
 *   - Hours calculation (actual, overtime, late, early leave)
 *   - Ramadan auto-detection (7h for Muslim employees)
 *   - Holiday and rest day detection
 *   - Monthly summary for payroll
 */
final class AttendanceService
{
    public static function recordCheckIn(int $profileId, string $date, string $time, string $method = 'manual'): array
    {
        $db = App::getInstance()->db();

        $existing = HrAttendance::getForEmployeeDate($profileId, $date);
        if ($existing && $existing['check_in_time'] !== null) {
            return ['success' => false, 'error' => 'تم تسجيل الحضور بالفعل لهذا اليوم'];
        }

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

        $scheduleData = self::getScheduleForEmployee($profileId, $date);
        $isRamadan = self::isRamadanDay($date, $profile->religion ?? null);

        if ($existing) {
            $db->update('hr_attendance', [
                'check_in_time'       => $time,
                'check_in_method'     => $method,
                'is_ramadan_schedule' => $isRamadan ? 1 : 0,
                'status'              => 'present',
                'updated_at'          => date('Y-m-d H:i:s'),
            ], '`id` = ?', [(int) $existing['id']]);
        } else {
            $isHoliday = HrHoliday::isHoliday($date, $profile->religion);
            $isRestDay = self::isRestDay($profileId, $date);

            $status = 'present';
            if ($isHoliday) $status = 'holiday';
            elseif ($isRestDay) $status = 'rest_day';

            $db->insert('hr_attendance', [
                'employee_profile_id' => $profileId,
                'attendance_date'     => $date,
                'check_in_time'       => $time,
                'check_in_method'     => $method,
                'status'              => $status,
                'is_ramadan_schedule' => $isRamadan ? 1 : 0,
                'created_at'          => date('Y-m-d H:i:s'),
                'updated_at'          => date('Y-m-d H:i:s'),
            ]);
        }

        return ['success' => true];
    }

    public static function recordCheckOut(int $profileId, string $date, string $time, string $method = 'manual'): array
    {
        $existing = HrAttendance::getForEmployeeDate($profileId, $date);
        if (!$existing) {
            return ['success' => false, 'error' => 'لم يتم تسجيل الحضور لهذا اليوم'];
        }
        if ($existing['check_in_time'] === null) {
            return ['success' => false, 'error' => 'لم يتم تسجيل وقت الحضور'];
        }

        $db = App::getInstance()->db();

        // Calculate actual hours
        $checkIn = new \DateTime($date . ' ' . $existing['check_in_time']);
        $checkOut = new \DateTime($date . ' ' . $time);
        if ($checkOut < $checkIn) {
            $checkOut->modify('+1 day');
        }
        $diffMinutes = (int) (($checkOut->getTimestamp() - $checkIn->getTimestamp()) / 60);
        $scheduleData = self::getScheduleForEmployee($profileId, $date);
        $breakMinutes = (int) ($scheduleData['break_duration_minutes'] ?? 60);
        $actualMinutes = max(0, $diffMinutes - $breakMinutes);
        $actualHours = bcdiv((string) $actualMinutes, '60', 2);

        // Calculate overtime
        $profile = HrEmployeeProfile::find($profileId);
        $expectedHours = (float) self::getExpectedHours($profile, $scheduleData, $date);
        $overtimeHours = '0.00';
        if (bccomp($actualHours, (string) $expectedHours, 2) > 0) {
            $overtimeHours = bcsub($actualHours, (string) $expectedHours, 2);
        }

        // Calculate late minutes
        $lateMinutes = 0;
        $earlyLeaveMinutes = 0;

        if (!empty($scheduleData['start_time'])) {
            $expectedStart = new \DateTime($date . ' ' . $scheduleData['start_time']);
            $tolerance = (int) ($scheduleData['late_tolerance_minutes'] ?? 15);
            $gracePeriod = clone $expectedStart;
            $gracePeriod->modify("+{$tolerance} minutes");
            if ($checkIn > $gracePeriod) {
                $lateMinutes = (int) (($checkIn->getTimestamp() - $expectedStart->getTimestamp()) / 60);
            }
        }

        $status = $existing['status'];
        if ($lateMinutes > 0 && $status === 'present') {
            $status = 'late';
        }

        $db->update('hr_attendance', [
            'check_out_time'      => $time,
            'check_out_method'    => $method,
            'actual_hours'        => $actualHours,
            'overtime_hours'      => $overtimeHours,
            'late_minutes'        => $lateMinutes,
            'early_leave_minutes' => $earlyLeaveMinutes,
            'status'              => $status,
            'updated_at'          => date('Y-m-d H:i:s'),
        ], '`id` = ?', [(int) $existing['id']]);

        return ['success' => true];
    }

    public static function getExpectedHours($profile, array $scheduleData, string $date): string
    {
        $isRamadan = self::isRamadanDay($date, $profile->religion ?? ($profile['religion'] ?? null));
        if ($isRamadan && ($profile->religion ?? ($profile['religion'] ?? '')) === 'muslim') {
            return $scheduleData['ramadan_hours_per_day'] ?? '7.00';
        }
        return $scheduleData['hours_per_day'] ?? '8.00';
    }

    public static function isRamadanDay(string $date, ?string $religion): bool
    {
        if ($religion !== 'muslim') {
            return false;
        }
        $db = App::getInstance()->db();
        $start = $db->selectOne("SELECT config_value FROM system_config WHERE config_key = 'hr.ramadan.start_date'");
        $end = $db->selectOne("SELECT config_value FROM system_config WHERE config_key = 'hr.ramadan.end_date'");

        if (empty($start['config_value']) || empty($end['config_value'])) {
            return false;
        }

        return $date >= $start['config_value'] && $date <= $end['config_value'];
    }

    public static function getScheduleForEmployee(int $profileId, string $date): array
    {
        $db = App::getInstance()->db();
        $row = $db->selectOne(
            "SELECT ws.* FROM hr_employee_schedules es
             JOIN hr_work_schedules ws ON ws.id = es.schedule_id
             WHERE es.employee_profile_id = ? AND es.effective_from <= ? AND (es.effective_to IS NULL OR es.effective_to >= ?)
             ORDER BY es.effective_from DESC LIMIT 1",
            [$profileId, $date, $date]
        );

        if (!$row) {
            $row = $db->selectOne("SELECT * FROM hr_work_schedules WHERE code = 'STD_6DAY' AND is_active = 1 LIMIT 1");
        }

        return $row ?: ['hours_per_day' => '8.00', 'ramadan_hours_per_day' => '7.00', 'late_tolerance_minutes' => 15];
    }

    public static function isRestDay(int $profileId, string $date): bool
    {
        $schedule = self::getScheduleForEmployee($profileId, $date);
        $dayName = strtolower(date('l', strtotime($date)));
        $config = json_decode($schedule['days_config_json'] ?? '{}', true) ?: [];
        return !($config[$dayName]['is_working'] ?? true);
    }

    public static function getMonthlyWorkingDays(int $profileId, int $year, int $month): int
    {
        $startDate = sprintf('%04d-%02d-01', $year, $month);
        $daysInMonth = (int) date('t', strtotime($startDate));
        $workingDays = 0;

        $profile = HrEmployeeProfile::find($profileId);
        $religion = $profile->religion ?? null;

        for ($d = 1; $d <= $daysInMonth; $d++) {
            $date = sprintf('%04d-%02d-%02d', $year, $month, $d);
            if (!self::isRestDay($profileId, $date) && !HrHoliday::isHoliday($date, $religion)) {
                $workingDays++;
            }
        }

        return $workingDays;
    }
}
