<?php

namespace routes;

use attachments\helpers\attachment_content;
use classes\attachment_store;
use classes\attachments;
use classes\authentication;
use classes\response;
use classes\stripe;
use JetBrains\PhpStorm\NoReturn;
use objects\collected_order_invoices_o;
use objects\departments_o;
use objects\economic_module_orders;
use objects\logs_o;
use objects\orders_o;
use objects\stripe_module_orders_o;
use objects\stripe_payment_intents_o;
use objects\users_o;
use traits\route_t;
use modules\subusers\helpers\subusers_permission_node_key;

class ordersRoute
{
    use route_t;

    public function run(): void
    {
        $this->get('/orders', function () {
            // Require the user to be logged in
            global $response;
            /** Authentication */
            $auth = new authentication();
            $user = $auth->get_user();
            if ($user === false) {
                (new logs_o())->add('orders', 'global', 1, 0, 'LIST_ORDERS', 'No user found, or invalid session');
                $response->error('Invalid session', 400);
            }
            /** Permissions (subuser-aware) */
            $permission_own = self::definePermission('list_own_orders', subusers_permission_node_key::ORDERS_LIST);
            $permission_other = self::definePermission('list_orders');
            $has_permission_other = self::hasPermission($permission_other);
            self::allowOwnOrDepartmentAccess(
                $permission_own,
                $permission_other,
                null,
                null,
                null,
                'You do not have permission to list orders.'
            );

            // Log the incident
            (new logs_o())->add('orders', 'global', 1, $user->id, 'LIST_ORDERS', 'Successfully listed orders');

            // Build department filter when listing as department/admin
            $department_ids = [];
            if ($has_permission_other) {
                $department_ids = $user->getGroup()->getDepartments();
            }
            if (self::isParametersSet(['show_wash_subscription'])) {
                if (self::getParameter('show_wash_subscription') === 'true') {
                    $department_ids[] = '10';
                }
            }

            $orders = new orders_o();
            $orders->setView('orders_with_invoice_collections');
            $effectiveCustomer = self::resolveEffectiveCustomerNumber();

            $response->success(
                $orders->listObjectsWithPaginationIfSet(
                    function ($order) {
                        $order_obj = new orders_o();
                        // Get the order object
                        $order_obj->select((int)$order['id']);
                        // Add the invoice status to the order
                        $order['economic_invoice_module'] = (new economic_module_orders())->getByOrderId($order['id'])->asArray();
                        // Add the total amount to the order
                        $order['total_net_amount'] = $order_obj->getNetAmount();
                        // Add the stripe status to the order
                        $stripe_module_orders = (new stripe_module_orders_o())->select($order['id']);
                        if ($stripe_module_orders->exists()) {
                            $order['stripe_invoice_module'] = $stripe_module_orders->asArray();
                        }
                        // If the invoice collection is set, add it to the order
                        if (!empty($order['invoice_collection_id'])) {
                            $collected_order_invoices_obj = new collected_order_invoices_o();
                            $collected_order_invoices_obj->select((int)$order['invoice_collection_id']);
                            $order['invoice_collection'] = [
                                'id' => $order['invoice_collection_id'],
                                'closed_at' => $collected_order_invoices_obj->closed_at->value(),
                                'booked_invoice_id' => $collected_order_invoices_obj->booked_invoice_id->value() ?? null,
                                'processor' => (int)$collected_order_invoices_obj->processor->value() ?? null,
                            ];
                        }
                        // Get the customer
                        $tmp_customer = (new users_o())->getUserByCustomerNumber((int)$order['customer_id']);
                        // Add the customer name to the order
                        $order['customer_name'] = (new users_o())->getCustomerName((int)$tmp_customer->customer_number->value());
                        $order['user_id'] = (int)$tmp_customer->id;
                        // Add the cashier name to the order
                        $order['cashier_name'] = (new users_o())->getCashierName((int)$order['cashier_id']);
                        $order['pending_handheld'] = $order_obj->isPendingHandheld();
                        $order['attachments'] = $order_obj->listAttachments();
                        $order['po'] = $order['po'] ?? null;
                        $order['lane'] = $order['lane'] ?? null;
                        /** @var array $order */
                        return $order;
                    },
                    $orders->forceRestrictFilters([
                        ...($has_permission_other ? [
                            'department_id' => $department_ids
                        ] : []),
                        ...(!$has_permission_other && $effectiveCustomer !== null ? [
                            'customer_id' => [(int)$effectiveCustomer]
                        ] : []),
                    ])
                )
            );
        },
            [
                'list_orders' => 'List all orders',
                'list_own_orders' => 'List own orders. Subusers require node: ORDERS_LIST and X-Customer-Number header.',
            ]
        );


        $this->post('/orders', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('add_order');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the post data
                $data = json_decode(file_get_contents('php://input'), true);
                if (!is_array($data)) {
                    $data = [];
                }
                // Check if the required fields are set
                $data = $this->getData($data, $response);
                // This is used to determine if the order is created from a handheld device,
                // If it is, we will add an indicator "pending" to the order, in the cache.
                // This will automatically be removed at midnight, or when the order is completed.
                if (isset($data['is_handheld']) && !is_bool($data['is_handheld'])) {
                    $response->error('is_handheld must be a boolean', 400);
                }
                if (isset($data['is_handheld'])) {
                    $isHandHeld = (bool)$data['is_handheld'];
                    unset($data['is_handheld']);
                } else {
                    $isHandHeld = false;
                }
                // Validate the department
                if (!(new departments_o())->getDepartmentById((int)$data['department_id'])) {
                    $response->error('Department not found', 400);
                }
                // Make sure the customer number set is valid
                $targetUser = (new users_o())->getUserByCustomerNumber((int)$data['customer_id']);
                if (!$targetUser->exists()) {
                    $response->error('Customer not found', 400);
                }

                // Check if the user requires a reference
                if ($targetUser->requiresReference() && empty($data['reference'])) {
                    $response->error('Reference is required by the customer', 400);
                }
                // Get the registration number
                $reg_1 = $data['reg_1'];
                // Get the registration numbers (If they are set, they 2-3 are optional)
                $reg_2 = $data['reg_2'] ?? '';
                $reg_3 = $data['reg_3'] ?? '';
                // Strip the registration numbers of any whitespace
                $reg_1 = preg_replace('/\s+/', '', $reg_1);
                $reg_2 = preg_replace('/\s+/', '', $reg_2);
                $reg_3 = preg_replace('/\s+/', '', $reg_3);
                $new_data = [
                    'customer_id' => (int)$data['customer_id'],
                    'department_id' => (int)$data['department_id'],
                    'reference' => (string)$data['reference'] ?? '',
                    'cashier_id' => (int)$user->id, // The user who created the order
                    'notes' => (string)$data['notes'] ?? '',
                    'reg_1' => (string)$reg_1,
                    'reg_2' => (string)$reg_2,
                    'reg_3' => (string)$reg_3,
                    ...(!empty($data['lane']) ? ['lane' => (int)$data['lane']] : []), // Optional lane
                    ...(!empty($data['wash_id']) ? ['wash_id' => (string)$data['wash_id']] : []), // Optional wash ID
                    ...(!empty($data['booking_id']) ? ['booking_id' => (int)$data['booking_id']] : []), // Optional booking ID 
                    'created_at' => (string)($data['created_at'] ?? date('Y-m-d H:i:s')), // Default to current time if not set
                ];
                // Create the order
                //$order = (new orders_o())->add((int)$data['customer_id'], $user->id, $data['reference'], $data['notes'], (int)$data['department_id'], (string)$reg_1, (string)$reg_2, (string)$reg_3);
                $order = (new orders_o())->addArray($new_data);
                // If the order is created from a handheld device, we will add an indicator "pending" to the order, in the cache.
                if ($isHandHeld) {
                    $order->setPendingHandheldIndicator();
                }
                // Log the incident
                (new logs_o())->add('orders', $data['department_id'], 1, $user->id, 'ADD_ORDER', 'Successfully added an order (ID: ' . $data['department_id'] . ')');
                // Return a success message, containing the orders array
                $response->success($order->asArray());
            } else { 
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'ADD_ORDER', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'add_order' => 'Add an order'
            ]
        );

