<?php

namespace App\Http\Controllers\Hotel;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use Carbon\Carbon;

use App\Models\Environment\PaymentMethod;
use App\Models\Hotel\Agency;
use App\Models\Hotel\Client;
use App\Models\Hotel\Regimen;
use App\Models\Hotel\Reservation;
use App\Models\Hotel\ReservationHistory;
use App\Models\Hotel\Room;
use App\Models\Hotel\RoomType;
use App\Models\Hotel\Source;
use App\Models\Hotel\Status;

use App\Models\Hotel\ConsumptionCharges;
use App\Models\Hotel\ConsumptionPayments;
use App\Models\Hotel\ExtraCharges;
use App\Models\Hotel\ExtraPayments;
use App\Models\Hotel\LodgingPayments;

use App\Http\Controllers\Hotel\RoomController;

use App\Helpers\Environment\DeviceManagementHelper;
use App\Helpers\Environment\SystemFeaturesHelper;

class ReservationController extends Controller
{
  //* Constructor
    public function __construct() {
        $this->middleware(['auth']);
    }

    //* CRUD Functions
        public function index(Request $request) {
          // View & Data
            $statuses = Status::all();
            $agencies = Agency::all();
            $sources = Source::all();
            $roomTypes = RoomType::orderBy('id', 'desc')->get();
            $rooms = Room::all();
            $regimens = Regimen::all();
            $paymentMethods = PaymentMethod::all();

            return view('hotel.reservations.index', compact([
                'statuses',
                'agencies',
                'sources',
                'roomTypes',
                'rooms',
                'regimens',
                'paymentMethods',
            ]));
        }

        public function store(Request $request) {
            // Validate Data
            $this->validateReservation($request);

            // Start Storage
            DB::beginTransaction(); //? Start Transaction

            try {
              // Definir Estatus y Progresos
                $statusData = $this->getReservationStatus($request);

              // Procesar Datos del cliente
                $client_data = $this->parseClientData($request);
                    if (empty($request->client_id)) { // Create Client
                        $client = Client::create($client_data);
                        $client_id = $client->id;
                    } else { // Update Client
                        Client::findOrFail($request->client_id)->update($client_data);
                        $client_id = $request->client_id;
                    }

              // Procesar Datos de la reserva
                // Merge
                    $form_data = $this->parseCommonReservationData($request);
                    $form_data = array_merge($form_data, [
                        'client_id' => $client_id,
                        'has_checkOut' => $statusData['has_checkout'],
                        'has_checkIn' => $statusData['has_checkin'],
                    ]);

                // Define Checkin User
                    if ($statusData['has_checkin'] === 'Si')
                        $form_data['checkin_receptionist_id'] = $form_data['receptionist_id'];

              // Crear Reservación
                Reservation::create($form_data);

              // Iniciar Historial
                $lastReservationId = Reservation::latest('id')->value('id');
                $this->updateHistory($lastReservationId, 'Create', auth()->user()->name);

              // Retornar mensaje
                DB::commit(); //? Conclude Transaction and Commit Changes
                return response()->json(['success' => true, 'message' => 'Agendado Correctamente']);

            } catch (\Throwable $e) {
                DB::rollBack(); //? Conclude Transaction and Abort Changes

                Log::error('Error al crear reserva', [
                    'error' => $e->getMessage(),
                    'request' => $request->all()
                ]);
                return response()->json(['success' => false, 'message' => 'Error al guardar la reserva.'], 500);
            }
        }

        public function edit($id) {
          //* Ajax Request
            if(request()->ajax()){
              // Control Data
                $has_express_reservations = SystemFeaturesHelper::getFeatureStatus('express_reservations');

              // Record
                if ($has_express_reservations)
                    $data = Reservation::with('extraCharges')->findOrFail($id);
                else
                    $data = Reservation::findOrFail($id);

              // Return
                return response()->json(['data' => $data], 200, ['Content-Type' => 'application/json; charset=utf-8']);
            }

          //* Normal Request
            return view('errors.404');
        }

