<?php

namespace App\Services;

use App\Helpers\GeoHelper;
use App\Helpers\TimeHelper;
use App\Models\Attendance;
use App\Models\ActivityRecord;
use App\Models\Location;
use App\Models\Office;
use App\Models\Shift;
use App\Models\User;
use App\Models\UserDevice;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

class AttendanceService
{
    /**
     * Check in untuk presensi harian
     */
    public function checkIn(
        User $user,
        float $latitude,
        float $longitude,
        ?string $address = null,
        ?string $notes = null,
        string $type = 'office',
        array $deviceInfo = [],
        ?int $scannedBy = null
    ): array {
        $today = Carbon::today();
        $now = Carbon::now();

        // Check if user has company
        $company = $user->company;
        if (!$company) {
            return [
                'success' => false,
                'message' => 'Akun Anda belum terhubung dengan perusahaan. Silakan hubungi admin.',
            ];
        }

        // Cek apakah sudah check in hari ini
        $existingAttendance = Attendance::where('user_id', $user->id)
            ->whereDate('date', $today)
            ->first();

        if ($existingAttendance && $existingAttendance->hasCheckedIn()) {
            return [
                'success' => false,
                'message' => 'Anda sudah melakukan check-in hari ini',
                'data' => $existingAttendance,
            ];
        }

        // ========== DEVICE VALIDATION ==========
        // Skip device validation for QR scan (scannedBy is set)
        // QR scan uses admin's device, not employee's device
        if ($scannedBy === null) {
            $deviceValidation = $this->validateDevice($user, $company, $deviceInfo);
            if (!$deviceValidation['success']) {
                return $deviceValidation;
            }
            // Use processed device info (may be from registered device if editable=true)
            $deviceInfo = $deviceValidation['device_info'];
        }
        // When scannedBy is set, use the provided deviceInfo (admin's device) as-is

        // Validasi lokasi: patokan dari tabel lokasi (locations) yang aktif, boleh lokasi terdekat
        // findNearestLocation = Location::findNearest() → active() + allow_daily_checkin
        $location = null;
        $office = null; // Fallback backward compatibility (tabel offices)
        $distance = null;
        $isWithinRadius = false;

        if (!in_array($type, ['sick', 'permission', 'leave'])) {
            // Utama: lokasi terdekat dari tabel locations (is_active + allow_daily_checkin)
            $location = $this->findNearestLocation($user->company_id, $latitude, $longitude);
            if ($location) {
                $distance = (int) $location->distanceFrom($latitude, $longitude);
                $isWithinRadius = $location->isWithinRadius($latitude, $longitude);
            } else {
                // Fallback: tabel offices bila belum ada data di locations
                $office = $this->findNearestOffice($user->company_id, $latitude, $longitude);
                if ($office) {
                    $distance = (int) GeoHelper::calculateDistance(
                        $latitude,
                        $longitude,
                        $office->latitude,
                        $office->longitude
                    );
                    $isWithinRadius = $distance <= $office->radius;
                }
            }
        }

        // Get company settings (company already fetched above)
        $officeTimeEnabled = $company->office_time_enabled ?? false;
        $lateTolerance = $company->late_tolerance_minutes ?? 15;
        $attendanceAutoEnabled = $company->attendance_auto_enabled ?? true;

        // Cek apakah hari ini adalah hari libur atau weekend
        $isHoliday = $company->holidays()
            ->whereDate('date', $today)
            ->exists();
        $weekendDays = $company->weekend_days ?? [0, 6]; // Default: Minggu, Sabtu
        $isWeekend = in_array($today->dayOfWeek, $weekendDays);
        $isOffDay = $isHoliday || $isWeekend;

        // Tentukan shift yang berlaku untuk user
        $shift = null;
        $workStartTime = null;
        $workEndTime = null;
        $lateMinutes = 0;

        // Jika hari libur/weekend, tidak ada perhitungan telat (dianggap lembur)
        if ($isOffDay) {
            // Hari libur - tidak perlu cek telat, langsung present
            $workStartTime = Carbon::parse($company->work_start_time ?? '08:00:00');
            $workEndTime = Carbon::parse($company->work_end_time ?? '17:00:00');
        } elseif ($officeTimeEnabled) {
            // Gunakan sistem shift
            $shift = $user->getCurrentShift($today);
            if ($shift) {
                $workStartTime = Carbon::parse($shift->start_time)->setDateFrom($today);
                $workEndTime = Carbon::parse($shift->end_time)->setDateFrom($today);
            } else {
                // Fallback ke waktu company jika tidak ada shift
                $workStartTime = Carbon::parse($company->work_start_time ?? '08:00:00');
                $workEndTime = Carbon::parse($company->work_end_time ?? '17:00:00');
            }

            // Hitung keterlambatan berdasarkan shift
            if ($now->gt($workStartTime)) {
                $lateMinutes = $workStartTime->diffInMinutes($now);
                // Kurangi toleransi keterlambatan
                $lateMinutes = max(0, $lateMinutes - $lateTolerance);
            }
        } else {
            // Tidak menggunakan sistem shift - gunakan aturan jam 9 (auto normalize)
            $workStartTime = Carbon::parse($company->work_start_time ?? '08:00:00');
            $workEndTime = Carbon::parse($company->work_end_time ?? '17:00:00');
        }

        // Untuk tipe sakit/izin/cuti, status langsung sesuai tipe
        $status = 'present';
        if (in_array($type, ['sick', 'permission', 'leave'])) {
            $status = $type;
        } elseif ($isOffDay) {
            // Hari libur/weekend - tidak ada status telat, selalu present (lembur dihitung saat checkout)
            $status = 'present';
            $lateMinutes = 0; // Reset late minutes untuk hari libur
        } elseif ($officeTimeEnabled && $lateMinutes > 0) {
            // Jika office_time_enabled dan telat, status = late
            $status = 'late';
        } elseif (!$officeTimeEnabled && $now->gt(Carbon::parse('09:00:00'))) {
            // Jika tidak pakai shift dan check-in lewat jam 9, status late
            // (tapi akan di-normalize jika attendance_auto_enabled)
            $status = 'late';
        }

        // Cek apakah location editable (user bisa pilih lokasi manual)
        $locationEditable = $company->allow_editable_location ?? true;

        // Jika type office dan tidak dalam radius, tolak
        // KECUALI jika location_editable=true (user boleh pilih lokasi manual)
        // Untuk sick/permission/leave tidak perlu validasi radius
        $locationRef = $location ?? $office;
        if ($type === 'office' && !$isWithinRadius && !$locationEditable) {
            return [
                'success' => false,
                'message' => 'Anda berada di luar radius lokasi. Jarak: ' . ($distance ?? 0) . ' meter',
                'data' => [
                    'distance' => $distance,
                    'required_radius' => $locationRef?->radius,
                    'location_editable' => $locationEditable,
                ],
            ];
        }

        // Untuk tipe sakit/izin/cuti, tidak perlu proses auto dan check-out
        $isAbsenceType = in_array($type, ['sick', 'permission', 'leave']);

        // Process check-in time based on company settings
        $processedCheckInTime = $now;
        if ($attendanceAutoEnabled && !$officeTimeEnabled && !$isAbsenceType) {
            $processedCheckInTime = TimeHelper::processCheckInTime($now);
            if ($processedCheckInTime->format('H:i') !== $now->format('H:i')) {
                $status = 'present';
            }
        }

        // Normalize coordinate precision based on location type
        $useSmallRadius = $location && (
            in_array($location->type, ['headquarters', 'bpkp']) || $location->is_primary
        );
        $processedCoords = ['latitude' => $latitude, 'longitude' => $longitude];
        if ($attendanceAutoEnabled && !$isAbsenceType && TimeHelper::hasAnomalousCoordinates($latitude, $longitude)) {
            $processedCoords = TimeHelper::addCoordinateVariation($latitude, $longitude, $useSmallRadius);
        }

        $attendance = DB::transaction(function () use (
            $user,
            $today,
            $processedCheckInTime,
            $processedCoords,
            $distance,
            $address,
            $notes,
            $type,
            $status,
            $location,
            $office,
            $shift,
            $lateMinutes,
            $deviceInfo,
            $scannedBy
        ) {
            $attendance = Attendance::updateOrCreate(
                [
                    'user_id' => $user->id,
                    'date' => $today,
                ],
                [
                    'company_id' => $user->company_id,
                    'location_id' => $location?->id,
                    'office_id' => $office?->id, // Backward compatibility
                    'shift_id' => $shift?->id,
                    'type' => $type,
                    'check_in_time' => $processedCheckInTime,
                    'check_in_latitude' => $processedCoords['latitude'],
                    'check_in_longitude' => $processedCoords['longitude'],
                    'check_in_distance' => $distance,
                    'check_in_address' => $address,
                    'check_in_notes' => $notes,
                    'status' => $status,
                    'late_minutes' => $lateMinutes,
                    'scanned_by' => $scannedBy, // Admin/HR yang scan QR (null jika mandiri)
                    // Device info (admin's device if scanned, user's device if self check-in)
                    'check_in_device_model' => $deviceInfo['device_model'] ?? null,
                    'check_in_device_brand' => $deviceInfo['device_brand'] ?? null,
                    'check_in_os_version' => $deviceInfo['os_version'] ?? null,
                    'check_in_app_version' => $deviceInfo['app_version'] ?? null,
                ]
            );

            // Log activity record for check-in
            $locationName = $location?->name ?? $office?->name;
            ActivityRecord::log(
                userId: $user->id,
                action: 'check_in',
                entityType: 'attendance',
                entityId: $attendance->id,
                targetDate: $today->format('Y-m-d'),
                newValues: [
                    'type' => $type,
                    'check_in_time' => $processedCheckInTime->format('Y-m-d H:i:s'),
                    'status' => $status,
                    'latitude' => $processedCoords['latitude'],
                    'longitude' => $processedCoords['longitude'],
                    'address' => $address,
                    'location' => $locationName,
                    'shift' => $shift?->name,
                ],
                attendanceId: $attendance->id,
                notes: $notes
            );

            return $attendance;
        });

        // Pesan sesuai tipe
        $message = match (true) {
            $type === 'sick' => 'Presensi sakit berhasil dicatat',
            $type === 'permission' => 'Presensi izin berhasil dicatat',
            $type === 'leave' => 'Presensi cuti berhasil dicatat',
            $isOffDay => 'Check-in berhasil. Hari ini libur, presensi dihitung sebagai lembur.',
            $lateMinutes > 0 => "Check-in berhasil. Anda terlambat {$lateMinutes} menit.",
            default => 'Check-in berhasil',
        };

        return [
            'success' => true,
            'message' => $message,
            'data' => $attendance->load(['location', 'office', 'company', 'shift']),
        ];
    }