        $this->put('/order', function () {
            self::updateOrder();
        },
            [
                'edit_order' => 'Edit an order'
            ]
        );

        $this->put('/orders', function () {
            self::updateOrder();
        },
            [
                'edit_order' => 'Edit an order'
            ]
        );

        $this->delete('/orders', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('delete_order');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the payload
                $id = $this->fromRequest('id');
                // Check if the ID is set
                if (!$id) {
                    $response->error('ID is required', 400);
                }
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$id);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // Check if the user has access to the department
                self::requireDepartmentAccess((int)$order->department_id->value());
                // Delete the order
                $order->delete();
                // Log the incident
                (new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'DELETE_ORDER', 'Successfully deleted an order (ID: ' . $id . ')');
                // Return a success message
                $response->success(['message' => 'Order deleted successfully']);
            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'DELETE_ORDER', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'delete_order' => 'Delete an order'
            ]
        );

        $this->get('/orders/attachments/download', function () {
            // Require the user to be logged in
            global $response;
            // Permissions (subuser-aware)
            $permission_own = self::definePermission('download_order_attachments_own', subusers_permission_node_key::ORDERS_LIST);
            $permission_other = self::definePermission('download_order_attachments');
            $has_permission_other = self::hasPermission($permission_other);
            if (!$has_permission_other) {
                self::requirePermission($permission_own);
            }
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the order ID and attachment ID from the request
                self::requireParameters([
                    'order_id',
                    'attachment_id'
                ]);
                $order_id = self::getParameter('order_id');
                $attachment_id = self::getParameter('attachment_id');
                if (!is_numeric($order_id) || (int)$order_id < 1) {
                    $response->error('Invalid order ID', 400);
                }
                if (!is_numeric($attachment_id) || (int)$attachment_id < 1) {
                    $response->error('Invalid attachment ID', 400);
                }
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$order_id);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // If operating under own-scope (classic user or subuser), ensure the order belongs to the effective customer context
                if (!$has_permission_other) {
                    $effectiveCustomer = self::resolveEffectiveCustomerNumber();
                    if ($effectiveCustomer === null || (int)$order->customer_id->value() !== (int)$effectiveCustomer) {
                        $response->error('You do not have permission to download this order attachment', 403);
                    }
                }
                // Get the attachment
                $attachment = $order->getAttachment((int)$attachment_id);
                if (!$attachment->exists()) {
                    $response->error('Attachment not found', 400);
                }
                // Create a download link
                $attachment_store = new attachment_store();
                $attachments = new attachments();
                $attachment_formatted = $attachments->format($attachment);
                $download_link = $attachment_store->generateDirectDownloadUrl(
                    $attachment_formatted->content->document
                );
                // Log the incident
                (new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'DOWNLOAD_ORDER_ATTACHMENT', 'Successfully downloaded an attachment for an order (Order ID: ' . $order_id . ', Attachment ID: ' . $attachment_id . ')');
                // Return the download link
                $response->success(['download_link' => $download_link]);

            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'DOWNLOAD_ORDER_ATTACHMENT', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'download_order_attachments' => 'Download attachments for an order',
                'download_order_attachments_own' => 'Download attachments for an order (Only for own orders). Subusers require node: ORDERS_LIST and X-Customer-Number header.'
            ]
        );

        $this->get('/orders/attachments', function () {
            // Require the user to be logged in
            global $response;
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the order ID from the request
                self::requireParameters([
                    'id'
                ]);
                $order_id = self::getParameter('id');
                if (!is_numeric($order_id) || (int)$order_id < 1) {
                    $response->error('Invalid order ID', 400);
                }
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$order_id);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // Permissions (subuser-aware)
                $permission_own = self::definePermission('list_own_order_attachments', subusers_permission_node_key::ORDERS_LIST);
                $permission_other = self::definePermission('list_order_attachments');
                $has_permission_other = self::hasPermission($permission_other);
                if (!$has_permission_other) {
                    self::requirePermission($permission_own);
                    $effectiveCustomer = self::resolveEffectiveCustomerNumber();
                    if ($effectiveCustomer === null || (int)$order->customer_id->value() !== (int)$effectiveCustomer) {
                        $response->error('You do not have permission to view attachments for this order.', 403);
                    }
                }
                // Get the attachments
                $attachments = $order->listAttachments();
                // Log the incident
                (new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'LIST_ORDER_ATTACHMENTS', 'Successfully listed attachments for an order (ID: ' . $order_id . ')');
                // Return the attachments
                $response->success($attachments);
            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'LIST_ORDER_ATTACHMENTS', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'list_order_attachments' => 'List attachments for an order',
                'list_own_order_attachments' => 'List attachments for an order (Only for own orders). Subusers require node: ORDERS_LIST and X-Customer-Number header.'
            ]
        );

        $this->post('/orders/attachments/upload', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('add_order_attachments');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Check if the order ID and file are set
                $this->requireParameters(['order_id', 'base64_file', 'file_name']);
                $order_id = (int)$this->getParameter('order_id');
                if (!is_numeric($order_id) || (int)$order_id < 1) {
                    $response->error('Invalid order ID', 400);
                }
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$order_id);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // Get the base64 file
                $base64_file = (string)$this->getParameter('base64_file');
                $attachment_store = new attachment_store();
                // Determine the file extension from the file name
                $file_name = (string)$this->getParameter('file_name');
                $extension = pathinfo($file_name, PATHINFO_EXTENSION);
                $object_name = $attachment_store->storeTempFileFromBase64(
                    $base64_file,
                    $extension
                );
                if ($object_name === false) {
                    $response->error('Failed to store the attachment file.', 500);
                }
                $object_attachment = $order->addAttachment((new attachment_content())->setDocument($object_name)->setOther((string)self::getParameter('file_name')));
                // Log the incident
                (new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'ADD_ORDER_ATTACHMENT', 'Successfully added an attachment for an order (Order ID: ' . $order_id . ')');
                // Return a success message
                $attachments = new attachments();
                $response->success($attachments->format($attachments->get($object_attachment->id)));
            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'ADD_ORDER_ATTACHMENT', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'add_order_attachments' => 'Add attachments for an order'
            ]
        );

        $this->delete('/orders/attachments', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('delete_order_attachments');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the order ID and attachment ID from the request
                self::requireParameters([
                    'order_id',
                    'attachment_id'
                ]);
                $order_id = self::getParameter('order_id');
                $attachment_id = self::getParameter('attachment_id');
                if (!is_numeric($order_id) || (int)$order_id < 1) {
                    $response->error('Invalid order ID', 400);
                }
                if (!is_numeric($attachment_id) || (int)$attachment_id < 1) {
                    $response->error('Invalid attachment ID', 400);
                }
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$order_id);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // Delete the attachment
                $order->removeAttachment((int)$attachment_id);
                // Log the incident
                (new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'DELETE_ORDER_ATTACHMENT', 'Successfully deleted an attachment for an order (Order ID: ' . $order_id . ', Attachment ID: ' . $attachment_id . ')');
                // Return a success message
                $response->success(['message' => 'Attachment deleted successfully']);
            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'DELETE_ORDER_ATTACHMENT', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'delete_order_attachments' => 'Delete attachments for an order'
            ]
        );

        $this->post('/orders/mark_as_completed', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('mark_order_as_completed');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the post data
                $data = json_decode(file_get_contents('php://input'), true);
                // Check if the required fields are set
                if (!isset($data['id'])) {
                    $response->error('ID is required', 400);
                }
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$data['id']);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // Mark the order as completed
                $order->markAsCompleted();
                // Log the incident
                (new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'MARK_ORDER_AS_COMPLETED', 'Successfully marked an order as completed (ID: ' . $data['id'] . ')');
                // Return a success message
                $response->success(['message' => 'Order marked as completed successfully']);
            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'MARK_ORDER_AS_COMPLETED', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'mark_order_as_completed' => 'Mark an order as completed'
            ]
        );

        $this->post('/orders/module/stripe/payment_intent', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('charge_order');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the post data
                $data = json_decode(file_get_contents('php://input'), true);
                // Check if the required fields are set
                if (!isset($data['id'])) {
                    $response->error('ID is required', 400);
                }
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$data['id']);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // Get the department
                $department = (new departments_o())->selectId((int)$order->department_id->value());
                // Check if the department is configured for Stripe payments
                if (!$department->isStripeConfigured()) {
                    $response->error('Department is not configured for Stripe payments', 400);
                }
                // Check if the reader is set
                if (!isset($data['reader'])) {
                    $response->error('Reader ID is required', 400);
                }
                // Get the tax percentage (if any)
                $tax_percentage = (isset($data['tax_percentage'])) ? (int)$data['tax_percentage'] : null;
                // Check if the tax percentage is valid
                if ($tax_percentage !== null && ($tax_percentage < 0 || $tax_percentage > 100)) {
                    $response->error('Invalid tax percentage', 400);
                }
                function addTaxNetAmount($net_amount, $tax_percentage): float
                {
                    // Check if the tax percentage is above 0
                    if (empty($tax_percentage) || $tax_percentage <= 0) {
                        return $net_amount;
                    }
                    return $net_amount + ($net_amount * ($tax_percentage / 100));
                }

                // Get the Stripe payment intent
                $stripe = new stripe();
                $paymentIntent = $stripe->payment_intents->create(
                    addTaxNetAmount(
                        (float)$order->getNetAmount() * 100,
                        $tax_percentage ?? 0
                    ),
                    [
                        'description' => 'Order ID: ' . $order->id,
                        'metadata' => [
                            'order_id' => $order->id,
                            'customer_id' => $order->customer_id->value(),
                            'department_id' => $order->department_id->value(),
                            'tax_percentage' => $tax_percentage ?? 0,
                        ],
                        'payment_method_types' => ['card_present'],
                        'capture_method' => 'manual',
                    ]
                );
                // Validate the payment intent
                try {
                    $stripe->payment_intents->get($paymentIntent->id);
                } catch (\Stripe\Exception\InvalidRequestException $e) {
                    $response->error('Payment intent not found', 400);
                }
                // Set the payment intent ID in the order
                $stripe_payment_intents = new stripe_payment_intents_o();
                $stripe_payment_intents->add(
                    (int)$order->id,
                    $paymentIntent->id,
                    $paymentIntent->client_secret,
                    $paymentIntent->toJSON()
                );

                // Send the payment intent to the reader
                $stripe->readers->sendPaymentIntent(
                    $data['reader'],
                    $paymentIntent->id,
                );
                // Set the reader on the stripe payment intent
                $stripe_payment_intents->reader_id->set($data['reader']);

                // Log the incident
                (new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'CHARGE_ORDER', 'Successfully charged an order (ID: ' . $data['id'] . ')');

                $response->success([
                    'payment_intent' => $paymentIntent->id,
                    'client_secret' => $paymentIntent->client_secret,
                ]);
            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'CHARGE_ORDER', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'charge_order' => 'Charge an order'
            ]
        );

        $this->get('/orders/module/stripe/payment_intent', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('get_payment_intent');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the post data
                self::requireParameters([
                    'id'
                ]);
                // Check if the required fields are set
                $id = self::getParameter('id');
                if (!isset($id)) {
                    $response->error('ID is required', 400);
                }
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$id);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // Check if the order has a payment intent
                $stripe_payment_intents = new stripe_payment_intents_o();
                if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
                    $response->error('Order does not have a payment intent', 400);
                }
                $stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
                // Get the Stripe payment intent
                $stripe = new stripe();
                try {
                    $payment_intent = $stripe->payment_intents->get(
                        $stripe_payment_intents->payment_intent_id->value(),
                        [
                            //'expand' => ['latest_charge'], // This is used to get the latest charge, that can be used to check if the payment has been refunded.
                        ]
                    );
                } catch (\Stripe\Exception\InvalidRequestException $e) {
                    $response->error('Payment intent not found', 400);
                }
                $response->success(
                    $payment_intent->toJSON()
                );
            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'GET_PAYMENT_INTENT', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'get_payment_intent' => 'Get a payment intent'
            ]
        );

        $this->delete('/orders/module/stripe/payment_intent', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('charge_order');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the data
                self::requireParameters([
                    'id'
                ]);
                $id = self::fromRequest('id');
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$id);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // Check if the order has a payment intent
                $stripe_payment_intents = new stripe_payment_intents_o();
                if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
                    $response->error('Order does not have a payment intent', 400);
                }
                $stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
                try {
                    $stripe_payment_intents->delete();
                    // Log the incident
                    (new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'DELETE_PAYMENT_INTENT', 'Successfully deleted a payment intent (ID: ' . $id . ')');
                    // Return a success message
                    $response->success(['message' => 'Payment intent deleted successfully']);
                } catch (\Stripe\Exception\InvalidRequestException $e) {
                    $response->error('Payment intent not found', 400);
                }
            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'DELETE_PAYMENT_INTENT', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'charge_order' => 'Delete a payment intent'
            ]
        );

        $this->post('/orders/module/stripe/payment_intent/capture', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('confirm_payment_intent');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the post data
                $data = json_decode(file_get_contents('php://input'), true);
                // Check if the required fields are set
                if (!isset($data['id'])) {
                    $response->error('ID is required', 400);
                }
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$data['id']);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // Check if the order has a payment intent
                $stripe_payment_intents = new stripe_payment_intents_o();
                if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
                    $response->error('Order does not have a payment intent', 400);
                }
                $stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
                // Confirm the payment intent
                $stripe = new stripe();
                try {
                    $paymentIntent = $stripe->payment_intents->capture(
                        $stripe_payment_intents->payment_intent_id->value(),
                        [] // Since we are capturing the payment, we don't need to pass any data
                    );
                    // Log the incident
                    (new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'CONFIRM_PAYMENT_INTENT', 'Successfully confirmed a payment intent (ID: ' . $data['id'] . ')');
                    // Check if the payment intent was successful
                    if ($paymentIntent->status !== 'succeeded') {
                        $response->error('Payment intent not successful', 400);
                    } else {
                        // Update the order collection to reflect the payment
                        $order_collection = $order->getOrderCollection();
                        $order_collection->paidWithStripe($paymentIntent->id);
                    }
                    // Return a success message
                    $response->success($paymentIntent->toJSON());
                } catch (\Stripe\Exception\InvalidRequestException $e) {
                    $response->error('Payment intent not found', 400);
                }
            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'CONFIRM_PAYMENT_INTENT', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'confirm_payment_intent' => 'Confirm a payment intent'
            ]
        );

        $this->post('/orders/module/stripe/debug/simulate_payment', function () {
            // Require the user to be logged in
            global $response;
            $this->requirePermission('debug_simulate_payment_intent');
            // Get the user object
            $user = (new authentication())->get_user();
            // Check if the request was successful
            if ($user) {
                // Get the post data
                $data = json_decode(file_get_contents('php://input'), true);
                // Check if the required fields are set
                if (!isset($data['id'])) {
                    $response->error('ID is required', 400);
                }
                // Get the current order
                $order = (new orders_o())->getOrderById((int)$data['id']);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 400);
                }
                // Check if the order has a payment intent
                $stripe_payment_intents = new stripe_payment_intents_o();
                if (!$stripe_payment_intents->doesOrderHavePaymentIntent((int)$order->id)) {
                    $response->error('Order does not have a payment intent', 400);
                }
                $stripe_payment_intents->selectOrderPaymentIntent((int)$order->id);
                // Simulate the payment
                $stripe = new stripe();
                //$paymentIntent = $stripe->readers->simulatePayment(
                //    $stripe_payment_intents->payment_intent_id->value(),
                //);
                $response->success('TEST_SUCCESS', 200);
            } else {
                // Log the incident
                (new logs_o())->add('orders', 'global', 1, 0, 'SIMULATE_PAYMENT_INTENT', 'No user found, or invalid session');
                // Return an error
                $response->error('Invalid session', 400);
            }
        },
            [
                'simulate_payment_intent' => 'Simulate a payment intent, this is only for testing purposes and should under no circumstances be used in production'
            ]
        );
    }

    /**
     * @throws \Exception
     */
    #[NoReturn] private function updateOrder(): void
    {
        // Require the user to be logged in
        global $response;
        // Auth & permissions (subuser-aware)
        $auth = new authentication();
        $user = $auth->get_user();
        $permission_own = self::definePermission('edit_own_orders', subusers_permission_node_key::ORDERS_EDIT);
        $permission_other = self::definePermission('edit_order');
        $has_permission_other = self::hasPermission($permission_other);
        // Classic user own-edit path (legacy behaviour)
        $classic_own_path = ($user !== false && $user->hasPermission('user') && !$has_permission_other);
        // Subuser own-edit path via node ORDERS_EDIT
        $subuser_own_path = (!$has_permission_other && self::hasPermission($permission_own));
        $isOwnPath = $classic_own_path || $subuser_own_path;
        if (!$isOwnPath && !$has_permission_other) {
            // Neither own nor admin permission — deny via admin requirement to unify error shape
            self::requirePermission($permission_other);
        }
        // Check if the request was successful
        if ($user) {
            // Get the post data
            $data = json_decode(file_get_contents('php://input'), true);
            if (!is_array($data)) {
                $data = [];
            }
            // Check if the required fields are set
            if (!isset($data['id'])) {
                $response->error('ID is required', 400);
            }
            // Get the current order
            $order = (new orders_o())->getOrderById((int)$data['id']);
            // Check if the order exists
            if (!$order->exists()) {
                $response->error('Order not found', 400);
            }
            // Own path (classic or subuser) — limited field edits only
            if ($isOwnPath) {
                // Validate that the order belongs to the effective customer context
                if ($subuser_own_path) {
                    $effectiveCustomer = self::resolveEffectiveCustomerNumber();
                    if ($effectiveCustomer === null || (int)$order->customer_id->value() !== (int)$effectiveCustomer) {
                        $response->error('You do not have permission to edit this order', 403);
                    }
                } else {
                    // Classic own path — compare against authenticated user customer number
                    if ($order->customer_id->value() !== $user->customer_number->value()) {
                        $response->error('You do not have permission to edit this order', 403);
                    }
                    // Ensure classic own-path requires user permission
                    $this->requirePermission('user');
                }
                // Allowed to edit list
                $allowed_to_edit = [
                    // Include the order ID (Even though it is not editable)
                    'id',
                    'po',
                ];
                // Check if the $data contains any non-allowed keys
                foreach ( $data as $key => $value ) {
                    if (!in_array($key, $allowed_to_edit)) {
                        $response->error('You do not have permission to edit this order field (key: ' . $key . ')', 400);
                        break;
                    }
                };
                // PO
                if (isset($data['po'])) {
                    $order->po->set((string)$data['po']);
                }
                // Registration numbers
                if (isset($data['reg_1'])) {
                    $order->reg_1->set((string)$data['reg_1']);
                }
                if (isset($data['reg_2'])) {
                    $order->reg_2->set((string)$data['reg_2']);
                }
                if (isset($data['reg_3'])) {
                    $order->reg_3->set((string)$data['reg_3']);
                }
                // Reference
                if (isset($data['reference'])) {
                    $order->reference->set((string)$data['reference']);
                }
                // Notes
                if (isset($data['notes'])) {
                    $order->notes->set((string)$data['notes']);
                }
                // Register the change
                $order->objectChanged();
                // Return a success message
                $response->success($order->asArray());
            }
            // Admin/department path (requires edit_order)
            self::requirePermission($permission_other);
            /** Departmental access */
            // If the customer ID is set, validate it
            if (isset($data['customer_id'])) {
                if (!(new users_o())->getUserByCustomerNumber((int)$data['customer_id'])->exists() || empty($data['customer_id'])) {
                    $response->error('Customer not found or invalid', 400);
                }
                $order->customer_id->set((int)$data['customer_id']);
            }
            // If the reference is set, validate it
            if (isset($data['reference'])) {
                $order->reference->set($data['reference']);
            }
            // If the notes are set, validate them
            if (isset($data['notes'])) {
                $order->notes->set($data['notes']);
            }
            // If the registration number is set, validate it
            if (isset($data['reg_1'])) {
                $order->reg_1->set($data['reg_1']);
            }
            // If the registration number 2 is set, validate it
            if (isset($data['reg_2'])) {
                $order->reg_2->set($data['reg_2']);
            }
            // If the registration number 3 is set, validate it
            if (isset($data['reg_3'])) {
                $order->reg_3->set($data['reg_3']);
            }
            // If the PO is set, validate it
            if (isset($data['po'])) {
                $order->po->set((string)$data['po']);
            }
            // If the lane is set, validate it
            if (isset($data['lane'])) {
                $order->lane->set((int)$data['lane']);
            }
            // If the department ID is set, validate it
            if (isset($data['department_id'])) {
                if (!(new departments_o())->getDepartmentById((int)$data['department_id'])) {
                    $response->error('Department not found', 400);
                }
                $order->department_id->set((int)$data['department_id']);
            }
            // If the booking ID is set, validate it
            if (isset($data['booking_id'])) {
                $order->booking_id->set((int)$data['booking_id']);
            }
            // Check if the invoice collection is set
            if (isset($data['invoice_collection_id'])) {
                $order->invoice_collection_id->set((int)$data['invoice_collection_id']);
            }
            // Check if the wash_id is set
            if (isset($data['wash_id'])) {
                $order->wash_id->set($data['wash_id']);
            }
            // Check if the created_at is set
            if (isset($data['created_at'])) {
                $order->created_at->set($data['created_at']);
            }
            // Void any cached key for the order
            $order->objectChanged();
            // Log the incident
            (new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'EDIT_ORDER', 'Successfully updated an order (ID: ' . $data['id'] . ')');
            // Return a success message
            $response->success(['message' => 'Order updated successfully']);
        } else {
            // Log the incident
            (new logs_o())->add('orders', 'global', 1, 0, 'EDIT_ORDER', 'No user found, or invalid session');
            // Return an error
            $response->error('Invalid session', 400);
        }
    }

    /**
     * @param mixed $data
     * @param response $response
     * @return mixed
     */
    private function getData(mixed $data, response $response): mixed
    {
        if (!isset($data['customer_id'])) {
            $response->error('Customer ID is required', 400);
        }
        if ((int)$data['customer_id'] < 1 || !is_numeric((int)$data['customer_id'])) {
            $response->error('Customer ID is required', 400);
        }
        if (!isset($data['department_id'])) {
            $response->error('Department ID is required', 400);
        }
        if (!isset($data['reference'])) {
            $response->error('Reference is required', 400);
        }
        if (!isset($data['notes'])) {
            $response->error('Notes is required', 400);
        }
        if (!isset($data['reg_1'])) {
            $response->error('Registration number 1 is required', 400);
        }
        if (strlen($data['reg_1']) < 4) {
            $response->error('Registration number 1 must be at least 4 characters', 400);
        }
        // Optional fields are not checked here, as they are optional and can be empty
        return $data;
    }
}