        public function update(Request $request) {
            // Validate Data
            $this->validateReservation($request);

            // Start Storage
            DB::beginTransaction(); //? Start Transaction

            try {
              // Definir Estatus y Progresos
                $statusData = $this->getReservationStatus($request);

              // Procesar Datos del cliente
                $client_data = $this->parseClientData($request);
                    if (empty($request->client_id)) { // Create Client
                        $client = Client::create($client_data);
                        $client_id = $client->id;
                    } else { // Update Client
                        Client::findOrFail($request->client_id)->update($client_data);
                        $client_id = $request->client_id;
                    }

              // Procesar Datos de la reserva
                // Get Latest Data
                $reservation = Reservation::findOrFail($request->hidden_id);

                // Merge
                    $form_data = $this->parseCommonReservationData($request);
                    $form_data = array_merge($form_data, [
                        'client_id' => $client_id,
                        'has_checkOut' => $statusData['has_checkout'],
                        'has_checkIn' => $statusData['has_checkin'],
                        'show_in_calendar' => $statusData['show_in_calendar'],
                    ]);

                // Define Checkin User
                if (
                    $statusData['has_checkin'] === 'Si'
                    && is_null($reservation->checkin_receptionist_id)
                )
                    $form_data['checkin_receptionist_id'] = $form_data['receptionist_id'];

              // Actualizar Reservación
                $reservation->update($form_data);

              // Actualizar Historial
                $this->updateHistory($request->hidden_id,'Update',auth()->user()->name);

              // Retornar mensaje
                DB::commit(); //? Conclude Transaction and Commit Changes
                return response()->json(['success', 'Actualizado con éxito']);

            } catch (\Throwable $e) {
                DB::rollBack(); //? Conclude Transaction and Abort Changes

                Log::error('Error al actualizar reserva', [
                    'error' => $e->getMessage(),
                    'request' => $request->all()
                ]);
                return response()->json(['success' => false, 'message' => 'Error al actualizar la reserva.'], 500);
            }
        }

        public function destroy($id) {
            $this->updateHistory($id,'Delete',auth()->user()->name);
            $data = Reservation::findOrFail($id);
            $data->delete();
        }

