<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\API\BaseController;
use App\Models\Equipment;
use App\Models\EquipmentReservation;
use App\Http\Requests\EquipmentReservationRequest;
use App\Http\Requests\EquipmentReservationSlotRequest;
use Illuminate\Http\Request;
use Carbon\Carbon;
use App\Models\TimeSlot;

class EquipmentReservationApiController extends BaseController
{
    /**
     * Get all reservations with optional filtering.
     */
    public function index(Request $request)
    {
        try {
            $query = EquipmentReservation::with(['equipment', 'user'])->ordered();

            // Filter by equipment
            if ($request->has('equipment_id')) {
                $query->forEquipment($request->equipment_id);
            }

            // Filter by equipment type
            if ($request->has('equipment_type') && in_array($request->equipment_type, array_keys(Equipment::getTypes()))) {
                $query->whereHas('equipment', function ($q) use ($request) {
                    $q->ofType($request->equipment_type);
                });
            }

            // Filter by status
            if ($request->has('status') && in_array($request->status, array_keys(EquipmentReservation::getStatuses()))) {
                $query->where('status', $request->status);
            }

            // Filter by date range
            if ($request->has('date_from')) {
                $query->where('reservation_date', '>=', $request->date_from);
            }

            if ($request->has('date_to')) {
                $query->where('reservation_date', '<=', $request->date_to);
            }

            // Filter by user (for authenticated users, show only their reservations)
            if (auth()->check()) {
                $query->forUser(auth()->id());
            }

            // Search by user name or equipment name
            if ($request->has('search')) {
                $search = $request->search;
                $query->where(function ($q) use ($search) {
                    $q->whereHas('user', function ($userQuery) use ($search) {
                        $userQuery->where('name', 'like', "%{$search}%");
                    })->orWhereHas('equipment', function ($equipmentQuery) use ($search) {
                        $equipmentQuery->where('name', 'like', "%{$search}%");
                    });
                });
            }

            $perPage = $request->get('per_page', 15);
            $reservations = $query->paginate($perPage);

            $data = [
                'reservations' => $reservations->items(),
                'pagination' => [
                    'current_page' => $reservations->currentPage(),
                    'last_page' => $reservations->lastPage(),
                    'per_page' => $reservations->perPage(),
                    'total' => $reservations->total(),
                    'from' => $reservations->firstItem(),
                    'to' => $reservations->lastItem(),
                ],
                'filters' => [
                    'statuses' => EquipmentReservation::getStatuses(),
                    'equipment_types' => Equipment::getTypes(),
                    'applied_filters' => $request->only(['equipment_id', 'equipment_type', 'status', 'date_from', 'date_to', 'search']),
                ],
            ];

            return $this->sendResponse($data, 'Reservations retrieved successfully');
        } catch (\Exception $e) {
            return $this->sendError('Error retrieving reservations', $e->getMessage(), 500);
        }
    }

    /**
     * Get a specific reservation by ID.
     */
    public function show($id)
    {
        try {
            $reservation = EquipmentReservation::with(['equipment', 'user'])->findOrFail($id);
            
            // Check if user can view this reservation
            if (auth()->check() && !auth()->user()->isAdmin() && !auth()->user()->isOperation()) {
                if ($reservation->user_id !== auth()->id()) {
                    return $this->sendError('Unauthorized', 'You can only view your own reservations', 403);
                }
            }
            
            $data = [
                'reservation' => $reservation->toApiDetailArray(),
            ];

            return $this->sendResponse($data, 'Reservation details retrieved successfully');
        } catch (\Exception $e) {
            return $this->sendError('Reservation not found', $e->getMessage(), 404);
        }
    }

    /**
     * Create a new reservation.
     */
    public function store(EquipmentReservationSlotRequest $request)
    {
        try {
            $data = $request->validated();
            $data['user_id'] = auth()->id();
            $data['status'] = 'confirmed';

            $timeSlotIds = $data['time_slot_ids'];
            $createdReservations = [];
            $timeSlotService = app(\App\Services\TimeSlotService::class);

            // Create a reservation for each selected time slot
            foreach ($timeSlotIds as $timeSlotId) {
                // Get the time slot to extract date and time information
                $timeSlot = TimeSlot::find($timeSlotId);
                
                // Create reservation data for this slot
                $reservationData = [
                    'equipment_id' => $data['equipment_id'],
                    'user_id' => $data['user_id'],
                    'time_slot_id' => $timeSlotId,
                    'reservation_date' => $timeSlot->slot_date,
                    'start_time' => $timeSlot->start_time->format('H:i'),
                    'end_time' => $timeSlot->end_time->format('H:i'),
                    'status' => $data['status'],
                    'notes' => $data['notes'] ?? null,
                ];

                // Create the reservation
                $reservation = EquipmentReservation::create($reservationData);
                $reservation->load(['equipment', 'user', 'timeSlot']);
                $createdReservations[] = $reservation;

                // Update the time slot status and link it to the reservation
                $timeSlotService->reserveTimeSlot($reservation);
            }

            $slotCount = count($createdReservations);
            $message = $slotCount === 1 
                ? 'Reservation created successfully' 
                : "{$slotCount} reservations created successfully";

            $data = [
                'reservations' => collect($createdReservations)->map->toApiDetailArray(),
                'total_created' => $slotCount,
            ];

            return $this->sendResponse($data, $message, 201);
        } catch (\Exception $e) {
            return $this->sendError('Error creating reservation', $e->getMessage(), 500);
        }
    }