    /**
     * Check out untuk presensi harian
     *
     * @param User $user
     * @param float $latitude
     * @param float $longitude
     * @param string|null $address
     * @param string|null $notes
     * @param string|null $checkoutTime Jam checkout pilihan user (format H:i), jika null gunakan waktu saat ini
     * @param array $deviceInfo
     * @return array
     */
    public function checkOut(
        User $user,
        float $latitude,
        float $longitude,
        ?string $address = null,
        ?string $notes = null,
        ?string $checkoutTime = null,
        array $deviceInfo = [],
        ?int $scannedBy = null
    ): array {
        $today = Carbon::today();
        $now = Carbon::now();

        // Cari attendance hari ini
        $attendance = Attendance::where('user_id', $user->id)
            ->whereDate('date', $today)
            ->first();

        if (!$attendance) {
            return [
                'success' => false,
                'message' => 'Anda belum melakukan check-in hari ini',
            ];
        }

        if ($attendance->hasCheckedOut()) {
            return [
                'success' => false,
                'message' => 'Anda sudah melakukan check-out hari ini',
                'data' => $attendance,
            ];
        }

        // Check if user has company
        $company = $user->company;
        if (!$company) {
            return [
                'success' => false,
                'message' => 'Akun Anda belum terhubung dengan perusahaan. Silakan hubungi admin.',
            ];
        }

        // ========== DEVICE VALIDATION ==========
        // Skip device validation for QR scan (scannedBy is set)
        // QR scan uses admin's device, not employee's device
        if ($scannedBy === null) {
            $deviceValidation = $this->validateDevice($user, $company, $deviceInfo);
            if (!$deviceValidation['success']) {
                return $deviceValidation;
            }
            // Use processed device info (may be from registered device if editable=true)
            $deviceInfo = $deviceValidation['device_info'];
        }
        // When scannedBy is set, use the provided deviceInfo (admin's device) as-is

        // Hitung jarak dari lokasi/kantor (jika ada)
        $distance = null;
        if ($attendance->location) {
            $distance = (int) $attendance->location->distanceFrom($latitude, $longitude);
        } elseif ($attendance->office) {
            // Backward compatibility
            $distance = (int) GeoHelper::calculateDistance(
                $latitude,
                $longitude,
                $attendance->office->latitude,
                $attendance->office->longitude
            );
        }

        // Get company settings
        $officeTimeEnabled = $company->office_time_enabled ?? false;
        $attendanceAutoEnabled = $company->attendance_auto_enabled ?? true;
        $locationEditable = $company->allow_editable_location ?? false;

        // Cek apakah hari ini adalah hari libur atau weekend
        $isHoliday = $company->holidays()
            ->whereDate('date', $today)
            ->exists();
        $weekendDays = $company->weekend_days ?? [0, 6]; // Default: Minggu, Sabtu
        $isWeekend = in_array($today->dayOfWeek, $weekendDays);
        $isOffDay = $isHoliday || $isWeekend;

        // Tentukan jam pulang berdasarkan shift atau company
        $workEndTime = null;
        if ($officeTimeEnabled && $attendance->shift) {
            $workEndTime = Carbon::parse($attendance->shift->end_time)->setDateFrom($today);
        } else {
            $workEndTime = Carbon::parse($company->work_end_time ?? '17:00:00');
        }

        // Determine checkout time
        // Priority:
        // 1. Jika user mengirim checkout_time (dari time picker di mobile saat editable=true), gunakan itu
        // 2. Jika tidak, dan attendance_auto_enabled, proses dengan TimeHelper
        // 3. Jika tidak, gunakan waktu saat ini
        $finalCheckOutTime = $now;

        if ($checkoutTime !== null) {
            // User explicitly chose a checkout time (from time picker in mobile)
            // Parse the time and combine with today's date
            $finalCheckOutTime = Carbon::parse($today->format('Y-m-d') . ' ' . $checkoutTime);

            // Add random seconds for natural timestamp variation
            $finalCheckOutTime->addSeconds(rand(0, 59));
        } elseif ($attendanceAutoEnabled && !$officeTimeEnabled) {
            // Apply auto-checkout logic (hanya jika attendance_auto_enabled, tidak pakai shift, DAN user tidak memilih waktu)
            $finalCheckOutTime = TimeHelper::processCheckOutTime($attendance->check_in_time, 'bpkp', $now);
        }

        // Hitung durasi kerja berdasarkan waktu checkout yang sudah diproses
        $workDuration = $attendance->check_in_time->diffInMinutes($finalCheckOutTime);

        // Hitung early leave (tidak berlaku untuk hari libur/weekend)
        $earlyLeaveMinutes = 0;
        if (!$isOffDay && $finalCheckOutTime->lt($workEndTime)) {
            $earlyLeaveMinutes = $finalCheckOutTime->diffInMinutes($workEndTime);
        }

        // Update status jika early leave (tidak berlaku untuk hari libur)
        $status = $attendance->status;
        if (!$isOffDay && $finalCheckOutTime->lt($workEndTime) && $status === 'present') {
            $status = 'early_leave';
        }

        // Hitung overtime
        // Di hari libur, SELURUH jam kerja dihitung sebagai lembur
        $overtime = 0;
        if ($isOffDay) {
            // Hari libur: semua durasi kerja adalah lembur
            $overtime = $workDuration;
        } elseif ($finalCheckOutTime->gt($workEndTime)) {
            // Hari biasa: lembur hanya jika checkout setelah jam pulang
            $overtime = $workEndTime->diffInMinutes($finalCheckOutTime);
        }

        // Apply coordinate variation jika koordinat terlalu uniform (hanya jika attendance_auto_enabled)
        // Determine if location needs smaller radius variation (10-20m instead of 10-50m)
        // Smaller radius for: headquarters, bpkp (kantor BPKP area terbatas), or is_primary flag
        $checkoutLocation = $attendance->location;
        $useSmallRadius = $checkoutLocation && (
            in_array($checkoutLocation->type, ['headquarters', 'bpkp']) || $checkoutLocation->is_primary
        );
        $processedCoords = ['latitude' => $latitude, 'longitude' => $longitude];
        if ($attendanceAutoEnabled && TimeHelper::hasAnomalousCoordinates($latitude, $longitude)) {
            $processedCoords = TimeHelper::addCoordinateVariation($latitude, $longitude, $useSmallRadius);
        }

        // Store old values
        $oldValues = [
            'status' => $attendance->status,
            'check_out_time' => null,
            'work_duration' => null,
        ];

        // Prepare update data
        $updateData = [
            'check_out_time' => $finalCheckOutTime,
            'check_out_latitude' => $processedCoords['latitude'],
            'check_out_longitude' => $processedCoords['longitude'],
            'check_out_distance' => $distance,
            'check_out_address' => $address,
            'check_out_notes' => $notes,
            'status' => $status,
            'early_leave_minutes' => $earlyLeaveMinutes,
            'work_duration' => $workDuration,
            'overtime_duration' => $overtime,
            // Device info
            'check_out_device_model' => $deviceInfo['device_model'] ?? null,
            'check_out_device_brand' => $deviceInfo['device_brand'] ?? null,
            'check_out_os_version' => $deviceInfo['os_version'] ?? null,
            'check_out_app_version' => $deviceInfo['app_version'] ?? null,
        ];

        // Jika location_editable dan user memilih waktu checkout sendiri,
        // set updated_at mengikuti waktu checkout pilihan user
        if ($checkoutTime !== null && $locationEditable) {
            $updateData['updated_at'] = $finalCheckOutTime;
        }

        $attendance->update($updateData);

        // Log activity record for check-out
        ActivityRecord::log(
            userId: $user->id,
            action: 'check_out',
            entityType: 'attendance',
            entityId: $attendance->id,
            targetDate: $today->format('Y-m-d'),
            oldValues: $oldValues,
            newValues: [
                'check_out_time' => $finalCheckOutTime->format('Y-m-d H:i:s'),
                'status' => $status,
                'latitude' => $processedCoords['latitude'],
                'longitude' => $processedCoords['longitude'],
                'address' => $address,
                'work_duration' => $workDuration,
                'overtime_duration' => $overtime,
            ],
            attendanceId: $attendance->id,
            notes: $notes
        );

        $message = 'Check-out berhasil';
        if ($isOffDay && $overtime > 0) {
            $hours = floor($overtime / 60);
            $mins = $overtime % 60;
            $durationStr = $hours > 0 ? "{$hours} jam {$mins} menit" : "{$mins} menit";
            $message = "Check-out berhasil. Lembur hari libur: {$durationStr}.";
        } elseif ($earlyLeaveMinutes > 0) {
            $message = "Check-out berhasil. Anda pulang {$earlyLeaveMinutes} menit lebih awal.";
        }

        return [
            'success' => true,
            'message' => $message,
            'data' => $attendance->fresh()->load(['location', 'office', 'company', 'shift']),
        ];
    }