    //* Support Functions
        private function validateReservation(Request $request) {
          // Control Variables
            $has_express_reservations =  SystemFeaturesHelper::getFeatureStatus("express_reservations");
            $has_control_hours =  SystemFeaturesHelper::getFeatureStatus("control_hours");
            $is_express_reserve = $request->boolean("is_express_reserve");

          // Structure Validation
            $rules = [
              //* Basic Data
                // Of Creation
                'fe_reserva'                => ['required', 'date'],
                'receptionist_id'           => ['required', 'exists:users,id'],

                // Of Registration
                'client_id'                 => ['nullable', 'exists:clients,id'],
                'client_reference'          => ['required', 'string', 'max:255'],
                'fe_in'                     => ['required', 'date'],
                'ho_in'                     => ['required', 'regex:/^\d{2}:\d{2}(:\d{2})?$/'],
                'fe_out'                    => ['required', 'date', $has_express_reservations || $has_control_hours ? '' : 'after:fe_in'],
                'ho_out'                    => ['required', 'regex:/^\d{2}:\d{2}(:\d{2})?$/'],
                'reserva_noches'            => ['required', 'integer', $has_express_reservations || $has_control_hours ? 'min:0' : 'min:1'],
                'reserva_horas'             => ['required', 'integer', $is_express_reserve ? 'min:1' : 'min:0'],
                'reserva_adultos'           => ['required', 'integer', 'min:0'],
                'reserva_ninos'             => ['required', 'integer', 'min:0'],
                'comentarios'               => ['nullable', 'string', 'max:500'],

                // Of Stay
                'room_id'                   => ['required', 'exists:rooms,id'],
                'typeroom_id'               => ['required', 'exists:room_types,id'],
                'regimen_id'                => ['required', 'exists:regimens,id'],

                // Of Contact
                'agencia_id'                => ['nullable', 'exists:agencies,id'],
                'fuentes_id'                => ['required', 'exists:sources,id'],

                // Of Payment
                'discount'                  => ['required', 'numeric', 'min:0', 'max:100'],
                'precio_original_sugerido'  => ['required', 'numeric', 'min:0'],
                'precio_sugerido'           => ['required', 'numeric', 'min:0'],
                'precio_final'              => ['required', 'numeric', 'min:0'],
                'total'                     => ['required', 'numeric', 'min:0'],
                'lodgingContract'           => ['required', 'in:Pago en Destino,Cortesia,Credito'],

                // Complementary
                'reference_invoice_uuid'    => ['nullable', 'uuid'],

              //* Control Data
                'hidden_id'                 => ['nullable', 'exists:reservations,id'],
                'estatus_id'                => ['required', 'exists:statuses,id'],
                'guest_estatus_id'          => ['required', 'exists:statuses,id'],
            ];

            $attributes = [
              //* Basic Data
                // Of Creation
                'fe_reserva' => __('reservation-ballot-validation.attributes.fe_reserva'),
                'receptionist_id' => __('reservation-ballot-validation.attributes.receptionist_id'),

                // Of Registration
                'client_id' => __('reservation-ballot-validation.attributes.client_id'),
                'client_reference' => __('reservation-ballot-validation.attributes.client_reference'),
                'fe_in' => __('reservation-ballot-validation.attributes.fe_in'),
                'ho_in' => __('reservation-ballot-validation.attributes.ho_in'),
                'fe_out' => __('reservation-ballot-validation.attributes.fe_out'),
                'ho_out' => __('reservation-ballot-validation.attributes.ho_out'),
                'reserva_noches' => __('reservation-ballot-validation.attributes.reserva_noches'),
                'reserva_horas' => __('reservation-ballot-validation.attributes.reserva_horas'),
                'reserva_adultos' => __('reservation-ballot-validation.attributes.reserva_adultos'),
                'reserva_ninos' => __('reservation-ballot-validation.attributes.reserva_ninos'),
                'comentarios' => __('reservation-ballot-validation.attributes.comentarios'),

                // Of Stay
                'room_id' => __('reservation-ballot-validation.attributes.room_id'),
                'typeroom_id' => __('reservation-ballot-validation.attributes.typeroom_id'),
                'regimen_id' => __('reservation-ballot-validation.attributes.regimen_id'),

                // Of Contact
                'agencia_id' => __('reservation-ballot-validation.attributes.agencia_id'),
                'fuentes_id' => __('reservation-ballot-validation.attributes.fuentes_id'),

                // Of Payment
                'discount' => __('reservation-ballot-validation.attributes.discount'),
                'precio_original_sugerido' => __('reservation-ballot-validation.attributes.precio_original_sugerido'),
                'precio_sugerido' => __('reservation-ballot-validation.attributes.precio_sugerido'),
                'precio_final' => __('reservation-ballot-validation.attributes.precio_final'),
                'total' => __('reservation-ballot-validation.attributes.total'),
                'lodgingContract' => __('reservation-ballot-validation.attributes.lodgingContract'),

              //* Control Data
                'hidden_id' => __('reservation-ballot-validation.attributes.hidden_id'),
                'estatus_id' => __('reservation-ballot-validation.attributes.estatus_id'),
                'guest_estatus_id' => __('reservation-ballot-validation.attributes.guest_estatus_id'),
            ];

            $messages = [
                'fe_reserva.required'   => __('reservation-ballot-validation.messages.fe_reserva.required'),
                'fe_reserva.date'       => __('reservation-ballot-validation.messages.fe_reserva.date'),

                'receptionist_id.required' => __('reservation-ballot-validation.messages.receptionist_id.required'),
                'receptionist_id.exists'   => __('reservation-ballot-validation.messages.receptionist_id.exists'),

                'client_id.exists'   => __('reservation-ballot-validation.messages.client_id.exists'),

                'client_reference.required' => __('reservation-ballot-validation.messages.client_reference.required'),
                'client_reference.max'      => __('reservation-ballot-validation.messages.client_reference.max'),

                'fe_in.required' => __('reservation-ballot-validation.messages.fe_in.required'),
                'fe_in.date'     => __('reservation-ballot-validation.messages.fe_in.date'),

                'ho_in.required' => __('reservation-ballot-validation.messages.ho_in.required'),
                'ho_in.regex'    => __('reservation-ballot-validation.messages.ho_in.regex'),

                'fe_out.required' => __('reservation-ballot-validation.messages.fe_out.required'),
                'fe_out.date'     => __('reservation-ballot-validation.messages.fe_out.date'),
                'fe_out.after'    => __('reservation-ballot-validation.messages.fe_out.after'),

                'ho_out.required' => __('reservation-ballot-validation.messages.ho_out.required'),
                'ho_out.regex'    => __('reservation-ballot-validation.messages.ho_out.regex'),

                'reserva_noches.required' => __('reservation-ballot-validation.messages.reserva_noches.required'),
                'reserva_noches.integer'  => __('reservation-ballot-validation.messages.reserva_noches.integer'),
                'reserva_noches.min'      => __('reservation-ballot-validation.messages.reserva_noches.min'),

                'reserva_adultos.required' => __('reservation-ballot-validation.messages.reserva_adultos.required'),
                'reserva_adultos.integer'  => __('reservation-ballot-validation.messages.reserva_adultos.integer'),
                'reserva_adultos.min'      => __('reservation-ballot-validation.messages.reserva_adultos.min'),

                'reserva_ninos.required' => __('reservation-ballot-validation.messages.reserva_ninos.required'),
                'reserva_ninos.integer'  => __('reservation-ballot-validation.messages.reserva_ninos.integer'),
                'reserva_ninos.min'      => __('reservation-ballot-validation.messages.reserva_ninos.min'),

                'comentarios.max' => __('reservation-ballot-validation.messages.comentarios.max'),

                'room_id.required' => __('reservation-ballot-validation.messages.room_id.required'),
                'room_id.exists'   => __('reservation-ballot-validation.messages.room_id.exists'),

                'typeroom_id.required' => __('reservation-ballot-validation.messages.typeroom_id.required'),
                'typeroom_id.exists'   => __('reservation-ballot-validation.messages.typeroom_id.exists'),

                'regimen_id.required' => __('reservation-ballot-validation.messages.regimen_id.required'),
                'regimen_id.exists'   => __('reservation-ballot-validation.messages.regimen_id.exists'),

                'agencia_id.exists'   => __('reservation-ballot-validation.messages.agencia_id.exists'),

                'fuentes_id.required' => __('reservation-ballot-validation.messages.fuentes_id.required'),
                'fuentes_id.exists'   => __('reservation-ballot-validation.messages.fuentes_id.exists'),

                'discount.required' => __('reservation-ballot-validation.messages.discount.required'),
                'discount.numeric'  => __('reservation-ballot-validation.messages.discount.numeric'),
                'discount.min'      => __('reservation-ballot-validation.messages.discount.min'),
                'discount.max'      => __('reservation-ballot-validation.messages.discount.max'),

                'precio_original_sugerido.required' => __('reservation-ballot-validation.messages.precio_original_sugerido.required'),
                'precio_original_sugerido.numeric'  => __('reservation-ballot-validation.messages.precio_original_sugerido.numeric'),
                'precio_original_sugerido.min'      => __('reservation-ballot-validation.messages.precio_original_sugerido.min'),

                'precio_sugerido.required' => __('reservation-ballot-validation.messages.precio_sugerido.required'),
                'precio_sugerido.numeric'  => __('reservation-ballot-validation.messages.precio_sugerido.numeric'),
                'precio_sugerido.min'      => __('reservation-ballot-validation.messages.precio_sugerido.min'),

                'precio_final.required' => __('reservation-ballot-validation.messages.precio_final.required'),
                'precio_final.numeric'  => __('reservation-ballot-validation.messages.precio_final.numeric'),
                'precio_final.min'      => __('reservation-ballot-validation.messages.precio_final.min'),

                'total.required' => __('reservation-ballot-validation.messages.total.required'),
                'total.numeric'  => __('reservation-ballot-validation.messages.total.numeric'),
                'total.min'      => __('reservation-ballot-validation.messages.total.min'),

                'lodgingContract.required' => __('reservation-ballot-validation.messages.lodgingContract.required'),
                'lodgingContract.in'       => __('reservation-ballot-validation.messages.lodgingContract.in'),

                'hidden_id.exists'   => __('reservation-ballot-validation.messages.hidden_id.exists'),

                'estatus_id.required' => __('reservation-ballot-validation.messages.estatus_id.required'),
                'estatus_id.exists'   => __('reservation-ballot-validation.messages.estatus_id.exists'),

                'guest_estatus_id.required' => __('reservation-ballot-validation.messages.guest_estatus_id.required'),
                'guest_estatus_id.exists'   => __('reservation-ballot-validation.messages.guest_estatus_id.exists'),
            ];

          // Validate Fields
            return $request->validate($rules, $messages, $attributes);
        }

