<?php

namespace App\Http\Controllers;

use App\Models\Registration;
use Illuminate\Http\Request;
use App\Models\Holiday;
use App\Models\Setting;
use App\Models\TimeSlot;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Gd\Driver;
use Illuminate\Validation\Rule; // Import Rule for dynamic validation

class RegistrationController extends Controller
{
    // Removed unused generateKtpFilename and generateKtpPath methods

    private function generateUniqueBookingCode()
    {
        $attempt = 0;
        $maxAttempts = 10;
        
        do {
            // Generate a 6 character alphanumeric string
            $code = strtoupper(Str::random(6));
            
            // Check if this code already exists
            $exists = Registration::where('booking_code', $code)->exists();
            
            $attempt++;
        } while ($exists && $attempt < $maxAttempts);
        
        // If we couldn't generate a unique code after max attempts
        if ($exists) {
            \Log::error('Could not generate unique booking code after ' . $maxAttempts . ' attempts');
            throw new \Exception('Could not generate unique booking code');
        }
        
        return $code;
    }

    private function checkExistingRegistration($nik)
    {
        $twoWeeksAgo = Carbon::now()->subWeeks(2);
        
        $existingRegistration = Registration::where('nik', $nik)
            ->where('created_at', '>=', $twoWeeksAgo)
            ->first();

        if ($existingRegistration) {
            $nextAvailableDate = Carbon::parse($existingRegistration->created_at)
                ->addWeeks(2)
                ->format('d-m-Y');

            throw new \Exception("Anda sudah melakukan registrasi dalam 2 minggu terakhir. Silakan coba lagi setelah tanggal {$nextAvailableDate}.");
        }

        return true;
    }

    public function terms()
    {
        return view('terms');
    }

    public function showForm()
    {
        return view('register');
    }

    public function checkQuota($date, $time)
    {
        // Set timezone explicitly
        date_default_timezone_set('Asia/Jakarta');
        
        // Parse dates using Carbon with explicit timezone
        $currentDate = Carbon::now()->timezone('Asia/Jakarta');
        $selectedDate = Carbon::createFromFormat('Y-m-d', $date, 'Asia/Jakarta');
        
        // Check if holiday
        if(Holiday::whereDate('date', $selectedDate->format('Y-m-d'))->exists()) {
            return response()->json(['available' => false, 'message' => 'Hari Libur']);
        }

        // Compare dates at start of day
        $todayJkt = $currentDate->copy()->startOfDay();
        $selectedStartOfDay = $selectedDate->copy()->startOfDay();

        if ($selectedStartOfDay->lt($todayJkt)) {
            return response()->json(['available' => false, 'message' => 'Tanggal tidak valid']);
        }

        // Get registration date range settings
        $registrationStartDate = Setting::where('key', 'registration_start_date')->first();
        $registrationEndDate = Setting::where('key', 'registration_end_date')->first();
        
        // Check if date is outside the registration period
        if ($registrationStartDate) {
            $startLimit = Carbon::parse($registrationStartDate->value)->startOfDay();
            if ($selectedStartOfDay->lt($startLimit)) {
                return response()->json(['available' => false, 'message' => 'Diluar periode registrasi']);
            }
        }
        
        if ($registrationEndDate) {
            $endLimit = Carbon::parse($registrationEndDate->value)->startOfDay();
            if ($selectedStartOfDay->gt($endLimit)) {
                return response()->json(['available' => false, 'message' => 'Diluar periode registrasi']);
            }
        }

        // Get time slot info from the new TimeSlot model
        $timeSlot = TimeSlot::where('time', $time)->first();
        
        // If time slot doesn't exist or is inactive
        if (!$timeSlot || !$timeSlot->is_active) {
            return response()->json(['available' => false, 'message' => 'Slot waktu tidak tersedia', 'disabled' => true]);
        }
        
        // Check if quota is set to 0 (disabled slot)
        $maxQuota = $timeSlot->quota;
        if ($maxQuota == 0) {
            return response()->json(['available' => false, 'message' => 'Slot waktu tidak tersedia', 'disabled' => true]);
        }
        
        // Count existing registrations
        $count = Registration::whereDate('visit_date', $date)
                            ->where('visit_time', $time)
                            ->count();

        // For backward compatibility, if using old settings system and no time slot found
        if (!$timeSlot) {
            // Convert time format (e.g., "08:00" to "quota_08_00")
            $quotaKey = 'quota_' . str_replace(':', '_', $time);
            
            // Get quota setting
            $setting = Setting::where('key', $quotaKey)->first();
            $maxQuota = $setting ? $setting->value : 0;
            
            // Check if quota is set to 0 (disabled slot)
            if ($maxQuota == 0) {
                return response()->json(['available' => false, 'message' => 'Slot waktu tidak tersedia', 'disabled' => true]);
            }
        }

        // Final availability check
        $isAvailable = $count < $maxQuota;
        $remainingSlots = $maxQuota - $count;
        
        return response()->json([
            'available' => $isAvailable,
            'quota' => $maxQuota,
            'used' => $count,
            'debug' => [
                'date' => $date,
                'time' => $time,
                'today' => $todayJkt->format('Y-m-d'),
                'selected' => $selectedDate->format('Y-m-d')
            ]
        ]);
    }

