<?php

namespace helpers;

use Exception;
use objects\orders_o;
use objects\users_o;
use objects\xlvask_customers_o;
use objects\xlvask_potential_order_matches_o;
use objects\xlvask_usage_logs_o;
use objects\xlvask_vehicles_o;

class xlvask_tasks
{
    public function __construct()
    {
        // Define the error messages if they are not already defined
        /**
         * if (!defined("ERROR_MESSAGE_ECONOMIC_INVOICE_NOT_FOUND")) {
         * define("ERROR_MESSAGE_ECONOMIC_INVOICE_NOT_FOUND", 'ECONOMIC_INVOICE_NOT_FOUND');
         * }
         * if (!defined("ERROR_MESSAGE_ECONOMIC_INVOICE_DRAFT_NOT_FOUND")) {
         * define("ERROR_MESSAGE_ECONOMIC_INVOICE_DRAFT_NOT_FOUND", 'ECONOMIC_INVOICE_DRAFT_NOT_FOUND');
         * }
         */
    }

    /**
     * @throws Exception
     */
    public function runCronTasks(): void
    {
        // Define the XL Vask object
        $xlvask = new \classes\xlvask();
        // Require the module to be enabled
        $xlvask->requireModuleEnabled();
        // Run the synchronization tasks
        if ($xlvask->config->synchronization_enabled->isTrue()) {
            // Synchronize users, this will keep the users (XL Vask customer objects) in cache up to date
            $this->runImportTasks();
            $this->runSyncUsers(true);
            $this->runSyncUsage();
            $this->runSyncVehicles();
            $this->runCleanupTasks();
        };
    }

    /**
     * Run the import tasks
     * This task is used to import the XL Vask vehicles, users, usage logs, and other necessary data into the system.
     * It will import the necessary classes and objects to be used in the system.
     * @return void
     * @throws Exception If the task fails
     */
    public function runImportTasks(): void
    {
        // Define the XL Vask object
        $xlvask = new \classes\xlvask();
        // Require the module to be enabled
        $xlvask->requireModuleEnabled();
        // Run the synchronization tasks
        if ($xlvask->config->synchronization_enabled->isTrue()) {
            // Synchronize users, this will keep the database tables updated:
            // - xlvask_customers
            // - xlvask_vehicles
            // - xlvask_usage_logs
            (new xlvask_customers_o())->importCustomers();
            (new xlvask_vehicles_o())->importVehicles();
            (new xlvask_usage_logs_o())->importUsageLogs();
        };
    }

    /**
     * Run the synchronize users task
     * This task fetches the users from XL Vask, and synchronizes them with this system.
     * This includes:
     * - Fetching the users from XL Vask
     * - Checking if the users exist in this system
     * - Add objects to the cache, when a matching user is found.
     * @return null|array['customers'] The customers fetched from XL Vask, null if synchronization is not enabled
     * @throws Exception If the task fails
     */
    public function runSyncUsers(bool $isSilent = false): array|null
    {
        // Define the XL Vask object
        $xlvask = new \classes\xlvask();
        // Require the module to be enabled
        $xlvask->requireModuleEnabled();
        // Check if synchronization is enabled
        if (!$xlvask->config->synchronization_enabled->isTrue()) {
            if (!$isSilent) {
                throw new Exception('XL Vask synchronization is not enabled.');
            }
            return null; // Synchronization is not enabled, do nothing
        }
        // Get the users from XL Vask
        $customer_objects = $this->fetchCustomers();
        //echo 'Customers fetched from XL Vask: ' . count($customer_objects) . PHP_EOL;
        // Return the customers with an external ID
        $customers = [
            'with_external_id' => [...self::filterCustomersWithExternalId($customer_objects)],
            'without_external_id' => [...self::filterCustomersWithoutExternalId($customer_objects)],
            'all' => $customer_objects,
        ];

        return self::synchronize_customers($customers['with_external_id']);
    }