        private function parseClientData(Request $request) {
            return array_filter([
                'client_name' => $request->client_reference,
                'client_phone' => $request->client_phone ?? __('neutral_words')['none_2'],
                'client_email' => $request->client_email ?? __('neutral_words')['none_2'],
                'client_state' => $request->client_state ?? __('neutral_words')['none_2'],

                'identity_document_type' => $request->client_document_type,
                'identity_document_number' => $request->client_document_number ?? "--- --- ---",

                'customer_segment' => $request->client_segment_type,
                'behavior_report' => $request->client_behavior_report,

                'discount_per_loyalty' => $request->discount ?? 0.0,
            ], fn ($value) => $value !== null); // Retrieves only non-null fields
        }

        private function parseCommonReservationData(Request $request) {
            $has_assigned_room = $request->filled('room_id'); // Room has been selected?

            return [
                'agency_id' => $request->agencia_id,
                'guest_status_id' => $request->guest_estatus_id,
                'receptionist_id' => $request->receptionist_id,
                'roomType_id' => $has_assigned_room ? $request->typeroom_id : 2, // Use Unassigned type as alternative
                'room_id' => $has_assigned_room ? $request->room_id : 1, // Use Unassigned room as alternative
                'regimen_id' => $request->regimen_id,
                'source_id' => $request->fuentes_id,
                'status_id' => $request->estatus_id,

                'reservation_date' => $request->fe_reserva,
                'lodging_contract' => $request->lodgingContract,
                'is_express' => $request->boolean('is_express_reserve'),

                'date_entry' => $request->fe_in,
                'hour_entry' => $request->ho_in,
                'date_departure' => $request->fe_out,
                'hour_departure' => $request->ho_out,
                'number_nights' => $request->reserva_noches,
                'number_hours' => $request->reserva_horas,
                'number_adults' => $request->reserva_adultos,
                'number_children' => $request->reserva_ninos,
                'include_children_in_pax' => $request->boolean('count_children'),

                'discount_percentage' => $request->discount ?? 0.0,
                'original_suggested_price' => $has_assigned_room ? $request->precio_original_sugerido : 0.00, // Use Unassigned price as alternative
                'suggested_price' => $has_assigned_room ? $request->precio_sugerido : 0.00, // Use Unassigned price as alternative
                'final_price' => $has_assigned_room ? $request->precio_final : 0.00, // Use Unassigned price as alternative
                'total_price' => $has_assigned_room ? $request->total : 0.00, // Use Unassigned price as alternative

                'client_reference' => $request->client_reference,
                'reservation_reason' => $request->motivo_reserva,
                'notes' => $request->comentarios ?? __('neutral_words')['none'],
                'requiere_invoice' => $request->boolean('solicitar_factura'),
                'invoice_uuid' => $request->reference_invoice_uuid,
            ];
        }

