<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use App\Models\Equipment;
use App\Models\EquipmentReservation;
use Carbon\Carbon;

class EquipmentReservationRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     */
    public function rules(): array
    {
        return [
            'equipment_id' => 'required|exists:equipment,id',
            'reservation_date' => 'required|date|after_or_equal:today',
            'start_time' => 'required|date_format:H:i',
            'end_time' => 'nullable|date_format:H:i|after:start_time',
            'notes' => 'nullable|string|max:1000',
        ];
    }

    /**
     * Get custom messages for validator errors.
     */
    public function messages(): array
    {
        return [
            'equipment_id.required' => 'Please select an equipment.',
            'equipment_id.exists' => 'The selected equipment does not exist.',
            'reservation_date.required' => 'Please select a reservation date.',
            'reservation_date.date' => 'Please enter a valid date.',
            'reservation_date.after_or_equal' => 'Reservation date must be today or in the future.',
            'start_time.required' => 'Please select a start time.',
            'start_time.date_format' => 'Please enter a valid time format (HH:MM).',
            'end_time.date_format' => 'Please enter a valid time format (HH:MM).',
            'end_time.after' => 'End time must be after start time.',
            'notes.max' => 'Notes cannot exceed 1000 characters.',
        ];
    }

    /**
     * Configure the validator instance.
     */
    public function withValidator($validator)
    {
        $validator->after(function ($validator) {
            $this->validateEquipmentAvailability($validator);
            $this->validateEquipmentOperatingHours($validator);
            $this->validateTimeSlotAlignment($validator);
            $this->validateTimeSlotNotInPast($validator);
        });
    }

    /**
     * Validate that the equipment is available for the requested time slot.
     */
    protected function validateEquipmentAvailability($validator)
    {
        $equipmentId = $this->input('equipment_id');
        $reservationDate = $this->input('reservation_date');
        $startTime = $this->input('start_time');
        $endTime = $this->input('end_time');

        if (!$equipmentId || !$reservationDate || !$startTime) {
            return;
        }

        $equipment = Equipment::find($equipmentId);
        if (!$equipment) {
            return;
        }

        // Calculate end time if not provided
        if (!$endTime) {
            $startCarbon = Carbon::parse($startTime);
            $endCarbon = $startCarbon->copy()->addMinutes($equipment->slot_duration);
            $endTime = $endCarbon->format('H:i');
        }

        // Check if the time slot is already reserved
        $existingReservation = EquipmentReservation::where('equipment_id', $equipmentId)
            ->where('reservation_date', $reservationDate)
            ->where('status', 'confirmed')
            ->where(function ($query) use ($startTime, $endTime) {
                $query->where(function ($q) use ($startTime, $endTime) {
                    // Check if the new reservation overlaps with existing ones
                    $q->where('start_time', '<', $endTime)
                      ->where('end_time', '>', $startTime);
                });
            })
            ->first();

        if ($existingReservation) {
            $validator->errors()->add('time_slot', 'This time slot is already reserved. Please select a different time.');
        }
    }

    /**
     * Validate that the reservation is within the equipment's operating hours.
     */
    protected function validateEquipmentOperatingHours($validator)
    {
        $equipmentId = $this->input('equipment_id');
        $startTime = $this->input('start_time');
        $endTime = $this->input('end_time');

        if (!$equipmentId || !$startTime) {
            return;
        }

        $equipment = Equipment::find($equipmentId);
        if (!$equipment) {
            return;
        }

        // Calculate end time if not provided
        if (!$endTime) {
            $startCarbon = Carbon::parse($startTime);
            $endCarbon = $startCarbon->copy()->addMinutes($equipment->slot_duration);
            $endTime = $endCarbon->format('H:i');
        }

        $equipmentStartTime = Carbon::parse($equipment->start_time)->format('H:i');
        $equipmentEndTime = Carbon::parse($equipment->end_time)->format('H:i');

        if ($startTime < $equipmentStartTime) {
            $validator->errors()->add('start_time', "Reservation start time must be after {$equipmentStartTime}.");
        }

        if ($endTime > $equipmentEndTime) {
            $validator->errors()->add('end_time', "Reservation end time must be before {$equipmentEndTime}.");
        }
    }

    /**
     * Validate that the time slots align with the equipment's slot duration.
     */
    protected function validateTimeSlotAlignment($validator)
    {
        $equipmentId = $this->input('equipment_id');
        $startTime = $this->input('start_time');
        $endTime = $this->input('end_time');

        if (!$equipmentId || !$startTime) {
            return;
        }

        $equipment = Equipment::find($equipmentId);
        if (!$equipment) {
            return;
        }

        // Calculate end time if not provided
        if (!$endTime) {
            $startCarbon = Carbon::parse($startTime);
            $endCarbon = $startCarbon->copy()->addMinutes($equipment->slot_duration);
            $endTime = $endCarbon->format('H:i');
        }

        $startCarbon = Carbon::parse($startTime);
        $endCarbon = Carbon::parse($endTime);
        $duration = $startCarbon->diffInMinutes($endCarbon);

        if ($duration % $equipment->slot_duration !== 0) {
            $validator->errors()->add('time_slot', "Reservation duration must be in multiples of {$equipment->slot_duration} minutes.");
        }

        // Check if start time aligns with slot boundaries
        $equipmentStartTime = Carbon::parse($equipment->start_time);
        $minutesFromStart = $startCarbon->diffInMinutes($equipmentStartTime);
        
        if ($minutesFromStart % $equipment->slot_duration !== 0) {
            $validator->errors()->add('start_time', "Start time must align with {$equipment->slot_duration}-minute slot boundaries.");
        }
    }

    /**
     * Validate that the time slot is not in the past.
     */
    protected function validateTimeSlotNotInPast($validator)
    {
        $reservationDate = $this->input('reservation_date');
        $startTime = $this->input('start_time');

        if (!$reservationDate || !$startTime) {
            return;
        }

        // Create a Carbon instance for the specific date and time
        $slotDateTime = Carbon::parse($reservationDate)->setTimeFrom(Carbon::parse($startTime));
        
        // Check if the time slot is in the past
        if ($slotDateTime->isPast()) {
            $validator->errors()->add('time_slot', 'Cannot create a reservation for a time slot in the past.');
        }
    }
}