    /**
     * Get the customers registered in XL Vask
     * This method fetches the customers from XL Vask and returns them as an array of customer objects.
     * @return array[xlvask_customer] An array of customer objects fetched from XL Vask
     * @throws Exception If the task fails
     */
    public function fetchCustomers(): array
    {
        // Define the XL Vask object
        $xlvask = new \classes\xlvask();
        // Require the module to be enabled
        $xlvask->requireModuleEnabled();
        // Get the users from XL Vask
        $customer_objects_raw = $xlvask->getCustomers();
        // Parse the users into customer objects
        return array_map(function ($customer) use ($xlvask) {
            /** @var xlvask_customer $tmp */
            $tmp = new $xlvask->helpers->xlvask_customer();
            foreach ( $customer as $key => $value ) {
                // Assign the properties shared by the xlvask_customer class.
                if (property_exists($tmp, $key)) {
                    $tmp->{$key} = $value;
                }
            }
            return $tmp; // Return the xlvask_customer object
        }, $customer_objects_raw);
    }

    /**
     * Get the customers with an external ID
     * @param array $customers Customers to filter
     * @return array The customers with an external ID
     */
    public static function filterCustomersWithExternalId(array $customers): array
    {
        return array_filter($customers, function ($customer) {
            /** @var xlvask_customer $customer */
            return !empty($customer->externId);
        });
    }

    /**
     * Get the customers without an external ID
     * @param array $customers Customers to filter
     * @return array The customers without an external ID
     */
    public static function filterCustomersWithoutExternalId(array $customers): array
    {
        return array_filter($customers, function ($customer) {
            /** @var xlvask_customer $customer */
            return empty($customer->externId);
        });
    }

    /**
     * Synchronize customers with external IDs
     * This method synchronizes the customers with external IDs from XL Vask.
     * It checks if the customers exist in this system, creates them if they do not exist,
     * and adds/removes the associated external ID to the users in this system.
     * @param array $with_external_id Customers with external IDs to synchronize
     * @return array|null The synchronized customers, null if synchronization is not enabled
     * @throws Exception
     */
    private static function synchronize_customers(array $with_external_id): array|null
    {
        // Define the XL Vask object
        $xlvask = new \classes\xlvask();
        // Require the module to be enabled
        $xlvask->requireModuleEnabled();
        // Check if synchronization is enabled
        if (!$xlvask->config->synchronization_enabled->isTrue()) {
            return null; // Synchronization is not enabled, do nothing
        }

        // Get a list of customer numbers that we need to synchronize
        $customer_numbers = array_map(function ($customer) {
            /** @var xlvask_customer $customer */
            return $customer->externId; // The customer number is stored in the externId property in XL Vask
        }, $with_external_id);

        // Get all the user ids associated with the customer numbers
        $users = (new users_o())->getUsersByCustomerNumbers($customer_numbers, true);

        $cache = $xlvask->getCache();

        /** @var xlvask_customer $customer */
        foreach ( $with_external_id as $customer ) {
            // Check if the customer exists in this system
            if (isset($users[$customer->externId])) {
                // The user exists, update the user with the external ID
                /** @var users_o $user */
                $user = $users[$customer->externId];
                // Cache the customer
                $cache->setCustomerCache((int)$customer->externId, $customer);
            } else {
                //echo 'MISSING USER: ' . $customer->name . ' (' . $customer->externId . ')' . PHP_EOL;
            }
        }
        return $with_external_id; // Return the synchronized customers
    }