        private function getReservationStatus(Request $request) {
            $id_guest_status = $request->guest_estatus_id;
            $id_status = $request->estatus_id;
            $has_checkin = $has_checkout = 'No';
            $show_in_calendar = 1;

            if ($id_status == '7') { //* Case: REservation Cancelled
                $show_in_calendar = 0;
            } else { //* Case: Normal Checkin
                if ($id_guest_status == '4') { // CheckIn
                    $has_checkin = 'Si';
                } elseif ($id_guest_status == '6') { // CheckOut
                    $has_checkin = 'Si';
                    $has_checkout = 'Si';
                } elseif (!in_array($id_guest_status, ['4', '6'])) {
                  // En caso de aplicar un No Show
                    $date_entry = Carbon::parse($request->fe_in);
                    $actual_date = Carbon::today();
                    $dayDiff = $actual_date->diffInDays($date_entry);

                    // Revisamos si se se debe mostrar en el Calendario
                    if ($actual_date->isAfter($date_entry) && $dayDiff >= 2 && in_array($id_guest_status, [5, "5"])) {
                        $show_in_calendar = 0;
                    }
                }
            }

            return [
                'has_checkin' => $has_checkin,
                'has_checkout' => $has_checkout,
                'show_in_calendar' => $show_in_calendar,
            ];
        }

