<?php
declare(strict_types=1);

namespace App\Modules\HR\Services;

use App\Core\App;
use App\Core\EventBus;

final class AttendanceViolationService
{
    public static function detectViolations(string $date): array
    {
        $db = App::getInstance()->db();
        $violations = [];

        $attendanceRecords = $db->select(
            "SELECT a.*, e.id as emp_id, e.full_name_ar
             FROM hr_attendance a
             JOIN employees e ON e.id = a.employee_id
             WHERE a.attendance_date = ?",
            [$date]
        );

        foreach ($attendanceRecords as $record) {
            $shift = self::getEmployeeShift((int) $record['employee_id'], $date);
            if (!$shift) continue;

            if (!empty($record['check_in'])) {
                $lateMinutes = self::calculateLateness($record['check_in'], $shift['start_time'], (int) $shift['grace_period_minutes']);
                if ($lateMinutes > 0) {
                    $violations[] = self::createViolation([
                        'employee_id' => (int) $record['employee_id'],
                        'attendance_id' => (int) $record['id'],
                        'violation_date' => $date,
                        'violation_type' => 'late_arrival',
                        'scheduled_time' => $shift['start_time'],
                        'actual_time' => $record['check_in'],
                        'difference_minutes' => $lateMinutes,
                    ]);
                }
            }

            if (!empty($record['check_out'])) {
                $earlyMinutes = self::calculateEarlyLeave($record['check_out'], $shift['end_time'], (int) $shift['early_leave_threshold_minutes']);
                if ($earlyMinutes > 0) {
                    $violations[] = self::createViolation([
                        'employee_id' => (int) $record['employee_id'],
                        'attendance_id' => (int) $record['id'],
                        'violation_date' => $date,
                        'violation_type' => 'early_departure',
                        'scheduled_time' => $shift['end_time'],
                        'actual_time' => $record['check_out'],
                        'difference_minutes' => $earlyMinutes,
                    ]);
                }
            }
        }

        $absentees = self::detectAbsences($date, $attendanceRecords);
        foreach ($absentees as $absentee) {
            $violations[] = self::createViolation([
                'employee_id' => (int) $absentee['employee_id'],
                'violation_date' => $date,
                'violation_type' => 'absence',
                'difference_minutes' => 480,
            ]);
        }

        EventBus::dispatch('attendance.violations_detected', [
            'date' => $date,
            'count' => count($violations),
        ]);

        return $violations;
    }

    private static function getEmployeeShift(int $employeeId, string $date): ?array
    {
        $db = App::getInstance()->db();

        $assignment = $db->selectOne(
            "SELECT s.* FROM hr_shift_assignments sa
             JOIN hr_shifts s ON s.id = sa.shift_id
             WHERE sa.employee_id = ? AND sa.is_active = 1 AND sa.start_date <= ?
             AND (sa.end_date IS NULL OR sa.end_date >= ?)
             ORDER BY sa.start_date DESC LIMIT 1",
            [$employeeId, $date, $date]
        );

        if ($assignment) return $assignment;

        return $db->selectOne(
            "SELECT ws.start_time, ws.end_time, 15 as grace_period_minutes, 15 as early_leave_threshold_minutes
             FROM hr_work_schedules ws
             JOIN employees e ON e.id = ?
             WHERE ws.is_active = 1
             LIMIT 1",
            [$employeeId]
        );
    }

    private static function calculateLateness(string $checkIn, string $shiftStart, int $gracePeriod): int
    {
        $checkInMin = self::timeToMinutes($checkIn);
        $shiftStartMin = self::timeToMinutes($shiftStart) + $gracePeriod;

        $diff = $checkInMin - $shiftStartMin;
        return max(0, $diff);
    }

    private static function calculateEarlyLeave(string $checkOut, string $shiftEnd, int $threshold): int
    {
        $checkOutMin = self::timeToMinutes($checkOut);
        $shiftEndMin = self::timeToMinutes($shiftEnd) - $threshold;

        $diff = $shiftEndMin - $checkOutMin;
        return max(0, $diff);
    }

    private static function detectAbsences(string $date, array $presentRecords): array
    {
        $db = App::getInstance()->db();
        $presentIds = array_column($presentRecords, 'employee_id');

        $dayOfWeek = (int) date('w', strtotime($date));
        if ($dayOfWeek === 5) return [];

        $isHoliday = $db->selectOne(
            "SELECT id FROM hr_holidays WHERE holiday_date = ?",
            [$date]
        );
        if ($isHoliday) return [];

        $onLeave = $db->select(
            "SELECT employee_id FROM hr_leave_requests WHERE status = 'approved' AND ? BETWEEN start_date AND end_date",
            [$date]
        );
        $onLeaveIds = array_column($onLeave, 'employee_id');

        $allActive = $db->select(
            "SELECT id as employee_id FROM employees WHERE is_archived = 0 AND employment_status = 'active'"
        );

        $absentees = [];
        foreach ($allActive as $emp) {
            $empId = (string) $emp['employee_id'];
            if (in_array($empId, $presentIds) || in_array($empId, $onLeaveIds)) continue;
            $absentees[] = $emp;
        }

        return $absentees;
    }

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

        return $db->insert('hr_attendance_violations', [
            'employee_id' => $data['employee_id'],
            'attendance_id' => $data['attendance_id'] ?? null,
            'violation_date' => $data['violation_date'],
            'violation_type' => $data['violation_type'],
            'scheduled_time' => $data['scheduled_time'] ?? null,
            'actual_time' => $data['actual_time'] ?? null,
            'difference_minutes' => $data['difference_minutes'],
        ]);
    }

    public static function getMonthlyViolationSummary(int $employeeId, string $month): array
    {
        $db = App::getInstance()->db();

        $rows = $db->select(
            "SELECT violation_type, COUNT(*) as count, SUM(difference_minutes) as total_minutes
             FROM hr_attendance_violations
             WHERE employee_id = ? AND DATE_FORMAT(violation_date, '%Y-%m') = ?
             GROUP BY violation_type",
            [$employeeId, $month]
        );

        $summary = ['late_arrival' => 0, 'early_departure' => 0, 'absence' => 0, 'total_violations' => 0];
        foreach ($rows as $row) {
            $summary[$row['violation_type']] = (int) $row['count'];
            $summary['total_violations'] += (int) $row['count'];
        }

        return $summary;
    }

    private static function timeToMinutes(string $time): int
    {
        $parts = explode(':', $time);
        return ((int) $parts[0]) * 60 + ((int) ($parts[1] ?? 0));
    }
}