    public function store(Request $request)
    {
        try {
            // 1. Define custom validation messages
            $customMessages = [
                'name.required' => 'Nama lengkap wajib diisi',
                'gender.required' => 'Jenis kelamin wajib dipilih',
                'gender.in' => 'Jenis kelamin harus L atau P',
                'nik.required' => 'Nomor KTP wajib diisi',
                'nik.digits' => 'Nomor KTP harus terdiri dari 16 digit angka',
                'no_kk.required' => 'Nomor KK wajib diisi',
                'no_kk.digits' => 'Nomor KK harus terdiri dari 16 digit angka',
                'phone.required' => 'Nomor telepon wajib diisi',
                'phone.regex' => 'Format nomor telepon harus diawali dengan 62',
                'address.required' => 'Alamat wajib diisi',
                'email.email' => 'Format email tidak valid',
                'visit_date.required' => 'Tanggal kunjungan wajib diisi',
                'visit_date.date' => 'Format tanggal kunjungan tidak valid',
                'visit_date.after_or_equal' => 'Tanggal kunjungan tidak boleh kurang dari hari ini',
                'visit_time.required' => 'Jam kunjungan wajib diisi',
                'visit_time.in' => 'Jam kunjungan tidak valid atau tidak tersedia', // Updated message
                'ktp_image.required' => 'Foto KTP wajib diunggah',
                'ktp_image.image' => 'File harus berupa gambar',
                'ktp_image.mimes' => 'Format foto KTP harus jpeg, png, atau jpg',
                'ktp_image.max' => 'Ukuran foto KTP maksimal 5MB', // Updated message
                'g-recaptcha-response.required' => 'Mohon verifikasi reCAPTCHA'
            ];
            
            // Validate KTP image before main validation
            if ($request->hasFile('ktp_image')) {
                $ktpImage = $request->file('ktp_image');
                
                if (!$ktpImage->isValid()) {
                    \Log::error('KTP Image validation error: Invalid file upload', ['error' => $ktpImage->getErrorMessage()]);
                    return back()
                        ->withErrors(['ktp_image' => 'Upload gambar KTP tidak valid. Silakan coba lagi dengan file lain.'])
                        ->withInput();
                }
                
                // Check file extension directly
                $extension = strtolower($ktpImage->getClientOriginalExtension());
                if (!in_array($extension, ['jpg', 'jpeg', 'png'])) {
                    \Log::error('KTP Image validation error: Invalid extension', ['extension' => $extension]);
                    return back()
                        ->withErrors(['ktp_image' => 'Format foto KTP harus jpeg, png, atau jpg. Format yang diunggah: ' . $extension])
                        ->withInput();
                }
                
                // Check file size manually
                $maxSize = 5120; // 5MB in kilobytes
                if ($ktpImage->getSize() > $maxSize * 1024) {
                    return back()
                        ->withErrors(['ktp_image' => 'Ukuran foto KTP maksimal 5MB'])
                        ->withInput();
                }
            }

            // Check registration period
            $registrationStartDate = Setting::where('key', 'registration_start_date')->first();
            $registrationEndDate = Setting::where('key', 'registration_end_date')->first();
            
            if ($registrationStartDate && $registrationEndDate) {
                $startLimit = Carbon::parse($registrationStartDate->value)->startOfDay();
                $endLimit = Carbon::parse($registrationEndDate->value)->startOfDay();
                $selectedDate = Carbon::parse($request->visit_date)->startOfDay();
                
                if ($selectedDate->lt($startLimit) || $selectedDate->gt($endLimit)) {
                    return back()
                        ->withErrors(['visit_date' => 'Tanggal kunjungan diluar periode pendaftaran yang diperbolehkan'])
                        ->withInput();
                }
            }
            
            // Get active time slots for validation
            $activeTimeSlots = TimeSlot::active()->pluck('time')->toArray();

            // 2. Validate request
            $request->validate([
                'name' => 'required',
                'gender' => 'required|in:L,P',
                'nik' => 'required|digits:16',
                'no_kk' => 'required|digits:16',
                'phone' => 'required|regex:/^62\d+$/',
                'email' => 'nullable|email',
                'address' => 'required',
                'visit_date' => 'required|date|after_or_equal:today',
                'visit_time' => ['required', Rule::in($activeTimeSlots)], // Dynamic validation
                'g-recaptcha-response' => 'required'
                // Removed ktp_image validation here as we've already validated it above
            ], $customMessages);

            // 3. Check for existing registration within 2 weeks
            try {
                $this->checkExistingRegistration($request->nik);
            } catch (\Exception $e) {
                return back()
                    ->withErrors(['nik' => $e->getMessage()]) // Add specific error message
                    ->withInput(); // Keep user input
            }

            // 4. Verify reCAPTCHA
            $recaptcha = $request->input('g-recaptcha-response');
            $secretKey = env('RECAPTCHA_SECRET_KEY');
            
            $url = 'https://www.google.com/recaptcha/api/siteverify';
            $data = [
                'secret' => $secretKey,
                'response' => $recaptcha,
                'remoteip' => $request->ip()
            ];
            
            $options = [
                'http' => [
                    'header' => "Content-type: application/x-www-form-urlencoded\r\n",
                    'method' => 'POST',
                    'content' => http_build_query($data)
                ]
            ];
            
            $context = stream_context_create($options);
            $result = file_get_contents($url, false, $context);
            $resultJson = json_decode($result);

            if (!$resultJson || !$resultJson->success) { // Check if $resultJson is valid too
                return back()
                    ->withErrors(['g-recaptcha-response' => 'Verifikasi reCAPTCHA gagal atau tidak valid. Silakan coba lagi.']) // Add specific error message
                    ->withInput(); // Keep user input
            }

            // 5. Double-check quota availability (as a precaution)
            // Even though this should be checked by JavaScript before submission,
            // we verify again in case someone tries to submit when quota is already full
            $quotaResponse = $this->checkQuota($request->visit_date, $request->visit_time);
            $quotaData = json_decode($quotaResponse->getContent());

            if (!$quotaData || !$quotaData->available) { // Check if $quotaData is valid too
                // Optional: Log warning for security/monitoring, but keep it concise
                // \Log::warning('Attempted booking with full quota:', ['date' => $request->visit_date, 'time' => $request->visit_time]);

                return back()
                    ->withErrors(['visit_time' => 'Maaf, kuota untuk tanggal dan waktu yang dipilih sudah penuh atau tidak valid saat Anda mengirimkan formulir. Silakan pilih waktu lain.']) // Add specific error message
                    ->withInput(); // Keep user input
            }

            // 6. Handle KTP image upload
            $ktpImageBase64 = null;
            if ($request->hasFile('ktp_image')) {
                try {
                    $ktpImage = $request->file('ktp_image');
                    
                    // Additional debug logging
                    \Log::info('KTP Image Upload Details:', [
                        'original_name' => $ktpImage->getClientOriginalName(),
                        'mime' => $ktpImage->getMimeType(),
                        'extension' => $ktpImage->getClientOriginalExtension(),
                        'size' => $ktpImage->getSize(),
                        'valid' => $ktpImage->isValid(),
                    ]);
                    
                    // Process the image (resize, compress, base64 encode)
                    $ktpImageBase64 = $this->processKtpImage($ktpImage);
                    
                    if (empty($ktpImageBase64)) {
                        throw new \Exception('Hasil pemrosesan gambar KTP kosong.');
                    }
                } catch (\Exception $e) {
                    // Log the error with more detail
                    \Log::error('KTP Image Processing Error:', [
                        'message' => $e->getMessage(),
                        'file' => $e->getFile(),
                        'line' => $e->getLine()
                    ]);
                    
                    // Return back with an error message
                    return back()
                        ->withErrors(['ktp_image' => 'Terjadi kesalahan saat memproses gambar KTP: ' . $e->getMessage()]) 
                        ->withInput(); // Keep user input
                }
            } else {
                // If no file was uploaded in the request, log it and return error
                \Log::error('KTP Image Missing: No file uploaded in request');
                return back()
                    ->withErrors(['ktp_image' => 'Foto KTP wajib diunggah. Silakan pilih file gambar.'])
                    ->withInput();
            }

            // 7. Generate unique booking code and save registration
            try {
                $bookingCode = $this->generateUniqueBookingCode();

                $booking = Registration::create([
                    'name' => $request->name,
                    'gender' => $request->gender,
                    'nik' => $request->nik,
                    'no_kk' => $request->no_kk,
                    'phone' => $request->phone,
                    'email' => $request->email,
                    'address' => $request->address,
                    'topic' => $request->topic, // Make sure 'topic' is included
                    'priority' => $request->has('priority'), // Store as boolean
                    'pendamping' => $request->has('priority') ? $request->pendamping : null, // Only store if priority is checked
                    'visit_date' => $request->visit_date,
                    'visit_time' => $request->visit_time,
                    'ktp_image' => $ktpImageBase64, // Correct key to match DB column
                    'booking_code' => $bookingCode,
                    'status' => 'pending' // Default status
                ]);

                // Clear the date availability cache for this date
                $this->clearDateAvailabilityCache($request->visit_date);

                return view('booking-confirmation', compact('booking'));

            } catch (\Exception $e) {
                // No need to clean up files since we're storing base64 strings

                \Log::error('Registration Save Error:', ['message' => $e->getMessage()]); // Keep error log
                return back()
                    ->withErrors(['system' => 'Gagal menyimpan data registrasi setelah validasi berhasil. Silakan coba lagi atau hubungi administrator.']) // More specific error
                    ->withInput(); // Keep user input
            }

        } catch (\Throwable $e) { // Menggunakan Throwable untuk menangkap lebih banyak jenis error
            // Logging lebih detail
            \Log::error('Unexpected Error in RegistrationController@store', [
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(), // Menambahkan stack trace
                'request_data' => $request->except(['ktp_image', 'g-recaptcha-response', '_token']) // Log data request (hati-hati dengan data sensitif lain jika ada)
            ]);
            
            // Mencoba logging darurat jika \Log::error gagal
            try {
                error_log('EMERGENCY LOG (RegistrationController@store): ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
            } catch (\Throwable $logErr) {
                // Abaikan jika logging darurat juga gagal
            }

            return back()
                ->withErrors([
                    'system' => 'Mohon maaf, terjadi kesalahan yang tidak terduga. Silakan coba beberapa saat lagi.' // Pesan error tetap sama
                ])
                ->withInput();
        }
    }

    public function printVersion($id)
    {
        $booking = Registration::findOrFail($id);
        return view('booking-print', compact('booking'));
    }

    public function getActiveTimeSlots()
    {
        $activeSlots = TimeSlot::active()
            ->orderBy('display_order')
            ->orderBy('time')
            ->get();
            
        return response()->json([
            'slots' => $activeSlots
        ]);
    }
    
    /**
     * Process uploaded image - resize, compress, and convert to base64
     * 
     * @param \Illuminate\Http\UploadedFile $image
     * @param int $maxWidth Maximum width in pixels
     * @param int $quality Compression quality (0-100)
     * @return string Base64 encoded image string with mime type
     */
    private function processKtpImage($image, $maxWidth = 800, $quality = 75)
    {
        try {
            // Check if file exists and is valid
            if (!$image->isValid()) {
                \Log::error('KTP Image Processing: Invalid file', [
                    'error' => $image->getErrorMessage(),
                    'original_name' => $image->getClientOriginalName()
                ]);
                throw new \Exception('Gambar KTP tidak valid. Silakan coba lagi.');
            }
            
            // Explicitly validate mime type
            $allowedMimes = ['image/jpeg', 'image/png', 'image/jpg'];
            $mime = $image->getMimeType();
            
            if (!in_array($mime, $allowedMimes)) {
                \Log::error('KTP Image Processing: Invalid mime type', [
                    'mime' => $mime,
                    'original_name' => $image->getClientOriginalName()
                ]);
                throw new \Exception('Format gambar KTP tidak valid. Hanya format JPEG, JPG, dan PNG yang diperbolehkan.');
            }
            
            // Create Intervention Image Manager with GD driver
            $manager = new ImageManager(new Driver());
            
            try {
                // Create Image instance
                $img = $manager->read($image);
                
                // Keep aspect ratio and resize if width is greater than maxWidth
                if ($img->width() > $maxWidth) {
                    // Correct way to resize maintaining aspect ratio in Intervention Image v3+
                    $img->scale(width: $maxWidth); 
                }
    
                // Convert to JPEG format and compress
                $encodedImage = $img->toJpeg($quality);
                
                // Convert to base64 without data URI prefix
                $base64 = base64_encode($encodedImage->toString());
                
                return $base64;
            } catch (\Exception $e) {
                \Log::error('KTP Image Processing: Image manipulation failed', [
                    'error' => $e->getMessage(),
                    'trace' => $e->getTraceAsString(),
                    'mime' => $mime,
                    'original_name' => $image->getClientOriginalName()
                ]);
                throw new \Exception('Gagal memproses gambar KTP. Silakan coba dengan file lain.');
            }
        } catch (\Exception $e) {
            \Log::error('KTP Compression Error:', ['error' => $e->getMessage()]); // Keep error log
            throw $e;
        }
    }

    public function getDateAvailability($year = null, $month = null)
    {
        // Set timezone
        date_default_timezone_set('Asia/Jakarta');
        
        try {
            // If no month/year provided, use current month/year
            if (!$month || !$year) {
                $now = Carbon::now()->timezone('Asia/Jakarta');
                $month = $month ?? $now->month;
                $year = $year ?? $now->year;
            }
            
            // Ensure variables are integers
            $month = (int)$month;
            $year = (int)$year;
            
            // Validate input
            if ($month < 1 || $month > 12) {
                throw new \InvalidArgumentException("Month must be between 1 and 12, {$month} given");
            }
            
            if ($year < 2000 || $year > 2100) {
                throw new \InvalidArgumentException("Year must be between 2000 and 2100, {$year} given");
            }
            
            // Create cache key
            $cacheKey = "date_availability_{$year}_{$month}";
            
            // Store cache key for later clearing if needed
            $allCacheKeys = \Cache::get('all_date_availability_cache_keys', []);
            if (!in_array($cacheKey, $allCacheKeys)) {
                $allCacheKeys[] = $cacheKey;
                \Cache::put('all_date_availability_cache_keys', $allCacheKeys, now()->addDays(1));
            }
            
            // Check if we have this data in cache (cache for 6 hours)
            // Increased cache time for better performance
            if (\Cache::has($cacheKey)) {
                return response()->json(\Cache::get($cacheKey));
            }
            
            // Create start and end dates for the month
            $startDate = Carbon::createFromDate($year, $month, 1, 'Asia/Jakarta');
            $endDate = Carbon::createFromDate($year, $month, 1, 'Asia/Jakarta')->endOfMonth();
            
            // Get current date for comparison
            $today = Carbon::now()->timezone('Asia/Jakarta')->startOfDay();
            
            \Log::info('Checking date availability for month/year', [
                'month' => $month,
                'year' => $year,
                'start_date' => $startDate->format('Y-m-d'),
                'end_date' => $endDate->format('Y-m-d')
            ]);
            
            // Get all time slots
            $timeSlots = TimeSlot::active()->get();
            
            if ($timeSlots->isEmpty()) {
                $result = [
                    'success' => false,
                    'message' => 'No active time slots found',
                    'available_dates' => [],
                    'unavailable_dates' => []
                ];
                
                \Cache::put($cacheKey, $result, now()->addHours(6));
                return response()->json($result);
            }
            
            // Get holidays for the month - collect dates from holiday ranges
            $holidayDates = [];
            
            // First, try to use new start_date and end_date columns
            $holidayRanges = Holiday::where(function($query) use ($year, $month) {
                // Get holidays where start_date is in the current month/year
                $query->whereYear('start_date', $year)
                      ->whereMonth('start_date', $month);
            })->orWhere(function($query) use ($year, $month) {
                // Or end_date is in the current month/year
                $query->whereYear('end_date', $year)
                      ->whereMonth('end_date', $month);
            })->orWhere(function($query) use ($year, $month) {
                // Or where the range spans the current month
                // Find current month's start and end
                $monthStart = Carbon::createFromDate($year, $month, 1);
                $monthEnd = Carbon::createFromDate($year, $month, 1)->endOfMonth();
                
                // Find holidays where start_date is before month start and end_date is after month end
                $query->where('start_date', '<=', $monthStart)
                      ->where('end_date', '>=', $monthEnd);
            })->get();
            
            // Convert each range to individual dates
            foreach ($holidayRanges as $holiday) {
                $startDate = Carbon::parse($holiday->start_date);
                $endDate = Carbon::parse($holiday->end_date);
                $currentDate = clone $startDate;
                
                // Generate all dates in the range
                while ($currentDate->lte($endDate)) {
                    // Only include dates that are actually in the requested month
                    if ($currentDate->year == $year && $currentDate->month == $month) {
                        $holidayDates[] = $currentDate->format('Y-m-d');
                    }
                    $currentDate->addDay();
                }
            }
            
            // For backwards compatibility, also check old date column
            $legacyHolidays = Holiday::whereYear('date', $year)
                              ->whereMonth('date', $month)
                              ->whereNull('start_date') // Only get rows that haven't been migrated
                              ->pluck('date')
                              ->map(function($date) {
                                  return Carbon::parse($date)->format('Y-m-d');
                              })
                              ->toArray();
            
            // Merge both holiday collections and remove duplicates
            $holidays = array_unique(array_merge($holidayDates, $legacyHolidays));
            
            // Get registration date range
            $registrationStartDate = Setting::where('key', 'registration_start_date')->first();
            $registrationEndDate = Setting::where('key', 'registration_end_date')->first();
            
            $startDateLimit = $registrationStartDate ? Carbon::parse($registrationStartDate->value) : null;
            $endDateLimit = $registrationEndDate ? Carbon::parse($registrationEndDate->value) : null;
            
            $availableDates = [];
            $unavailableDates = [];
            
            // Check each day in the month
            $currentDate = clone $startDate;
            
            while ($currentDate->lte($endDate)) {
                // Skip processing if it's a weekend, holiday, past date, or outside registration period
                $dateStr = $currentDate->format('Y-m-d');
                $isWeekend = $currentDate->dayOfWeek === 0 || $currentDate->dayOfWeek === 6; // 0 = Sunday, 6 = Saturday
                $isHoliday = in_array($dateStr, $holidays);
                $isPastDate = $currentDate->lt($today);
                
                // Check if date is outside registration period
                $isBeforeRegistrationStart = $startDateLimit && $currentDate->lt($startDateLimit);
                $isAfterRegistrationEnd = $endDateLimit && $currentDate->gt($endDateLimit);
                $isOutsideRegistrationPeriod = $isBeforeRegistrationStart || $isAfterRegistrationEnd;
                
                if ($isWeekend || $isHoliday || $isPastDate || $isOutsideRegistrationPeriod) {
                    $unavailableDates[] = $dateStr;
                    $currentDate->addDay();
                    continue;
                }
                
                // Check each time slot for this date
                $hasAvailableSlot = false;
                
                foreach ($timeSlots as $slot) {
                    // Skip inactive slots
                    if (!$slot->is_active || $slot->quota === 0) {
                        continue;
                    }
                    
                    // Count existing registrations for this slot
                    $count = Registration::whereDate('visit_date', $dateStr)
                                       ->where('visit_time', $slot->time)
                                       ->count();
                    
                    // If there's at least one slot with availability, the date is available
                    if ($count < $slot->quota) {
                        $hasAvailableSlot = true;
                        break;
                    }
                }
                
                // Add date to appropriate array
                if ($hasAvailableSlot) {
                    $availableDates[] = $dateStr;
                } else {
                    $unavailableDates[] = $dateStr;
                }
                
                $currentDate->addDay();
            }
            
            $result = [
                'success' => true,
                'month' => (int)$month,
                'year' => (int)$year,
                'available_dates' => $availableDates,
                'unavailable_dates' => $unavailableDates
            ];
            
            // Store in cache for 6 hours for better performance
            \Cache::put($cacheKey, $result, now()->addHours(6));
            
            return response()->json($result);
        } catch (\Exception $e) {
            \Log::error('Error in RegistrationController@getDateAvailability', [
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'An error occurred while checking date availability. Please try again later.'
            ], 500);
        }
    }

    /**
     * Clear the date availability cache for a specific month and year
     *
     * @param string|Carbon\Carbon $date
     * @return void
     */
    private function clearDateAvailabilityCache($date)
    {
        if (is_string($date)) {
            $date = Carbon::parse($date);
        }
        
        $cacheKey = "date_availability_{$date->year}_{$date->month}";
        
        \Cache::forget($cacheKey);
    }

    /**
     * Get the current cache version for client-side cache invalidation
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function getCacheVersion()
    {
        try {
            // Menggunakan time() sebagai fallback jika cache key tidak ada
            $version = \Cache::get('time_slot_version', function () {
                // Jika key tidak ada, set dengan timestamp saat ini dan kembalikan
                $newVersion = time();
                \Cache::put('time_slot_version', $newVersion, now()->addDays(1)); // Cache version for 1 day
                return $newVersion;
            });
            return response()->json(['version' => $version]);
        } catch (\Exception $e) {
            // Log error jika terjadi masalah saat mengakses cache
            \Log::error('Error getting cache version:', ['error' => $e->getMessage()]);
            // Kembalikan timestamp saat ini sebagai fallback jika ada error
            return response()->json(['version' => time(), 'error' => 'Failed to retrieve cache version'], 500);
        }
    }

    public function checkQuotaBatch($date, Request $request)
    {
        // Set timezone explicitly
        date_default_timezone_set('Asia/Jakarta');
        
        // Parse dates using Carbon with explicit timezone
        $currentDate = Carbon::now()->timezone('Asia/Jakarta');
        $selectedDate = Carbon::createFromFormat('Y-m-d', $date, 'Asia/Jakarta');
        
        // For GET requests, times will come as a query parameter
        // For backwards compatibility, we check both query params and request body
        $requestedTimes = $request->query('times', []);
        
        // If times come as a string (comma-separated), convert to array
        if (is_string($requestedTimes)) {
            $requestedTimes = explode(',', $requestedTimes);
        }
        
        // If no times in query param, try the request body (for backwards compatibility)
        if (empty($requestedTimes)) {
            $requestedTimes = $request->input('times', []);
        }
        
        if (empty($requestedTimes)) {
            return response()->json([
                'success' => false,
                'message' => 'No time slots provided'
            ]);
        }
        
        // Check if holiday
        if(Holiday::whereDate('date', $selectedDate->format('Y-m-d'))->exists()) {
            // Return all requested times as unavailable
            $result = [];
            foreach ($requestedTimes as $time) {
                $result[$time] = ['available' => false, 'message' => 'Hari Libur'];
            }
            return response()->json(['success' => true, 'quotas' => $result]);
        }

        // Compare dates at start of day
        $todayJkt = $currentDate->copy()->startOfDay();
        $selectedStartOfDay = $selectedDate->copy()->startOfDay();

        if ($selectedStartOfDay->lt($todayJkt)) {
            // Return all requested times as unavailable due to invalid date
            $result = [];
            foreach ($requestedTimes as $time) {
                $result[$time] = ['available' => false, 'message' => 'Tanggal tidak valid'];
            }
            return response()->json(['success' => true, 'quotas' => $result]);
        }
        
        // Get registration date range settings
        $registrationStartDate = Setting::where('key', 'registration_start_date')->first();
        $registrationEndDate = Setting::where('key', 'registration_end_date')->first();
        
        // Check if date is outside the registration period
        if ($registrationStartDate) {
            $startLimit = Carbon::parse($registrationStartDate->value)->startOfDay();
            if ($selectedStartOfDay->lt($startLimit)) {
                // Return all requested times as unavailable due to out of period
                $result = [];
                foreach ($requestedTimes as $time) {
                    $result[$time] = ['available' => false, 'message' => 'Diluar periode registrasi'];
                }
                return response()->json(['success' => true, 'quotas' => $result]);
            }
        }
        
        if ($registrationEndDate) {
            $endLimit = Carbon::parse($registrationEndDate->value)->startOfDay();
            if ($selectedStartOfDay->gt($endLimit)) {
                // Return all requested times as unavailable due to out of period
                $result = [];
                foreach ($requestedTimes as $time) {
                    $result[$time] = ['available' => false, 'message' => 'Diluar periode registrasi'];
                }
                return response()->json(['success' => true, 'quotas' => $result]);
            }
        }
        
        // Get all active time slots in one query
        $timeSlots = TimeSlot::whereIn('time', $requestedTimes)
                            ->where('is_active', true)
                            ->get()
                            ->keyBy('time');
        
        // Count registrations for all requested times in a single query
        $registrationCounts = Registration::whereDate('visit_date', $date)
                                        ->whereIn('visit_time', $requestedTimes)
                                        ->selectRaw('visit_time, count(*) as count')
                                        ->groupBy('visit_time')
                                        ->pluck('count', 'visit_time')
                                        ->toArray();
        
        // Prepare response with quota information for each time
        $result = [];
        foreach ($requestedTimes as $time) {
            $timeSlot = $timeSlots->get($time);
            
            // If time slot doesn't exist or is inactive
            if (!$timeSlot || $timeSlot->quota === 0) {
                $result[$time] = [
                    'available' => false,
                    'message' => 'Slot waktu tidak tersedia',
                    'disabled' => true
                ];
                continue;
            }
            
            $maxQuota = $timeSlot->quota;
            $count = isset($registrationCounts[$time]) ? $registrationCounts[$time] : 0;
            
            // Final availability check
            $isAvailable = $count < $maxQuota;
            
            $result[$time] = [
                'available' => $isAvailable,
                'quota' => $maxQuota,
                'used' => $count
            ];
        }
        
        return response()->json([
            'success' => true,
            'quotas' => $result
        ]);
    }
}