    //* Advanced Functions
        // Filters
        public function filterTable(Request $request){
          // Control Data
            $filter_type = $request?->filter_type ? $request?->filter_type : [];
            $filter_agency = $request?->filter_agency ? $request?->filter_agency : [];
            $filter_contract = $request?->filter_contract ? $request?->filter_contract : [];
            $filter_status_payment = $request?->filter_status_payment ? $request?->filter_status_payment : [];

          // Filter By Dates
            $query = Reservation::query()
                ->with(['User', 'Room', 'Status']);

            if($request->filter_from && $request->filter_to){
                $query->whereBetween('date_entry', [$request->filter_from, $request->filter_to]);
            }

          // Filter By Id or Client
            if($request?->filter_guest_or_folio){
                $query->where(function($q) use ($request){
                    $q->where('client_reference','like', "%{$request->filter_guest_or_folio}%")
                    ->orWhere('id','like', "%{$request->filter_guest_or_folio}%");
                });
            }

          // Filter by Reservation Type
            if(!empty($filter_type)){
                $query->where(function ($q) use ($filter_type) {
                    $selected_types = array_filter($filter_type, fn($a) => !is_null($a));

                    if(!empty($selected_types))
                        $q->whereIn('is_express', $selected_types);
                });
            }

          // Filter by Agency
            if(!empty($filter_agency)){
                $query->where(function ($q) use ($filter_agency) {
                    $hasNull = in_array(null, $filter_agency, true);

                    $agencies = array_filter($filter_agency, fn($a) => !is_null($a));

                    if(!empty($agencies))
                        $q->whereIn('agency_id', $agencies);

                    if($hasNull)
                        $q->orWhereNull('agency_id');
                });
            }

          //Filter by Contract
            if(!empty($filter_contract))
                $query->whereIn('lodging_contract', $filter_contract);

          // Filter by Status
            if(!empty($filter_status_payment))
                $query->whereIn('payment_status', $filter_status_payment);

          // Build Data
            if(request()->ajax()){
                $control_hours = SystemFeaturesHelper::getFeatureStatus("control_hours");
                $express_reservations = SystemFeaturesHelper::getFeatureStatus("express_reservations");

                return datatables()->of($query->orderBy('id', 'desc'))
                    ->addColumn('tc', function($data){
                        if($data->lodging_contract === "Pago en Destino") return "PD";
                        if($data->lodging_contract === "Cortesia") return "CT";
                        if($data->lodging_contract === "Credito") return "CD";
                    })
                    ->addColumn('reservation_type', function($data){
                        return __('neutral_words')[$data->is_express ? 'express' : 'nights'];
                    })
                    ->addColumn('user', fn($data) => $data->User->name)
                    ->addColumn('check_in', function($data) use($control_hours, $express_reservations) {
                        return ($control_hours && $express_reservations)
                            ? "{$data->date_entry}<br>{$data->hour_entry}"
                            : $data->date_entry;
                    })
                    ->addColumn('checkout', function($data) use($control_hours, $express_reservations) {
                        return ($control_hours && $express_reservations)
                            ? "{$data->date_departure}<br>{$data->hour_departure}"
                            : $data->date_departure;
                    })
                    ->addColumn('num_room', fn($data) => $data->Room->room_name)
                    ->addColumn('length_of_stay', function($data) use($express_reservations){
                    // Dynamic Lang
                        $reservation_fields = __('entity_fields')['reservation'];

                    // Build Response
                        $nights = trans_choice($reservation_fields['dynamic_number_nights'], $data->number_nights);
                        $duration = "{$data->number_nights} {$nights}";

                            if ($express_reservations) {
                                $hours = trans_choice($reservation_fields['dynamic_number_hours'], $data->number_hours);
                                $duration .= ", {$data->number_hours} {$hours}";
                            }

                        return $duration;
                    })
                    ->addColumn('estado', fn($r) => $r->Status->status_name)
                    ->addColumn('action', function($data){
                        return "
                            <a href='/reservation-tickets/full-stay/$data->id' target='_blank' class='px-0 btn btn-secondary fixed-width'>
                                <i class='fas fa-eye'></i>
                            </a>&nbsp;

                            <button type='button' name='edit' id='$data->id' class='px-0 btn-sm edit btn btn-primary fixed-width'>
                                <i class='fas fa-edit'></i>
                            </button>&nbsp;

                            <button type='button' name='delete' id='$data->id' class='px-0 delete btn btn-danger fixed-width'>
                                <i class='fas fa-trash-alt'></i>
                            </button>
                        ";
                    })
                    ->rawColumns(['action', 'check_in', 'checkout'])
                    ->with([
                        'is_mobile' => DeviceManagementHelper::isMobile($request),
                    ])
                    ->make(true);
            }
        }