    /**
     * Run the sync usage task
     * This task is used to synchronize the usage of the XL Vask module.
     * It fetches the usage data from XL Vask and adds it to the applicable users.
     * The time format XL vask uses is "2025-05-01T00:00:00.000" - this is the ISO 8601 format.
     * @return array|null
     * @throws Exception
     */
    public function runSyncUsage(string $dateFrom = null, string $dateTo = null): array|null
    {
        // TODO: Remove this, this is just for testing purposes
        return null;
        // Define the XL Vask object
        $xlvask = new \classes\xlvask();
        // Require the module to be enabled
        $xlvask->requireModuleEnabled();
        // Check if synchronization is enabled
        if (!$xlvask->config->synchronization_enabled->isTrue()) {
            throw new Exception('XL Vask synchronization is not enabled.');
        }
        // TODO: Remove this, this is just for testing purposes:
        $dateFrom = "2025-06-01 00:00:00";
        $dateTo = "2025-07-01 00:00:00";
        // Set the date from which to fetch the usage data
        // Since this is a cron task, we will fetch the usage data from the last 24 hours
        $minutes = 24 * 60; // 24 hours in minutes
        // TODO: Remove this, this is just for testing purposes:
        $minutes = $minutes * 20;
        // If dateFrom is not set, set it to 24 hours ago
        if (empty($dateFrom)) {
            $dateFrom = date('Y-m-d\TH:i:s.000', strtotime('-' . $minutes . ' minutes'));
        } else {
            // If dateFrom is set, make sure it is in the correct format
            $dateFrom = date('Y-m-d\TH:i:s.000', strtotime($dateFrom));
        }
        // If dateTo is not set, set it to now
        if (empty($dateTo)) {
            $dateTo = date('Y-m-d\TH:i:s.000'); // Current time in ISO 8601 format
        } else {
            // If dateTo is set, make sure it is in the correct format
            $dateTo = date('Y-m-d\TH:i:s.000', strtotime($dateTo));
        }
        echo 'Fetching usage data from: ' . $dateFrom . ' to: ' . $dateTo . PHP_EOL;
        //echo $dateFrom; (E.g. 2025-06-11T12:13:16.000)


        // If there are no customers, get them. TODO: Remove this in production, this is just for testing
        $xlvask->getTasks()->runSyncUsers(true);

        // Get the cached customers
        $customers = $xlvask->getCache()->getAllCachedCustomers();

        // Get all the usage logs from XL Vask
        $debug_usage_logs = $xlvask->getUsageLog(
            $dateFrom,
            null, // regNr
            null, // vehicleId
            null // customerId
        );

        $debug_usage_logs = self::formatUsageLogs($debug_usage_logs);

        // Filter out the logs that are after the dateTo
        $debug_usage_logs = array_filter($debug_usage_logs, function ($log) use ($dateTo) {
            /** @var xlvask_usage_log $log */
            return strtotime($log->getFormattedDate()) <= strtotime($dateTo);
        });
        echo 'Found ' . count($debug_usage_logs) . ' usage logs in the date range from ' . $dateFrom . ' to ' . $dateTo . PHP_EOL;
        echo "Prepaid: " . count(array_filter($debug_usage_logs, function ($log) {
                /** @var xlvask_usage_log $log */
                return $log->isPrepaid();
            })) . PHP_EOL;
        echo "Finished: " . count(array_filter($debug_usage_logs, function ($log) {
                /** @var xlvask_usage_log $log */
                return $log->isCompleted();
            })) . PHP_EOL;
        echo 'Not finished (skipped): ' . count(array_filter($debug_usage_logs, function ($log) {
                /** @var xlvask_usage_log $log */
                return !$log->isCompleted();
            })) . PHP_EOL;
        echo 'Billed to default customer (skipped): ' . count(array_filter($debug_usage_logs, function ($log) {
                /** @var xlvask_usage_log $log */
                return $log->hasDefaultCustomer();
            })) . PHP_EOL;
        echo 'Unique customers: ' . count(array_unique(array_map(function ($log) {
                /** @var xlvask_usage_log $log */
                return $log->CustomerId;
            }, $debug_usage_logs))) . PHP_EOL;
        $linked_orders = (new orders_o())->getFieldsWhere([
            'wash_id' => array_map(function ($log) {
                /** @var xlvask_usage_log $log */
                return $log->WashId;
            }, $debug_usage_logs),
            'deleted_at' => null,
        ], [
            'id',
            'wash_id',
        ]);
        echo "Linked to orders: " . count($linked_orders) . PHP_EOL;
        $eligible_for_automatic_continuance = array_filter($debug_usage_logs, function ($log) {
            /** @var xlvask_usage_log $log */
            return $log->isEligibleForAutomaticContinuance(true);
        });
        $eligible_for_automatic_continuance_without_prepaid = array_filter($debug_usage_logs, function ($log) {
            /** @var xlvask_usage_log $log */
            return $log->isEligibleForAutomaticContinuance(false);
        });
        echo "Eligible for automatic continuance: " . count($eligible_for_automatic_continuance) . " (" . count($eligible_for_automatic_continuance_without_prepaid) . " without prepaid)" . PHP_EOL;
        // Print a list of customers that do not have an external ID
        $customers_without_external_id = array_filter($debug_usage_logs, function ($log) {
            /** @var xlvask_usage_log $log */
            return !$log->hasExternalId() && !$log->hasDefaultCustomer();
        });
        echo "Without billable customer: " . count(array_filter($debug_usage_logs, function ($log) {
                /** @var xlvask_usage_log $log */
                return !$log->hasBillableCustomer();
            })) . " (" . count(array_filter($debug_usage_logs, function ($log) {
                /** @var xlvask_usage_log $log */
                return !$log->hasBillableCustomer() && !$log->hasDefaultCustomer() && !$log->hasExternalId();
            })) . " without external ID)" . PHP_EOL;

        foreach ( self::sortLogsByDate($customers_without_external_id) as $customer ) {
            /** @var xlvask_usage_log $customer */
            echo ' - ' . $customer->getFormattedDate() . ' - ' . $customer->Customer . ' - ' . $customer->getDepartment()->name->value() . ', ' . $customer->getLane() . ' - ' . $customer->getTotalPrice() . ' DKK' . ' ( ' . ($customer->isCompleted() ? 'Finished' : 'Not finished') . ', ' . ($customer->isPrepaid() ? 'Prepaid' : 'Not prepaid') . ' )' . PHP_EOL;
        }

        // Get all unique external IDs from the customers
        $unique_external_customer_ids = array_unique(array_map(function ($customer) {
            /** @var xlvask_usage_log $customer */
            return $customer->CustomerId;
        }, $debug_usage_logs));
        // Remove the external IDs that are not numeric or empty
        $unique_external_customer_ids = array_filter($unique_external_customer_ids, function ($id) {
            return !empty($id) && is_numeric($id);
        });
        echo "Unique external customer IDs: " . count($unique_external_customer_ids) . PHP_EOL;
        foreach ( $unique_external_customer_ids as $customer_id ) {
            $doesCustomerExistInArray = array_filter($customers, function ($customer) use ($customer_id) {
                /** @var xlvask_customer $customer */
                return $customer->externId === $customer_id;
            });
            $tmp_does_customer_exist = count($doesCustomerExistInArray) > 0;
            if (!$tmp_does_customer_exist) {
                echo ' - ' . $customer_id . ' does not exist in the system (skipped)' . PHP_EOL;
                continue; // Skip customers that do not exist in the system
            }
            $tmp_customer_object = reset($doesCustomerExistInArray); // Get the first customer object that matches the external ID
            /** @var xlvask_customer $tmp_customer_object */
            echo ' - ' . $tmp_customer_object->name . ' (' . $tmp_customer_object->externId . ') - ' . $tmp_customer_object->customerId . PHP_EOL;
            echo " - # Washes: " . count(array_filter($debug_usage_logs, function ($log) use ($tmp_customer_object) {
                    /** @var xlvask_usage_log $log */
                    return $log->CustomerId === $tmp_customer_object->externId;
                })) . PHP_EOL;
            // Echo the eligible for automatic continuance logs
            foreach ( self::sortLogsByDate(array_filter($debug_usage_logs, function ($log) use ($tmp_customer_object) {
                /** @var xlvask_usage_log $log */
                return $log->CustomerId === $tmp_customer_object->externId && $log->isEligibleForAutomaticContinuance(false);
            })) as $log ) {
                /** @var xlvask_usage_log $log */
                $tmp_linked_to_order = array_filter($linked_orders, function ($order) use ($log) {
                    /** @var array $order */
                    return $order['wash_id'] === $log->WashId;
                });
                echo ' - - ' . $log->getFormattedDate() . ' - ' . $log->getDepartment()->name->value() . ', ' . $log->getLane() . ' - ' . $log->getTotalPrice() . ' DKK' . ' ( ' . ($log->isCompleted() ? 'Finished' : 'Not finished') . ', ' . ($log->isPrepaid() ? 'Prepaid' : 'Not prepaid') . ', ' . ($tmp_linked_to_order ? 'Linked to order' : 'Not linked to order') . ' )' . PHP_EOL;
            }
            // Echo the ineligible for automatic continuance logs
            echo " - # Not eligible for automatic continuance: " . count(array_filter($debug_usage_logs, function ($log) use ($tmp_customer_object) {
                    /** @var xlvask_usage_log $log */
                    return $log->CustomerId === $tmp_customer_object->externId && !$log->isEligibleForAutomaticContinuance(false);
                })) . PHP_EOL;
            foreach ( self::sortLogsByDate(array_filter($debug_usage_logs, function ($log) use ($tmp_customer_object) {
                /** @var xlvask_usage_log $log */
                return $log->CustomerId === $tmp_customer_object->externId && !$log->isEligibleForAutomaticContinuance(false);
            })) as $log ) {
                /** @var xlvask_usage_log $log */
                echo ' - - ' . $log->getFormattedDate() . ' - ' . $log->getDepartment()->name->value() . ', ' . $log->getLane() . ' - ' . $log->getTotalPrice() . ' DKK' . ' ( ' . ($log->isCompleted() ? 'Finished' : 'Not finished') . ', ' . ($log->isPrepaid() ? 'Prepaid' : 'Not prepaid') . ' )' . PHP_EOL;
            }
        }
        echo 'Customers:';
        print_r($customers);
        echo 'Eligible for automatic continuance:';
        print_r($eligible_for_automatic_continuance);
        exit;


        // Loop through the customers and check if there's any new usage data
        foreach ( $customers as $customer ) {
            /** @var xlvask_customer $customer */
            // Check if the customer has an externId
            if (empty($customer->externId)) {
                continue; // Skip customers without an externId
            }
            //echo 'Checking customer: ' . $customer->name . ' (' . $customer->externId . ')' . PHP_EOL;
            $tmp_usage_logs = self::formatUsageLogs($xlvask->getUsageLog(
                $dateFrom,
                null, // regNr
                null, // vehicleId
                $customer->customerId // customerId
            ));
            foreach ( $tmp_usage_logs as $log ) {
                /** @var xlvask_usage_log $log */
                // Check if the wash was completed
                if (!$log->isCompleted()) {
                    //echo 'A log is not completed: ' . $log->getFormattedDate() . ' - ' . $log->CustomerId . ' - ' . $log->VehicleId . PHP_EOL;
                    continue; // Skip logs that are not completed
                }

//                echo PHP_EOL;
//                echo '### ' . $log->getFormattedDate() . ' - ' . $customer->name . ' (' . $customer->externId . ') - ' . $log->getTotalPrice() . PHP_EOL;
//                echo '# License Plate: ' . $log->RegistrationNumber . PHP_EOL;
//                echo '# Vehicle ID: ' . $log->VehicleId . PHP_EOL;
//                echo '# Wash ID: ' . $log->WashId . PHP_EOL;
//                echo '# Hall: ' . $log->Hall . PHP_EOL;
//                echo '# Hall ID: ' . $log->HallId . PHP_EOL;
//                echo '# Department: ' . $log->getDepartment()->id . ' ( ' . $log->getDepartment()->name->value() . ' )' . PHP_EOL;
//                echo '# Track / Lane: ' . $log->getLane() . PHP_EOL;
//                echo '# Linked to order: ' . ($log->isLinkedToOrder() ? 'Yes' : 'No') . PHP_EOL;
//                echo '# Items: ' . PHP_EOL;
                $tmp_skipped_items = [];
                foreach ( $log->WashItems as $item ) {
                    /** @var xlvask_wash_item $item */
                    // Skip items that have the id 64, since these are not relevant to anyone.
                    $tmp_item_id = $item->getProduct($log)->id;
                    if ($tmp_item_id === 64 || !$item->isCountAboveZero()) {
                        $tmp_skipped_items[] = $item;
                        continue; // Skip items with id 64 (Or items simply not relevant)
                    }
                    //echo '## ' . $item->Count . ' x ' . $item->OriginalProductName . ' @ ' . $item->getPriceExVat() . ' as ' . $tmp_item_id . ' ( ' . $item->getProduct($log)->name->value() . ' )' . PHP_EOL;
                }
                //echo '### Total: ' . $log->getTotalPrice() . PHP_EOL;
                //echo '### Skipped items: ' . count($tmp_skipped_items) . PHP_EOL;
                if (count($tmp_skipped_items) > 0) {
                    //echo '### Skipped items details: ' . PHP_EOL;
                    foreach ( $tmp_skipped_items as $skipped_item ) {
                        /** @var xlvask_wash_item $skipped_item */
                        //echo '## ' . $skipped_item->Count . ' x ' . $skipped_item->OriginalProductName . ' @ ' . $skipped_item->getPriceExVat() . ' as ' . $skipped_item->getProduct($log)->id . ' ( ' . $skipped_item->getProduct($log)->name->value() . ' )' . PHP_EOL;
                    }
                }
                //echo '#' . PHP_EOL;
                if (!$log->isLinkedToOrder()) {
                    //echo '# Checking for orders that might be addressing this wash.' . PHP_EOL;
                    // Check if an order that matches this wash exists.
                    if ($log->getPotentialOrder() !== null) {
                        $xlvask_potential_order_matches_o = new xlvask_potential_order_matches_o();
                        // Check if the potential order match is already in the database (Prevent duplicates)
                        if (!$xlvask_potential_order_matches_o->doesWashPotentialOrderMatchExist(
                            $log->WashId,
                        )) {
                            // There's no match in the database, so we will add it.
                            //echo '# Adding potential order match for wash: ' . $log->WashId . ' - Order: ' . $log->getPotentialOrder()->id . ' - Customer: ' . $customer->customerId . ' - Customer Number: ' . (int)$customer->getUser()->customer_number->value() . ' - Department: ' . $log->getDepartment()->id . PHP_EOL;
                            $xlvask_potential_order_matches_o->add(
                                (string)$log->WashId,
                                (int)$log->getPotentialOrder()->id,
                                (string)$customer->customerId,
                                (int)$customer->getUser()->customer_number->value(),
                                (int)$log->getDepartment()->id,
                            );
                        }
                        //echo '# This wash might be associated with an order: ' . $log->getPotentialOrder()->id . PHP_EOL;
                    } else {
                        // If no order is found, we will generate a new order.
                        //echo '# No potential order found for this wash.' . PHP_EOL;
                        // Verify the customer object
                        if (!$customer instanceof xlvask_customer) {
                            //echo '# The customer object is not an instance of xlvask_customer.' . PHP_EOL;
                            continue; // Skip this wash
                        }
                        if (!$tmp_order = $this->createOrderFromWash($log, $customer)) {
                            //echo '# Failed to create an order from this wash.' . PHP_EOL;
                        } else {
                            //echo '# Order created successfully.' . PHP_EOL;
                            //echo '# Order ID: ' . $tmp_order->id . PHP_EOL;
                            //echo '# Order total: ' . $tmp_order->getNetAmount() . PHP_EOL;
                        }
                    }
                    //echo '# This wash is not linked to an order, generating a new order.' . PHP_EOL;
                }
                //echo '# ------------------------' . PHP_EOL;
            }
            //print_r($tmp_usage_logs);
            // Check if the customer has any usage data in the last 24 hours
            // This is a placeholder, you might want to implement a method to check this
            // if (!$xlvask->hasRecentUsageData($customer->externId, $dateFrom)) {
            //     continue; // Skip customers without recent usage data
            // }
        }

        // Process the usage data and update the users accordingly
        //$xlvask->processUsageData($usage_data);
        return [];
    }