    /**
     * Update a reservation.
     */
    public function update(EquipmentReservationRequest $request, $id)
    {
        try {
            $reservation = EquipmentReservation::findOrFail($id);
            
            // Check if user can update this reservation
            if (auth()->check() && !auth()->user()->isAdmin() && !auth()->user()->isOperation()) {
                if ($reservation->user_id !== auth()->id()) {
                    return $this->sendError('Unauthorized', 'You can only update your own reservations', 403);
                }
            }

            $data = $request->validated();

            // Additional check to prevent updating to past time slots
            $slotDateTime = Carbon::parse($data['reservation_date'])->setTimeFrom(Carbon::parse($data['start_time']));
            if ($slotDateTime->isPast()) {
                return $this->sendError('Cannot update to past time slot', 'Cannot update a reservation to a time slot in the past', 400);
            }

            // Store old values for time slot management
            $oldEquipmentId = $reservation->equipment_id;
            $oldDate = $reservation->reservation_date;
            $oldStartTime = $reservation->start_time;

            // Calculate end time based on slot duration
            $equipment = Equipment::find($data['equipment_id']);
            $startTime = Carbon::parse($data['start_time']);
            $endTime = $startTime->copy()->addMinutes($equipment->slot_duration);
            $data['end_time'] = $endTime->format('H:i');

            // Update the reservation
            $reservation->update($data);
            $reservation->load(['equipment', 'user']);

            // Handle time slot changes
            $timeSlotService = app(\App\Services\TimeSlotService::class);
            
            // If equipment, date, or time changed, release old slot and reserve new slot
            if ($oldEquipmentId != $data['equipment_id'] || 
                $oldDate != $data['reservation_date'] || 
                $oldStartTime != $data['start_time']) {
                
                // Release the old time slot
                $oldReservation = $reservation->replicate();
                $oldReservation->equipment_id = $oldEquipmentId;
                $oldReservation->reservation_date = $oldDate;
                $oldReservation->start_time = $oldStartTime;
                $timeSlotService->releaseTimeSlot($oldReservation);
                
                // Reserve the new time slot
                $timeSlotService->reserveTimeSlot($reservation);
            }

            $data = [
                'reservation' => $reservation->toApiDetailArray(),
            ];

            return $this->sendResponse($data, 'Reservation updated successfully');
        } catch (\Exception $e) {
            return $this->sendError('Error updating reservation', $e->getMessage(), 500);
        }
    }

    /**
     * Delete a reservation.
     */
    public function destroy($id)
    {
        try {
            $reservation = EquipmentReservation::findOrFail($id);
            
            // Check if user can delete this reservation
            if (auth()->check() && !auth()->user()->isAdmin() && !auth()->user()->isOperation()) {
                if ($reservation->user_id !== auth()->id()) {
                    return $this->sendError('Unauthorized', 'You can only delete your own reservations', 403);
                }
            }

            // Check if reservation is in the past
            if ($reservation->isPast()) {
                return $this->sendError('Cannot delete past reservations', 'Past reservations cannot be deleted', 400);
            }

            // Release the time slot to make it available again
            $timeSlotService = app(\App\Services\TimeSlotService::class);
            $timeSlotService->releaseTimeSlot($reservation);

            $reservation->delete();

            return $this->sendResponse([], 'Reservation deleted successfully');
        } catch (\Exception $e) {
            return $this->sendError('Error deleting reservation', $e->getMessage(), 500);
        }
    }

    /**
     * Cancel a reservation.
     */
    public function cancel($id)
    {
        try {
            $reservation = EquipmentReservation::findOrFail($id);
            
            // Check if user can cancel this reservation
            if (auth()->check() && !auth()->user()->isAdmin() && !auth()->user()->isOperation()) {
                if ($reservation->user_id !== auth()->id()) {
                    return $this->sendError('Unauthorized', 'You can only cancel your own reservations', 403);
                }
            }

            if ($reservation->isCancelled()) {
                return $this->sendError('Reservation already cancelled', 'This reservation is already cancelled', 400);
            }

            if ($reservation->isPast()) {
                return $this->sendError('Cannot cancel past reservations', 'Past reservations cannot be cancelled', 400);
            }

            $reservation->update(['status' => 'cancelled']);
            $reservation->load(['equipment', 'user']);

            // Release the time slot to make it available again
            $timeSlotService = app(\App\Services\TimeSlotService::class);
            $timeSlotService->releaseTimeSlot($reservation);

            $data = [
                'reservation' => $reservation->toApiDetailArray(),
            ];

            return $this->sendResponse($data, 'Reservation cancelled successfully');
        } catch (\Exception $e) {
            return $this->sendError('Error cancelling reservation', $e->getMessage(), 500);
        }
    }