        // Calendar
        public function updateByNormalDragAndDrop($id, Request $request) {
          // Validate Required Fields
            $requiredFields = ['entry_date', 'departure_date', 'room_id', 'receptionist'];
                if ($missing = collect($requiredFields)->filter(fn($f) => !$request->filled($f))->all())
                    return response()->json(['message' => 'Missing required fields'], 401);

          // Define Control Data
            // Get Records
            $data = Reservation::findOrFail($id);

            // Form Request
            $is_express = $request->boolean('is_express');
            $has_pax_pricing = $request->boolean('has_pax_pricing');
            $has_express_reservations = $request->boolean('has_express_reservations');

            $date_entry = Carbon::parse( $request->input('entry_date') );
            $date_departure = Carbon::parse( $request->input('departure_date') );

            $room_id = $request->input('room_id');
            $receptionist = $request->input('receptionist');

            // Calculates the difference between Reservation's Dates
            $number_nights = $date_entry->diffInDays($date_departure);

          // Calculate Total for Reserve
            $room_price = $data->final_price;
            $total = $is_express ? $room_price : $number_nights * $room_price;

          // Get the new Room Data
            $room_data = Room::with('roomType')->findOrFail($room_id);

            $price_request = new Request([
              // Period
                'from' => $date_entry,
                'to' => $date_departure,

              // Pax Status
                "count_children" => $data->include_children_in_pax,
                "has_pax_pricing" => $has_pax_pricing,
                "adults" => $data->number_adults,
                "children" => $data->number_children,
                "room_limit" => $room_data->roomType->pax_room_limit,

              // Express Status
                "has_express_reservations" => $has_express_reservations,
                "is_express" => $data->is_express,
                "hour_entry" => $date_entry->copy()->format("H:i"),
                "hour_departure" => $date_departure->copy()->format("H:i"),
                "selected_package" => $data->number_hours,
                "minimum_stay" => $room_data->roomType->minimum_express_stay,
            ]);

            $room_price_data = (new RoomController)->getPrice($room_id, $price_request);
            $room_price_data = $room_price_data->getData();

            $room_id = $room_id;
            $room_type = $room_data->roomType_id;
            $suggested_price = (float)str_replace(',', '', $room_price_data->room_price);

          // Update Reservation Data
            $data->date_entry = $date_entry;
            $data->date_departure = $date_departure;
                if ($is_express) {
                    $data->hour_entry = $date_entry->copy()->format("H:i");
                    $data->hour_departure = $date_departure->copy()->format("H:i");
                } else {
                    $data->number_nights = $number_nights;
                }

            $data->room_id = $room_id;
            $data->roomType_id = $room_type;
            $data->suggested_price = $suggested_price;
            $data->total_price = $total;
            $data->save();

            $this->updateHistory($id,'Update', $receptionist);

            return response()->json(['success', 'Actualizado']);
        }

        // Payments & Charges
        public function getPaymentAndCharges($reservation_id) {
            $models = [
                'lodging_payments' => LodgingPayments::class,
                'extra_payments' => ExtraPayments::class,
                'extra_charges' => ExtraCharges::class,
                'consumption_payments' => ConsumptionPayments::class,
                'consumption_charges' => ConsumptionCharges::class,
            ];

            $result = [];

            foreach ($models as $key => $model) {
                $result[$key] = $model::where('reservation_id', $reservation_id)->get();
            }

            return $result;
        }

        // Contracts
        public function updateConsumptionContract($id, Request $request){
            // Validate Required Fields
            $requiredFields = ['type', 'receptionist'];
                if ($missing = collect($requiredFields)->filter(fn($f) => !$request->filled($f))->all())
                    return response()->json(['message' => 'Missing required fields'], 401);

            // Define Control Data
            $type = $request->input('type');
            $receptionist = $request->input('receptionist');

            // Update Reservation
            $reserve = Reservation::findOrFail($id);
            $reserve->consumption_contract = $type;
            $reserve->save();

            // Update reservation History
            $this->updateHistory($id,'Update', $receptionist);

            return response()->json(['success', 'Actualizado']);
        }

        public function updateExtraContract($id, Request $request){
            // Validate Required Fields
            $requiredFields = ['type', 'receptionist'];
                if ($missing = collect($requiredFields)->filter(fn($f) => !$request->filled($f))->all())
                    return response()->json(['message' => 'Missing required fields'], 401);

            // Define Control Data
            $type = $request->input('type');
            $receptionist = $request->input('receptionist');

            // Update Reservation
            $reserve = Reservation::findOrFail($id);
            $reserve->extras_contract = $type;
            $reserve->save();

            // Update reservation History
            $this->updateHistory($id,'Update', $receptionist);

            return response()->json(['success', 'Actualizado']);
        }