    private static function formatUsageLogs(array $getUsageLog): array
    {
        //print_r($getUsageLog);
        // Format the usage logs to a more readable format
        // This is a placeholder, you might want to implement a method to format the logs
        return array_map(/**
         * @throws Exception
         */ function ($log) {
            $xlvask = new \classes\xlvask();
            return (new $xlvask->helpers->xlvask_usage_log())->setProperties($log);
        }, $getUsageLog);
    }

    private static function sortLogsByDate(array $logs): array
    {
        // Sort the logs by date
        usort($logs, function ($a, $b) {
            /** @var xlvask_usage_log $a */
            /** @var xlvask_usage_log $b */
            return strtotime($a->getFormattedDate()) - strtotime($b->getFormattedDate());
        });
        return $logs; // Return the sorted logs
    }

    /**
     * @throws Exception If the log is not completed, or if the customer does not have an externId, or if the customer does not exist in this system.
     */
    public function createOrderFromWash(xlvask_usage_log $log, xlvask_customer|\stdClass $customer): ?orders_o
    {
        // Make sure the XL Vask module is enabled
        $xlvask = new \classes\xlvask();
        $xlvask->requireModuleEnabled();

        // Check if the log is completed
        if (!$log->isCompleted()) {
            throw new Exception('The log ( ID: ' . $log->WashId . ' ) is not completed, cannot create an order from it.');
        }
        // Check if the customer has an externId
        if (empty($customer->externId)) {
            throw new Exception('The customer ( ID: ' . $customer->customerId . ' ) does not have an externId, cannot create an order from it.');
        }
        // Check if the customer exists in this system
        if (!$tmp_user = $customer->getUser()) {
            throw new Exception('The customer ( ID: ' . $customer->customerId . ' ) does not exist in this system, cannot create an order from it.');
        }
        // Create a new order object
        $order = new orders_o();
        $order->addXLVaskOrder(
            $tmp_user,
            $log,
        );
        // Return the order object
        return $order;
    }