    /**
     * Complete a reservation.
     */
    public function complete($id)
    {
        try {
            $reservation = EquipmentReservation::findOrFail($id);
            
            // Only admins and operation users can complete reservations
            if (auth()->check() && !auth()->user()->isAdmin() && !auth()->user()->isOperation()) {
                return $this->sendError('Unauthorized', 'Only administrators can complete reservations', 403);
            }

            if ($reservation->isCompleted()) {
                return $this->sendError('Reservation already completed', 'This reservation is already completed', 400);
            }

            if ($reservation->isFuture()) {
                return $this->sendError('Cannot complete future reservations', 'Future reservations cannot be completed', 400);
            }

            $reservation->update(['status' => 'completed']);
            $reservation->load(['equipment', 'user']);

            $data = [
                'reservation' => $reservation->toApiDetailArray(),
            ];

            return $this->sendResponse($data, 'Reservation marked as completed');
        } catch (\Exception $e) {
            return $this->sendError('Error completing reservation', $e->getMessage(), 500);
        }
    }

    /**
     * Get user's own reservations.
     */
    public function myReservations(Request $request)
    {
        try {
            if (!auth()->check()) {
                return $this->sendError('Unauthorized', 'Authentication required', 401);
            }

            $query = EquipmentReservation::with('equipment')
                ->forUser(auth()->id())
                ->ordered();

            // Filter by status
            if ($request->has('status') && in_array($request->status, array_keys(EquipmentReservation::getStatuses()))) {
                $query->where('status', $request->status);
            }

            // Filter by date range
            if ($request->has('date_from')) {
                $query->where('reservation_date', '>=', $request->date_from);
            }

            if ($request->has('date_to')) {
                $query->where('reservation_date', '<=', $request->date_to);
            }

            $perPage = $request->get('per_page', 15);
            $reservations = $query->paginate($perPage);

            $data = [
                'reservations' => $reservations->items(),
                'pagination' => [
                    'current_page' => $reservations->currentPage(),
                    'last_page' => $reservations->lastPage(),
                    'per_page' => $reservations->perPage(),
                    'total' => $reservations->total(),
                    'from' => $reservations->firstItem(),
                    'to' => $reservations->lastItem(),
                ],
                'filters' => [
                    'statuses' => EquipmentReservation::getStatuses(),
                    'applied_filters' => $request->only(['status', 'date_from', 'date_to']),
                ],
            ];

            return $this->sendResponse($data, 'My reservations retrieved successfully');
        } catch (\Exception $e) {
            return $this->sendError('Error retrieving my reservations', $e->getMessage(), 500);
        }
    }

    /**
     * Get reservation statistics.
     */
    public function statistics()
    {
        try {
            $stats = [
                'total' => EquipmentReservation::count(),
                'confirmed' => EquipmentReservation::confirmed()->count(),
                'cancelled' => EquipmentReservation::where('status', 'cancelled')->count(),
                'completed' => EquipmentReservation::where('status', 'completed')->count(),
                'today' => EquipmentReservation::forDate(now()->toDateString())->count(),
                'upcoming' => EquipmentReservation::where('reservation_date', '>', now()->toDateString())->count(),
                'past' => EquipmentReservation::where('reservation_date', '<', now()->toDateString())->count(),
                'by_equipment' => [],
                'by_status' => [],
            ];

            // Get statistics by equipment
            $equipmentStats = EquipmentReservation::with('equipment')
                ->selectRaw('equipment_id, COUNT(*) as total, status, COUNT(*) as count')
                ->groupBy('equipment_id', 'status')
                ->get();

            foreach ($equipmentStats as $stat) {
                if (!isset($stats['by_equipment'][$stat->equipment_id])) {
                    $stats['by_equipment'][$stat->equipment_id] = [
                        'name' => $stat->equipment->name,
                        'total' => 0,
                        'confirmed' => 0,
                        'cancelled' => 0,
                        'completed' => 0,
                    ];
                }
                $stats['by_equipment'][$stat->equipment_id]['total'] += $stat->total;
                $stats['by_equipment'][$stat->equipment_id][$stat->status] = $stat->count;
            }

            // Get statistics by status
            foreach (EquipmentReservation::getStatuses() as $status => $name) {
                $stats['by_status'][$status] = [
                    'name' => $name,
                    'count' => EquipmentReservation::where('status', $status)->count(),
                ];
            }

            return $this->sendResponse($stats, 'Reservation statistics retrieved successfully');
        } catch (\Exception $e) {
            return $this->sendError('Error retrieving statistics', $e->getMessage(), 500);
        }
    }
}