        // History Control
        public function updateHistory($id, $action, $cashier) {

            $this->updatePaymentStatus($id);

            // Data for 'Reservation_Data'
            $reservation = Reservation::findOrFail($id);
            $lodgingPayments = LodgingPayments::where('reservation_id',$reservation->id)->get();
            $consumptionCharges = ConsumptionCharges::where('reservation_id',$reservation->id)->get();
            $consumptionPayments = ConsumptionPayments::where('reservation_id',$reservation->id)->get();
            $extraCharges = ExtraCharges::where('reservation_id',$reservation->id)->get();
            $extraPayments = ExtraPayments::where('reservation_id',$reservation->id)->get();

            $consumptionPrice = ConsumptionCharges::where('reservation_id',$reservation->id)->where('status', 'Activo')->sum('consumption_price');
            $extraPrice = ExtraCharges::where('reservation_id',$reservation->id)->where('status', 'Activo')->sum('extras_price');

            $reservation_data = [
                'action' => $action,
                'id' => $reservation->id,
                'client' => $reservation->client_reference,
                'checkin' => $reservation->date_entry,
                'checkin_time' => $reservation->hour_entry,
                'checkout' => $reservation->date_departure,
                'checkout_time' => $reservation->hour_departure,
                'room_type' => optional($reservation->roomType)->tr_name,
                'room_name' => optional($reservation->room)->room_name,
                'status' => optional($reservation->status)->status_name,
                'guest_status' => optional($reservation->guestStatus)->status_name,
                'adults' => $reservation->number_adults,
                'children' => $reservation->number_children,
                'nights' => $reservation->number_nights,
                'hours' => $reservation->number_hours,
                'regimen' => optional($reservation->regimen)->regimen_name,
                'lodging_contract' => $reservation->lodging_contract,
                'consumption_contract' => $reservation->consumption_contract,
                'extras_contract' => $reservation->extras_contract,
                'source' => optional($reservation->source)->source_name,
                'agency' => optional($reservation->agency)->agency_name ?? __('neutral_words')['none'],

                'room_cost' => $reservation->final_price,
                'lodging_cost' => $reservation->total_price,
                'lodging_payments' => $lodgingPayments,

                'consumption_cost' => $consumptionPrice,
                'consumption_charges' => $consumptionCharges,
                'consumption_payments' => $consumptionPayments,

                'extra_cost' => $extraPrice,
                'extra_charges' => $extraCharges,
                'extra_payments' => $extraPayments,

                'total_cost' => $reservation->total_price + $consumptionPrice + $extraPrice,
            ];

            // Data for 'Reservation_Changes'
            $reservation_changes = [];
            $reservation_history = ReservationHistory::where('id_reservation', $id)->orderBy('id', 'desc')->first();

            if($reservation_history){
                $last_data = $reservation_history->reservation_data;
                    unset($last_data['action']);
                    unset($last_data['lodging_payments']);
                    unset($last_data['consumption_charges']);
                    unset($last_data['consumption_payments']);
                    unset($last_data['extra_charges']);
                    unset($last_data['extra_payments']);

                $new_data = $reservation_data;
                    unset($new_data['action']);
                    unset($new_data['lodging_payments']);
                    unset($new_data['consumption_charges']);
                    unset($new_data['consumption_payments']);
                    unset($new_data['extra_charges']);
                    unset($new_data['extra_payments']);

                    // Identify changes by comparison of old reservation values
                    foreach ($last_data as $key => $old_value) {
                        if (array_key_exists($key, $new_data)) {
                            $new_value = $new_data[$key];

                            if ($old_value != $new_value) {
                                // Translate if key is about Statuses
                                switch (true) {
                                    case in_array($key, ['status', 'guest_status']):
                                        $reservation_changes[$key] = __('seeders')['statuses'][$old_value] . ' => ' . __('seeders')['statuses'][$new_value];
                                        break;

                                    default:
                                        $reservation_changes[$key] = $old_value . ' => ' . $new_value;
                                        break;
                                }
                            }
                        }
                    }
            }

            $updatedHistoryData = [
                'id_reservation' => $id,
                'editor_name' => $cashier,
                'reservation_data' => $reservation_data,
                'reservation_changes' => $reservation_changes,
            ];
            ReservationHistory::create($updatedHistoryData);
        }

        public function findHistoryData($id) {
            if(request()->ajax()){
                $quantity = ReservationHistory::where('id_reservation',$id)->count();

                if($quantity){
                    return response()->json('Historial Existente',201);
                } else {
                    return response()->json('Historial Inexistente',404);
                }
            }

            return view('errors.404');
        }

        public function updatePaymentStatus($id){
            $reservation = Reservation::findOrFail($id);

            $lodgingCost = $reservation->total_price;
            $lodgingPaid = LodgingPayments::where('reservation_id',$id)->where('status', 'Activo')->sum('payment_price');

            $lodgingOwed = $lodgingCost - $lodgingPaid;

            //* Estados de Pagos:
            //   - Pago completo: #37f00f
            //   - Anticipo: #ffcf00
            //   - Falta de pago: #fd2319

            if ($lodgingOwed == $lodgingCost) { // Pago Faltante
                $reservation->payment_status = '#fd2319';
                // $reservation->save();
            } elseif ($lodgingOwed > 0 && $lodgingOwed < $lodgingCost) { // Pago en Proceso
                $reservation->payment_status = '#ffcf00';
                // $reservation->save();
            } elseif ($lodgingOwed <= 0) { // Pago Finalizado
                $reservation->payment_status = '#37f00f';
            }
            $reservation->save();

        }

}