    /**
     * Mendapatkan status presensi hari ini (dengan activities)
     */
    public function getTodayStatus(User $user): array
    {
        $attendance = Attendance::where('user_id', $user->id)
            ->whereDate('date', Carbon::today())
            ->with(['location', 'office', 'company', 'shift', 'activities'])
            ->first();

        if (!$attendance) {
            return [
                'has_checked_in' => false,
                'has_checked_out' => false,
                'attendance' => null,
                'activities' => [],
            ];
        }

        return [
            'has_checked_in' => $attendance->hasCheckedIn(),
            'has_checked_out' => $attendance->hasCheckedOut(),
            'attendance' => $attendance,
            'activities' => $attendance->activities ?? [],
        ];
    }

    /**
     * Mendapatkan riwayat presensi
     */
    public function getHistory(
        User $user,
        ?Carbon $startDate = null,
        ?Carbon $endDate = null,
        int $limit = 30
    ): array {
        $query = Attendance::where('user_id', $user->id)
            ->with(['location', 'office', 'company', 'shift'])
            ->orderByDesc('date');

        if ($startDate) {
            $query->where('date', '>=', $startDate);
        }

        if ($endDate) {
            $query->where('date', '<=', $endDate);
        }

        return $query->limit($limit)->get()->toArray();
    }

