<?php

namespace routes;

use classes\authentication;
use customers\economic_customer_mo;
use objects\bookings_o;
use objects\customer_vehicles_o;
use objects\departments_o;
use objects\logs_o;
use objects\order_bookings_o;
use objects\orders_o;
use objects\plate_scans_o;
use objects\products_o;
use objects\users_o;
use traits\route_t;
use modules\subusers\helpers\subusers_permission_node_key;

class vehiclesRoute
{
    use route_t;

    public function run(): void
    {
        $this->get('/vehicles', function () {
            global $response;
            $auth = new authentication();
            $user = $auth->get_user();
            // Define permissions with subuser node linkage
            $permission_own = self::definePermission('list_own_vehicles', subusers_permission_node_key::VEHICLES_LIST);
            $permission_other = self::definePermission('list_vehicles_other');
            $has_permission_other = self::hasPermission($permission_other);

            // If a specific ID is requested, validate access against that vehicle's customer context
            if ($this->isParametersSet(['id'])) {
                $id = (int)$this->getParameter('id');
                $this->requireType($id, self::type_int());
                $this->requireMinValue($id, 1);
                $this->requireMaxValue($id, 9999999999);
                $vehicle = (new customer_vehicles_o())->select($id);
                if (!$vehicle->exists()) {
                    (new logs_o())->add('vehicles', 'global', 1, (int)($user->id ?? 0), 'LIST_OWN_VEHICLES', 'Vehicle not found');
                    $response->error('Vehicle not found', 404);
                }
                $targetCustomer = (int)$vehicle->customer_id->value();
                self::allowOwnOrDepartmentAccess(
                    $permission_own,
                    $permission_other,
                    $targetCustomer,
                    null,
                    null,
                    'You do not have permission to view this vehicle.'
                );
                $response->success([...$vehicle->asArray()]);
            }

            // Listing: restrict to effective customer when lacking the broader permission
            $vehicles_o = new customer_vehicles_o();
            $effectiveCustomer = self::resolveEffectiveCustomerNumber();
            $response->success(
                $vehicles_o->listObjectsWithPaginationIfSet(
                    function ($vehicle) {
                        return [...(new customer_vehicles_o())->select($vehicle['id'])->asArray()];
                    },
                    $vehicles_o->forceRestrictFilters([
                        ...(!$has_permission_other && $effectiveCustomer !== null ? [
                            'customer_id' => [(int)$effectiveCustomer]
                        ] : [])
                    ])
                )
            );
        },
            [
                'list_own_vehicles' => 'List own vehicles',
                'list_vehicles_other' => 'List other users vehicles',
            ]
        );

        $this->get('/department/vehicles/unknown-customer', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('list_unknown_customer_vehicles');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Log the incident
                (new logs_o())->add('vehicles', 'global', 1, $user->id, 'LIST_UNKNOWN_CUSTOMER_VEHICLES', 'Successfully listed unknown customer vehicles');
                // Return the list of the user's vehicles
                $vehicles_o = new orders_o();
                $vehicles_o->setView('unique_order_reg_1');
                $response->success($vehicles_o
                    ->setSearchableFields([
                        'reg_1'
                    ])
                    ->listObjectsWithPaginationIfSet()
                );
            } else {
                // Log the incident
                (new logs_o())->add('vehicles', 'global', 1, 0, 'LIST_UNKNOWN_CUSTOMER_VEHICLES', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'list_unknown_customer_vehicles' => 'List unknown customer vehicles',
            ]
        );

        $this->post('/vehicles', function () {
            global $response;
            $auth = new authentication();
            $user = $auth->get_user();

            // Define permissions with subuser node linkage
            $permission_own = self::definePermission('add_vehicle', subusers_permission_node_key::VEHICLES_ADD);
            $permission_other = self::definePermission('add_vehicle_other');

            // Require the parameters
            self::requireParameters([
                'type',
                'reg',
                'wash_subscription',
            ]);

            // Determine target customer
            $targetCustomer = self::isParametersSet(['customer_id'])
                ? (int)self::getParameter('customer_id')
                : (int)(self::resolveEffectiveCustomerNumber() ?? 0);
            self::requireType($targetCustomer, self::type_int());
            self::requireMinValue($targetCustomer, 1);
            self::requireMaxValue($targetCustomer, 9999999999);

            // Enforce access (own vs broader)
            self::allowOwnOrDepartmentAccess(
                $permission_own,
                $permission_other,
                $targetCustomer,
                null,
                null,
                'You are not allowed to add vehicles to this customer'
            );

            $reference = null;
            if (self::isParametersSet(['reference'])) {
                if (!empty(self::getParameter('reference'))) {
                    $reference = (string)self::getParameter('reference');
                    self::requireType($reference, self::type_string());
                    self::requireMinLength('reference', 1);
                    self::requireMaxLength('reference', 255);
                }
            }
            // Validate the parameters
            self::requireType(self::getParameter('reg'), self::type_string());
            self::requireType(self::getParameter('type'), self::type_int());
            self::requireType(self::getParameter('wash_subscription'), self::type_bool());
            // Get the parameters
            $reg = (string)self::getParameter('reg');
            $type = (int)self::getParameter('type');
            $subscription = (bool)self::getParameter('wash_subscription');
            $reg = trim($reg);

            // Create a new vehicle
            $vehicle = new customer_vehicles_o();
            $vehicle->add(
                $targetCustomer,
                $type,
                $reg,
                $subscription ? 1 : 0,
                $reference
            );

            (new logs_o())->add('vehicles', 'global', 1, (int)($user->id ?? 0), 'ADD_VEHICLE', 'Successfully added vehicle');
            $response->success($vehicle->asArray());
        },
            [
                'add_vehicle' => 'Add a vehicle to own vehicles',
                'add_vehicle_other' => 'Add a vehicle to another users vehicles',
            ]
        );

        $this->put('/vehicles', function () {
            global $response;
            $auth = new authentication();
            $user = $auth->get_user();
            $permission_own = self::definePermission('edit_vehicle', subusers_permission_node_key::VEHICLES_EDIT);
            $permission_other = self::definePermission('edit_vehicle_other');
            // Get the request data
            self::requireParameters(['id']);
            $id = (int)self::getParameter('id');
            $vehicle = (new customer_vehicles_o())->select($id);
            if (!$vehicle->exists()) {
                (new logs_o())->add('vehicles', 'global', 1, (int)($user->id ?? 0), 'EDIT_VEHICLE', 'Vehicle not found');
                $response->error('Vehicle not found', 404);
            }
            // Enforce access (own vs broader)
            self::allowOwnOrDepartmentAccess(
                $permission_own,
                $permission_other,
                (int)$vehicle->customer_id->value(),
                null,
                null,
                'You are not allowed to edit vehicles from other users'
            );
                // Check all the fields, and if they are set, validate and set them
                if (self::isParametersSet(['type'])) {
                    $type = (int)self::getParameter('type');
                    // Make sure the type is an integer
                    self::requireType($type, self::type_int());
                    self::requireMinValue($type, 0);
                    // Make sure the type is a valid type (If it isn't 0)
                    if ($type === 0) {
                        // Set the type to null
                        $vehicle->type->set(0);
                        // Turn off the subscription
                        $vehicle->wash_subscription->set(0);
                        return;
                    } else {
                        $products_o = new products_o();
                        $products_o->select((int)$type);
                        if (!$products_o->exists() || !$products_o->subscription_allowed->value()) {
                            // Log the incident
                            (new logs_o())->add('vehicles', 'global', 1, $user->id, 'EDIT_VEHICLE', 'Invalid type');
                            // Return an error
                            $response->error('Invalid type', 400);
                        }
                        // Set the type
                        $vehicle->type->set(
                            (int)$type
                        );
                        // TODO: Make the potential vehicleTypeId reflect the correct type.
                        // It's currently possible to set the vehicleTypeId to a type that is not allowed for the vehicle.
                    }
                }
                if (self::isParametersSet(['reg'])) {
                    $reg = (string)self::getParameter('reg');
                    self::requireType($reg, self::type_string());
                    self::requireMinLength('reg', 2);
                    self::requireMaxLength('reg', 12);
                    // Strip the registration number of whitespace
                    $reg = preg_replace('/\s+/', '', $reg);
                    // Set the registration number
                    $vehicle->reg->set($reg);
                }
                if (self::isParametersSet(['wash_subscription'])) {
                    $subscription = (bool)self::getParameter('wash_subscription');
                    self::requireType($subscription, self::type_bool());
                    // Check if the type is a valid type (If it isn't 0)
                    if ((int)$vehicle->type->value() === 0 && $subscription) {
                        // Set the subscription to null
                        $response->error('Unable to set subscription, type is not set', 400);
                    }
                    // Set the wash subscription
                    $vehicle->wash_subscription->set($subscription ? 1 : 0);
                }
                if (self::isParametersSet(['reference'])) {
                    // Check if the reference is set to null/empty
                    if (empty(self::getParameter('reference'))) {
                        // Set the reference to null
                        $vehicle->reference->nullify();
                    } else {
                        // Check if the reference is valid
                        $reference = (string)self::getParameter('reference');
                        self::requireType($reference, self::type_string());
                        self::requireMinLength('reference', 1);
                        self::requireMaxLength('reference', 255);
                        // Set the reference
                        $vehicle->reference->set($reference);
                    }
                }
                $vehicle->objectChanged();
                // Return the vehicle
                $response->success($vehicle->asArray());
        },
            [
                'edit_vehicle' => 'Edit a vehicle',
                'edit_vehicle_other' => 'Edit a vehicle from another user'
            ]
        );

        $this->delete('/vehicles', function () {
            global $response;
            $auth = new authentication();
            $user = $auth->get_user();
            $permission_own = self::definePermission('delete_vehicle', subusers_permission_node_key::VEHICLES_DELETE);
            $permission_other = self::definePermission('delete_vehicle_other');
            // Get the request data
            self::requireParameters(['id']);
            $id = (int)self::getParameter('id');
            $vehicle = (new customer_vehicles_o())->select($id);
            if (!$vehicle->exists()) {
                (new logs_o())->add('vehicles', 'global', 1, (int)($user->id ?? 0), 'DELETE_VEHICLE', 'Vehicle not found');
                $response->error('Vehicle not found', 404);
            }
            // Enforce access (own vs broader)
            self::allowOwnOrDepartmentAccess(
                $permission_own,
                $permission_other,
                (int)$vehicle->customer_id->value(),
                null,
                null,
                'You are not allowed to delete vehicles from other users'
            );
            // Delete the vehicle
            $vehicle->delete();
            $response->success([
                'success' => true,
                'message' => 'Vehicle deleted successfully'
            ]);
        },
            [
                'delete_vehicle' => 'Delete a vehicle',
                'delete_vehicle_other' => 'Remove (delete) a vehicle from another user'
            ]
        );

        $this->get('/department/vehicle/customer-suggestions', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('list_vehicle_customer_suggestions');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                self::requireParameters(['reg_1']);
                self::requireType((string)self::getParameter('reg_1'), self::type_string());
                $plate = (string)self::getParameter('reg_1');
                self::requireMinLength('reg_1', 1);
                self::requireMaxLength('reg_1', 12);
                // Return the list of the user's vehicles
                $orders_o = new orders_o();
                // Check if the user is allowed to list other user's vehicles
                // Get all the customer_ids that has orders with the same plate
                $customer_ids = $orders_o->getFieldsWhere([
                    'reg_1' => $plate,
                    'deleted_at' => null,
                ], [
                    'customer_id',
                ]);
                // Get all the customer_ids that has orders with the same plate
                $customer_ids = array_map(function ($customer_id) {
                    return (int)$customer_id['customer_id'];
                }, $customer_ids);
                // Remove duplicates
                $customer_ids = array_unique($customer_ids);
                // Get all the customers that has orders with the same plate
                $customers = [];
                foreach ( $customer_ids as $customer_id ) {
                    $customers[] = (new users_o())->getUserByCustomerNumber($customer_id);
                }
                // Log the incident
                (new logs_o())->add('vehicles', 'global', 1, $user->id, 'LIST_VEHICLE_CUSTOMER_SUGGESTIONS', 'Successfully listed customer suggestions for a vehicle');
                // Format the response
                $response->success(
                    array_map(function ($customer) {
                        /** @var users_o $customer */
                        $customer_number = (int)$customer->customer_number->value();
                        return [
                            'id' => (int)$customer->id,
                            'customer_name' => (string)$customer->getCustomerName((int)$customer_number),
                            'customer_number' => (int)$customer_number,
                            'barred' => (new economic_customer_mo())->getCustomerByCustomerNumber((int)$customer_number)->asArray()['barred'],
                        ];
                    }, $customers)
                );

            } else {
                // Log the incident
                (new logs_o())->add('vehicles', 'global', 1, 0, 'LIST_VEHICLE_CUSTOMER_SUGGESTIONS', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'list_vehicle_customer_suggestions' => 'List customer suggestions for a vehicle',
            ]
        );

        $this->post('/vehicles/set-auto-start-on-lpr', function () {
            global $response;
            $auth = new authentication();
            $user = $auth->get_user();
            $permission_own = self::definePermission('set_auto_start_on_lpr', subusers_permission_node_key::VEHICLES_EDIT);
            $permission_other = self::definePermission('set_auto_start_on_lpr_other');
            self::requireParameters(['id', 'active']);
            $id = (int)self::getParameter('id');
            self::requireType($id, self::type_int());
            self::requireMinValue($id, 1);
            self::requireMaxValue($id, 9999999999);
            $autoStartOnLpr = (bool)self::getParameter('active');
            self::requireType($autoStartOnLpr, self::type_bool());
            $vehicle = (new customer_vehicles_o())->select($id);
            if (!$vehicle->exists()) {
                (new logs_o())->add('vehicles', 'global', 1, (int)($user->id ?? 0), 'SET_AUTO_START_ON_LPR', 'Vehicle not found');
                $response->error('Vehicle not found', 404);
            }
            self::allowOwnOrDepartmentAccess(
                $permission_own,
                $permission_other,
                (int)$vehicle->customer_id->value(),
                null,
                null,
                'You are not allowed to edit vehicles from other users'
            );
            $vehicle->setAutoStartOnLpr($autoStartOnLpr);
            $response->success([...$vehicle->asArray()]);
        },
            [
                'set_auto_start_on_lpr' => 'Set auto start on LPR',
                'set_auto_start_on_lpr_other' => 'Set auto start on LPR for another user\'s vehicle',
            ]
        );

        $this->post('/vehicles/set-vehicle-type-id', function () {
            global $response;
            $auth = new authentication();
            $user = $auth->get_user();
            $permission_own = self::definePermission('set_vehicle_type_id', subusers_permission_node_key::VEHICLES_EDIT);
            $permission_other = self::definePermission('set_vehicle_type_id_other');
            self::requireParameters(['id', 'vehicleTypeId']);
            $id = (int)self::getParameter('id');
            self::requireType($id, self::type_int());
            self::requireMinValue($id, 1);
            self::requireMaxValue($id, 9999999999);
            $vehicleTypeId = (string)self::getParameter('vehicleTypeId');
            self::requireType($vehicleTypeId, self::type_string());
            self::requireMinLength('vehicleTypeId', 1);
            self::requireMaxLength('vehicleTypeId', 50);
            $vehicle = (new customer_vehicles_o())->select($id);
            if (!$vehicle->exists()) {
                (new logs_o())->add('vehicles', 'global', 1, (int)($user->id ?? 0), 'SET_VEHICLE_TYPE_ID', 'Vehicle not found');
                $response->error('Vehicle not found', 404);
            }
            self::allowOwnOrDepartmentAccess(
                $permission_own,
                $permission_other,
                (int)$vehicle->customer_id->value(),
                null,
                null,
                'You are not allowed to edit vehicles from other users'
            );
            $customer = (new users_o())->getUserByCustomerNumber((int)$vehicle->customer_id->value());
            if (!$vehicle->hasXLVask()) {
                if (!$customer->hasXLVaskCustomerAccount()) {
                    (new logs_o())->add('vehicles', 'global', 1, (int)($user->id ?? 0), 'SET_VEHICLE_TYPE_ID', 'Attempt to set vehicle type ID without XL Vask registration and no customer account');
                    $response->error('Vehicle is not registered in the XL Vask system', 400);
                } else {
                    $vehicle->createXLVaskVehicle($vehicleTypeId);
                }
            } else {
                $vehicle->setVehicleTypeId($vehicleTypeId);
            }
            $response->success([...$vehicle->asArray()]);
        },
            [
                'set_vehicle_type_id' => 'Set vehicle type ID',
                'set_vehicle_type_id_other' => 'Set vehicle type ID for another user\'s vehicle',
            ]
        );


        $this->get('/superuser/users-with-vehicle-subscriptions', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('list_users_with_vehicle_subscriptions');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Log the incident
                (new logs_o())->add('vehicles', 'global', 1, $user->id, 'LIST_USERS_WITH_VEHICLE_SUBSCRIPTIONS', 'Successfully listed users with vehicle subscriptions');
                // Return the list of users with vehicle subscriptions
                $vehicles_o = new customer_vehicles_o();
                $customer_ids = $vehicles_o->getFieldsWhere(
                    [
                        'wash_subscription' => 1,
                    ],
                    ['customer_id']
                );
                // Get all the customer_ids that has vehicle subscriptions
                $customer_ids = array_map(function ($customer_id) {
                    return (int)$customer_id['customer_id'];
                }, $customer_ids);
                // Remove duplicates
                $customer_ids = array_unique($customer_ids);
                $response->success(
                    $customer_ids ? array_map(function ($customer_id) {
                        $tmp_user = new users_o();
                        $tmp_user->getUserByCustomerNumber((int)$customer_id);
                        return [...$tmp_user->asArray()];
                    }, $customer_ids) : []
                );
            } else {
                // Log the incident
                (new logs_o())->add('vehicles', 'global', 1, 0, 'LIST_USERS_WITH_VEHICLE_SUBSCRIPTIONS', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'list_users_with_vehicle_subscriptions' => 'List users with vehicle subscriptions',
            ]
        );

        $this->get('/vehicles/status', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('view_vehicle_status');
            /** Define parameters */
            // "reg" - The registration number to check the status of
            self::requireParameters([
                'reg',
            ]);
            self::requireType((string)self::getParameter('reg'), self::type_string());
            $reg = (string)self::getParameter('reg');
            self::requireMinLength('reg', 1);
            self::requireMaxLength('reg', 12);
            // "department" - The department to check the status of (optional)
            if (self::isParametersSet(['department'])) {
                self::requireType((int)self::getParameter('department'), self::type_int());
                $department = (int)self::getParameter('department');
                self::requireMinValue($department, 1);
                self::requireMaxValue($department, 999999);
                // Check if the department exists
                $department_o = new departments_o();
                $department_o->select($department);
                if (!$department_o->exists()) {
                    $response->error('Department not found', 404);
                }
            } else {
                $department = null;
            }
            // Validate the registration number
            $this->validateRegistrationNumber($reg, $response);
            $customer_vehicles_o = new customer_vehicles_o();
            /** Determine the statuses of the vehicle */
            $isVerified = $customer_vehicles_o->countRowsWhere(
                [
                    'reg' => $reg,
                ]
            ) > 0;
            $isKnown = (new orders_o())->countRowsWhere(
                [
                    'reg_1' => $reg,
                ]
            ) > 1; // We need to go above one, as the current order will also be counted
            $isBooked = (new bookings_o())->countRowsWhere(
                [
                    'regNrTraekker' => $reg,
                    'status' => 'pending',
                    ...($department ? ['department' => $department] : []), // Filter by department if set
                ]
            ) > 0;
            // Check if the customer is barred (only if the vehicle is verified)
            $customer_number = $isVerified ? ($customer_vehicles_o->getFieldsWhere(
                [
                    'reg' => $reg,
                ],
                ['customer_id']
            )) : null;
            $customer_number = $customer_number ? (int)$customer_number[0]['customer_id'] : null;
            $cardPaymentRequired = ($customer_number && (new users_o())->isCustomerBarred((int)$customer_number));
            $last_order = $customer_vehicles_o->getLastOrderByPlate($reg);
            $response->success([
                'department' => $department,
                'reg' => $reg,
                'verified' => $isVerified,
                'known' => $isKnown,
                'booked' => $isBooked,
                'card' => $cardPaymentRequired,
                'unknown' => !$customer_number && !$isKnown && !$isBooked,
                'last_order_id' => $last_order?->id ?? null,
            ]);
        },
            [
                'view_vehicle_status' => 'View vehicle status (Verified, Known, Unknown, Booked, Card payment required, etc.)',
            ]
        );