    /**
     * Synchronize vehicles with XL Vask
     * This method is used to synchronize the vehicles with XL Vask.
     * It fetches the vehicles from XL Vask and adds them to the cache.
     * This includes:
     * - Fetching the vehicles from XL Vask
     * - Caching the vehicles in this system
     * @param bool $isSilent If true, will not throw an exception if synchronization is not enabled
     * @return null|array
     * @throws Exception If the task fails or if synchronization is not enabled
     * @see xlvask_vehicle
     * @see xlvask_cache::getVehicleCache()
     * @see xlvask_cache::setVehicleCache()
     * @see xlvask_cache::isVehicleCached()
     */
    public function runSyncVehicles(bool $isSilent = false): ?array
    {
        // Define the XL Vask object
        $xlvask = new \classes\xlvask();
        // Require the module to be enabled
        $xlvask->requireModuleEnabled();
        // Check if synchronization is enabled
        if (!$xlvask->config->synchronization_enabled->isTrue()) {
            if (!$isSilent) {
                throw new Exception('XL Vask synchronization is not enabled.');
            }
            return null; // Synchronization is not enabled, do nothing
        }
        // Get the vehicles from XL Vask
        /** @var xlvask_vehicles $vehicles */
        $vehicles = new $xlvask->helpers->xlvask_vehicles();
        $vehicles = $vehicles->getVehicles();
        // Cache the vehicles
        foreach ( $vehicles as $vehicle ) {
            /** @var xlvask_vehicle $vehicle */
            $xlvask->getCache()->setVehicleCache($vehicle->registrationNumber, $vehicle);
        }
        return $vehicles; // Return the vehicles
    }

    /**
     * Run the cleanup tasks
     * This task is used to clean up the XL Vask module.
     * @return void
     * @throws Exception If the task fails
     */
    public function runCleanupTasks(): void
    {
        // Define the XL Vask object
        $xlvask = new \classes\xlvask();
        // Require the module to be enabled
        $xlvask->requireModuleEnabled();
        // Run
    }
}