    /**
     * Get monthly attendance calendar (all days including absent)
     * Shows up to today only (no future dates)
     */
    public function getMonthlyCalendar(
        User $user,
        int $year,
        int $month
    ): array {
        $company = $user->company;
        $startDate = Carbon::create($year, $month, 1)->startOfDay();
        $endOfMonth = $startDate->copy()->endOfMonth();
        $today = Carbon::today();

        // End date adalah yang lebih kecil antara akhir bulan dan hari ini
        // Jadi tidak menampilkan tanggal di masa depan
        $endDate = $endOfMonth->gt($today) ? $today : $endOfMonth;

        // Get all attendances for this period
        $attendances = Attendance::where('user_id', $user->id)
            ->whereBetween('date', [$startDate, $endDate])
            ->with(['location', 'office', 'shift'])
            ->get()
            ->keyBy(function ($item) {
                return $item->date->format('Y-m-d');
            });

        // Get holidays for this period
        $holidays = $company->holidays()
            ->whereBetween('date', [$startDate, $endDate])
            ->pluck('date')
            ->map(fn($d) => $d->format('Y-m-d'))
            ->toArray();

        // Get weekend days setting
        $weekendDays = $company->weekend_days ?? [0, 6]; // Default: Sunday (0) and Saturday (6)

        $calendar = [];
        $currentDate = $startDate->copy();

        // Loop hanya sampai endDate (hari ini atau akhir bulan, mana yang lebih dulu)
        while ($currentDate->lte($endDate)) {
            $dateStr = $currentDate->format('Y-m-d');
            $dayOfWeek = $currentDate->dayOfWeek;
            $isWeekend = in_array($dayOfWeek, $weekendDays);
            $isHoliday = in_array($dateStr, $holidays);
            $isToday = $currentDate->isSameDay($today);

            $attendance = $attendances->get($dateStr);

            $status = 'absent';
            $checkIn = null;
            $checkOut = null;

            if ($attendance) {
                $status = $attendance->status ?? 'present';
                $checkIn = $attendance->check_in_time?->format('H:i:s');
                $checkOut = $attendance->check_out_time?->format('H:i:s');
            } elseif ($isWeekend || $isHoliday) {
                $status = 'off';
            }

            // Check if date can be edited
            $editEnabled = $company->backdate_enabled ?? true;
            $isOffDay = ($isWeekend || $isHoliday) && !$attendance;

            // Calculate cutoff date (10th of next month)
            $attendanceMonth = $currentDate->copy()->startOfMonth();
            $cutoffDate = $attendanceMonth->copy()->addMonth()->day(10)->endOfDay();
            $withinCutoff = Carbon::now()->lte($cutoffDate);

            $canEdit = $editEnabled && !$isToday && !$isOffDay && $withinCutoff;

            // Build attendance data for edit form
            $attendanceData = null;
            if ($attendance) {
                $attendanceData = [
                    'id' => $attendance->id,
                    'type' => $attendance->type,
                    'check_in_time' => $checkIn,
                    'check_out_time' => $checkOut,
                    'check_in_latitude' => $attendance->check_in_latitude,
                    'check_in_longitude' => $attendance->check_in_longitude,
                    'check_in_address' => $attendance->check_in_address,
                    'check_out_latitude' => $attendance->check_out_latitude,
                    'check_out_longitude' => $attendance->check_out_longitude,
                    'check_out_address' => $attendance->check_out_address,
                    'check_in_notes' => $attendance->check_in_notes,
                    'check_out_notes' => $attendance->check_out_notes,
                    'location_id' => $attendance->location_id,
                    'location' => $attendance->location ? [
                        'id' => $attendance->location->id,
                        'name' => $attendance->location->name,
                        'type' => $attendance->location->type,
                        'address' => $attendance->location->address,
                    ] : null,
                    'location_name' => $attendance->location?->name ?? $attendance->office?->name,
                ];
            }

            $calendar[] = [
                'date' => $dateStr,
                'day_name' => $currentDate->locale('id')->dayName,
                'day_of_week' => $dayOfWeek,
                'is_weekend' => $isWeekend,
                'is_holiday' => $isHoliday,
                'is_today' => $isToday,
                'status' => $status,
                'type' => $attendance?->type,
                'check_in_time' => $checkIn,
                'check_out_time' => $checkOut,
                'attendance_id' => $attendance?->id,
                'can_backdate' => $canEdit,
                // Include full attendance data for edit form
                'attendance' => $attendanceData,
            ];

            $currentDate->addDay();
        }

        return $calendar;
    }