        $this->get('/vehicles/search', function () {
            global $response;
            $this->requirePermission('search_vehicles');
            /**
             * This endpoint allows searching for vehicles by registration number.
             * This includes:
             * - verified vehicles (in customer_vehicles)
             * - known vehicles (in orders).
             * - booked vehicles (in bookings).
             * - unknown vehicles (not in any of the above).
             */
            self::requireParameters(['search']);
            self::requireType((string)self::getParameter('search'), self::type_string());
            $search = (string)self::getParameter('search');
            self::requireMinLength('search', 1);
            self::requireMaxLength('search', 12);
            $department = null;
            // If department is set, validate it
            if (self::isParametersSet(['department'])) {
                $department = (int)self::getParameter('department');
                self::requireType($department, self::type_int());
                self::requireMinValue($department, 1);
                self::requireMaxValue($department, 999999);
                // Check if the department exists
                $department_o = new departments_o();
                $department_o->select($department);
                if (!$department_o->exists()) {
                    $response->error('Department not found', 404);
                }
                self::requireDepartmentAccess($department);
            }
            // Strip the search term of whitespace
            $search = preg_replace('/\s+/', '', $search);
            /** Get the different lists of registration numbers */
            $options = ['limit' => 10];
            $booked_filters = [...($department ? ['department' => $department] : [])];
            $verified_regs = self::getVerifiedRegs($search, $options);
            $known_regs = self::getKnownRegs($search, $options);
            $booked_regs = self::getBookedRegs($search, $options, $booked_filters);
            $unknown_regs = array_filter(array_unique(array_merge([...self::getUnknownRegs($search, $options), $search]))); // Add the search term as the first item, to ensure it is included in the results
            /**
             * Prevent overlaps between the lists
             * - Booked vehicles should not be in known, unknown or verified
             * - Verified vehicles should not be in known or booked
             * - Known vehicles should not be in unknown
             * - Unknown vehicles should not be in any other list
             */
            $known_regs = array_filter($known_regs, function ($reg) use ($verified_regs, $booked_regs) {
                return !in_array($reg, $verified_regs) && !in_array($reg, $booked_regs);
            });
            $verified_regs = array_filter($verified_regs, function ($reg) use ($booked_regs) {
                return !in_array($reg, $booked_regs);
            });
            $unknown_regs = array_filter($unknown_regs, function ($reg) use ($verified_regs, $known_regs, $booked_regs) {
                return !in_array($reg, $verified_regs) && !in_array($reg, $known_regs) && !in_array($reg, $booked_regs);
            });
            // Sort the lists alphabetically
            sort($verified_regs);
            sort($known_regs);
            sort($booked_regs);
            sort($unknown_regs);
            // Define the custom relevance sorting function
            $relevant = [
                ...array_values(array_unique($booked_regs)),
                ...array_values(array_unique($verified_regs)),
                ...array_values(array_unique($known_regs)),
                ...array_values(array_unique($unknown_regs)),
            ];
            // Limit the results to 10 items total, while keeping the relevance order
            $vehicles = array_slice($relevant, 0, 10);
            /** Format the relevance results */
            $vehicles = array_map(function ($reg) use ($verified_regs, $known_regs, $booked_regs, $unknown_regs) {
                // Determine the status of the vehicle
                $status = null;
                $isVerified = in_array($reg, $verified_regs);
                $isKnown = in_array($reg, $known_regs);
                $isBooked = in_array($reg, $booked_regs);
                $isUnknown = in_array($reg, $unknown_regs);
                // Initialize other variables
                $customer_number = null; // Will be populated for verified / booked vehicles
                $customer_name = null; // Will be populated for verified / booked vehicles
                $last_order_id = (new customer_vehicles_o())->getLastOrderByPlate($reg)?->id ?? null;
                // These variables are only for verified vehicles
                $type = null;
                $id = null;
                $reference = null;
                // Booking related variables
                $booking_id = null;
                $notes = null; // Booking notes

                // Set the status based on priority: booked > verified > known > unknown
                if ($isBooked) {
                    $status = 'booked';
                    /**
                     * // Get the booking
                     * $booking = (new bookings_o())->getFieldsWhere([
                     * 'regNrTraekker' => $reg,
                     * 'status' => 'pending',
                     * ], ['id', 'customer_number', 'reference_number', 'notes']);
                     * // If there is no booking with the tractor reg, check the trailer reg
                     * if (!$booking) {
                     * $booking = (new bookings_o())->getFieldsWhere([
                     * 'regNrTrailer' => $reg,
                     * 'status' => 'pending',
                     * ], ['id', 'customer_number', 'reference_number', 'notes']);
                     * }
                     */
                    // Get the booking
                    $booking = (new order_bookings_o())->getFieldsWhere([
                        'reg_1' => $reg,
                        'order_id' => null,
                        'deleted_at' => null,
                    ], ['id', 'customer_number', 'reference', 'note']);
                    // If there is no booking with the tractor reg, check the trailer reg
                    if (!$booking) {
                        $booking = (new order_bookings_o())->getFieldsWhere([
                            'reg_2' => $reg,
                            'order_id' => null,
                            'deleted_at' => null,
                        ], ['id', 'customer_number', 'reference', 'note']);
                    }
                    // Populate other variables
                    $booking_id = $booking ? (int)$booking[0]['id'] : null;
                    $customer_number = $booking ? (int)$booking[0]['customer_number'] : null;
                    $reference = $booking ? (string)$booking[0]['reference'] : null;
                    $notes = $booking ? (string)$booking[0]['note'] : null;
                    // If the booking has a customer number, check if the customer is barred
                    if ($customer_number && (new users_o())->isCustomerBarred((int)$customer_number)) {
                        $status = 'card';
                    }
                } elseif ($isVerified) {
                    $status = 'verified';
                    // Get the vehicle
                    $customer_vehicles_o = new customer_vehicles_o();
                    $vehicle = $customer_vehicles_o->getFieldsWhere(['reg' => $reg], ['customer_id', 'type', 'id', 'reference']);
                    // Populate other variables
                    $customer_number = $vehicle ? (int)$vehicle[0]['customer_id'] : null;
                    $type = $vehicle ? (int)$vehicle[0]['type'] : null;
                    $id = $vehicle ? (int)$vehicle[0]['id'] : null;
                    $reference = $vehicle ? (string)$vehicle[0]['reference'] : null;
                    // Check if the customer is barred (only if verified)
                    if ($customer_number && (new users_o())->isCustomerBarred((int)$customer_number)) {
                        $status = 'card';
                    }
                } elseif ($isKnown) {
                    $status = 'known';
                    // Check if the vehicle previously only has been set to a specific customer, and if so, get the customer name
                    $orders_o = new orders_o();
                    $order = $orders_o->getFieldsWhere([
                        'reg_1' => $reg,
                        'deleted_at' => null, // Only consider non-deleted orders
                    ], ['customer_id']);
                    $customer_ids = array_unique(array_map(function ($row) {
                        return (int)$row['customer_id'];
                    }, $order));
                    if (count($customer_ids) === 1) {
                        $customer_number = $customer_ids[0];
                        // Check if the customer is barred (only if known)
                        if ($customer_number && (new users_o())->isCustomerBarred((int)$customer_number)) {
                            $status = 'unknown'; // If the customer is barred, we set the status to unknown (as we don't need to know about barred known vehicles)
                            $customer_number = null; // Clear the customer number as well
                        }
                    } else {
                        // Set the customer name to multiple known customers
                        $customer_name = 'Multiple known customers';
                    }
                } elseif ($isUnknown) {
                    $status = 'unknown';
                }
                // Get the customer name if we have a customer number
                if ($customer_number) {
                    $customer = (new users_o())->getUserByCustomerNumber((int)$customer_number);
                    if ($customer->exists()) {
                        $customer_name = (string)$customer->getCustomerName((int)$customer_number);
                    }
                }
                // Return the formatted vehicle
                return [
                    'reg' => $reg,
                    'status' => $status,
                    'type' => $type,
                    'customer_id' => $customer_number,
                    'id' => $id,
                    'last_order_id' => $last_order_id,
                    'reference' => $reference,
                    'booking_id' => $booking_id,
                    'notes' => $notes,
                    'customer_name' => $customer_name,
                    'barred' => $status === 'card', // If the status is 'card', the customer is barred
                ];
            }, $vehicles);
            // Return the results
            $response->success($vehicles);
        },
            [
                'search_vehicles' => 'Search vehicles by registration number',
            ]
        );
    }

    private static function getVerifiedRegs(string $search, array $options = ['limit' => null]): array
    {
        $customer_vehicles_o = new customer_vehicles_o();
        $results = $customer_vehicles_o->getFieldsWhereContaining(
            [
                'reg' => $search
            ],
            ['reg'],
            $options
        );
        return array_map(function ($row) {
            return (string)$row['reg'];
        }, $results);
    }

    private static function getKnownRegs(string $search, array $options = ['limit' => null]): array
    {
        $orders_o = new orders_o();
        $results = $orders_o->getFieldsWhereContaining(
            [
                'reg_1' => $search
            ],
            ['reg_1'],
            $options
        );
        return array_map(function ($row) {
            return (string)$row['reg_1'];
        }, $results);
    }

    private static function getBookedRegs(string $search, array $options = ['limit' => null], ?array $filters = []): array
    {
        $bookings_o = new order_bookings_o();
        /**
         * $bookings_o = new bookings_o();
         *
         * $results1 = $bookings_o->getFieldsWhereContaining(
         * [
         * 'regNrTraekker' => $search,
         * 'status' => 'pending',
         * ],
         * ['regNrTraekker'],
         * $options
         * );
         * $results2 = $bookings_o->getFieldsWhereContaining(
         * [
         * 'regNrTrailer' => $search,
         * 'status' => 'pending',
         * ],
         * ['regNrTrailer'],
         * $options
         * );
         */
        $results1 = $bookings_o->getFieldsWhereContaining(
            [
                'reg_1' => $search,
                'order_id' => null,
                'deleted_at' => null,
                ...$filters
            ],
            ['reg_1'],
            $options
        );
        $results2 = $bookings_o->getFieldsWhereContaining(
            [
                'reg_2' => $search,
                'order_id' => null,
                'deleted_at' => null,
                ...$filters
            ],
            ['reg_2'],
            $options
        );
        return array_unique(array_merge(
            array_map(function ($row) {
                return (string)$row['reg_1'];
            }, $results1),
            array_map(function ($row) {
                return (string)$row['reg_2'];
            }, $results2)
        ));
    }

    private static function getUnknownRegs(string $search, array $options = ['limit' => null]): array
    {
        // Unknown vehicles are vehicles that has been scanned by the LPR system, but are not in any of the other lists
        $plate_scans_o = new plate_scans_o();
        $results = $plate_scans_o->getFieldsWhereContaining(
            [
                'plate' => $search,
            ],
            ['plate'],
            $options
        );
        return array_map(function ($row) {
            return (string)$row['plate'];
        }, $results);
    }

    private function getData(mixed $data, $response)
    {
        if (!isset($data['type'])) {
            $response->error('Type is required', 400);
        }
        if (!isset($data['reg'])) {
            $response->error('Registration number is required', 400);
        }
        if (!isset($data['notes'])) {
            $response->error('Notes is required', 400);
        }
        return $data;
    }

    private function validateRegistrationNumber(mixed $reg, $response): void
    {
        if (!preg_match('/^[A-Z0-9]{4,10}$/', $reg)) {
            $response->error('Invalid registration number, it must be 4-10 characters long, and only contain uppercase letters and numbers', 400);
        }
    }

    private function validateType(mixed $type, $response): void
    {
        // Make sure the type is more than 2 characters
        if (strlen($type) < 2) {
            $response->error('Type is too short, it must be at least 2 characters', 400);
        }
        // Make sure the type is less than 50 characters
        if (strlen($type) > 50) {
            $response->error('Type is too long, it must be less than 50 characters', 400);
        }
    }

    private function validateNotes(mixed $notes, $response): void
    {
        // If the notes are set, make sure they are less than 250 characters
        if (isset($notes) && strlen($notes) > 250) {
            $response->error('Notes are too long, they must be less than 250 characters', 400);
        }
    }
}