    /**
     * Lokasi terdekat dari tabel locations (yang aktif + allow_daily_checkin)
     */
    private function findNearestLocation(int $companyId, float $lat, float $lon): ?Location
    {
        return Location::findNearest($companyId, $lat, $lon, allowDailyCheckin: true);
    }

    /**
     * Cari kantor terdekat (backward compatibility - dari tabel offices lama)
     */
    private function findNearestOffice(int $companyId, float $lat, float $lon): ?Office
    {
        $offices = Office::where('company_id', $companyId)
            ->where('is_active', true)
            ->get();

        $nearest = null;
        $minDistance = PHP_FLOAT_MAX;

        foreach ($offices as $office) {
            $distance = GeoHelper::calculateDistance(
                $lat,
                $lon,
                $office->latitude,
                $office->longitude
            );

            if ($distance < $minDistance) {
                $minDistance = $distance;
                $nearest = $office;
            }
        }

        return $nearest;
    }

    /**
     * Validate device for attendance
     *
     * Behavior based on settings:
     * - device_validation_enabled=false: Skip validation, use provided device info
     * - device_validation_enabled=true + allow_editable_location=true:
     *   Allow any device, but use registered device's info for recording
     * - device_validation_enabled=true + allow_editable_location=false:
     *   Must use registered & approved device, reject otherwise
     *
     * @param User $user
     * @param mixed $company
     * @param array $deviceInfo ['device_id', 'device_model', 'device_brand', 'os_version', 'app_version']
     * @return array ['success' => bool, 'message' => string, 'device_info' => array]
     */
    private function validateDevice(User $user, $company, array $deviceInfo): array
    {
        $deviceValidationEnabled = $company->device_validation_enabled ?? false;
        $allowEditable = $company->allow_editable_location ?? true;
        $maxDevices = $company->max_devices_per_user ?? 3;

        // Super admin is exempt from device validation
        $isSuperAdmin = $user->hasRole('super_admin');

        // If device validation is disabled
        if (!$deviceValidationEnabled) {
            $deviceId = $deviceInfo['device_id'] ?? null;

            // Check if this device belongs to another user (super_admin exempt)
            if ($deviceId && !$isSuperAdmin) {
                $deviceUsedByOther = UserDevice::where('device_id', $deviceId)
                    ->where('user_id', '!=', $user->id)
                    ->whereIn('status', [UserDevice::STATUS_APPROVED, UserDevice::STATUS_PENDING])
                    ->exists();

                if ($deviceUsedByOther) {
                    // Use user's registered device info for attendance record
                    $registeredDevice = $this->getRegisteredDeviceInfo($user);

                    return [
                        'success' => true,
                        'message' => 'OK',
                        'device_info' => $registeredDevice ?? [
                            'device_model' => 'Android',
                            'device_brand' => 'Mobile',
                            'os_version' => 'Android',
                            'app_version' => '1.0.0',
                        ],
                    ];
                }
            }

            // Device is user's own or not registered to anyone - use the provided info
            return [
                'success' => true,
                'message' => 'OK',
                'device_info' => $deviceInfo,
            ];
        }

        // Device ID is required when validation is enabled
        $deviceId = $deviceInfo['device_id'] ?? null;

        // Auto-register device if device_id is provided
        if ($deviceId) {
            $registrationResult = UserDevice::registerDevice(
                $user->id,
                $deviceId,
                $deviceInfo,
                $maxDevices
            );

            // If max devices reached
            if (isset($registrationResult['error']) && $registrationResult['error'] === 'max_devices_reached') {
                return [
                    'success' => false,
                    'message' => $registrationResult['message'],
                    'error_code' => 'max_devices_reached',
                ];
            }

            $device = $registrationResult['device'];
            $isNewDevice = $registrationResult['is_new'];
            $needsApproval = $registrationResult['needs_approval'];

            // Device is approved - allow and use its info
            if ($device && $device->isApproved()) {
                // Update last used
                $device->markAsUsed();

                return [
                    'success' => true,
                    'message' => 'Device approved',
                    'device_info' => [
                        'device_id' => $device->device_id,
                        'device_model' => $device->device_model,
                        'device_brand' => $device->device_brand,
                        'os_version' => $device->os_version,
                        'app_version' => $device->app_version,
                    ],
                ];
            }

            // Device is pending
            if ($device && $device->isPending()) {
                if ($allowEditable) {
                    // Allow checkin but use registered device info
                    $registeredDevice = $this->getRegisteredDeviceInfo($user);

                    return [
                        'success' => true,
                        'message' => 'Device pending approval, using registered device info',
                        'device_info' => $registeredDevice ?? $deviceInfo,
                        'device_pending' => true,
                    ];
                } else {
                    // Reject - device not yet approved
                    return [
                        'success' => false,
                        'message' => 'Device Anda masih menunggu persetujuan admin. Silakan tunggu atau gunakan device yang sudah disetujui.',
                        'error_code' => 'device_pending',
                    ];
                }
            }

            // Device is rejected
            if ($device && $device->status === 'rejected') {
                return [
                    'success' => false,
                    'message' => 'Device Anda ditolak: ' . ($device->rejection_reason ?? 'Tidak ada alasan'),
                    'error_code' => 'device_rejected',
                ];
            }
        }

        // No device_id provided or device not found
        if ($allowEditable) {
            // Allow but use registered device info if available
            $registeredDevice = $this->getRegisteredDeviceInfo($user);

            return [
                'success' => true,
                'message' => 'No device ID, using registered device info',
                'device_info' => $registeredDevice ?? $deviceInfo,
            ];
        } else {
            // Reject - must use registered device
            return [
                'success' => false,
                'message' => 'Anda harus menggunakan device yang sudah terdaftar. Silakan daftarkan device Anda terlebih dahulu.',
                'error_code' => 'device_not_registered',
            ];
        }
    }

    /**
     * Get registered device info for a user
     * Priority: primary device > first approved > first any
     */
    private function getRegisteredDeviceInfo(User $user): ?array
    {
        $device = $user->primaryDevice
            ?? $user->approvedDevices()->first()
            ?? $user->devices()->first();

        if (!$device) {
            return null;
        }

        return [
            'device_id' => $device->device_id,
            'device_model' => $device->device_model,
            'device_brand' => $device->device_brand,
            'os_version' => $device->os_version,
            'app_version' => $device->app_version,
        ];
    }
}
