<?php
if (!defined('ABSPATH')) exit;

class W5E_Orders
{
    const META_SENT_FLAG     = '_w5e_order_exported';
    const META_LAST_ERROR    = '_w5e_order_export_error';
    const META_LAST_PAYLOAD  = '_w5e_order_export_payload';
    const META_REMOTE_IDS    = '_w5e_remote_order_ids';
    const ORDER_ACTION_SLUG  = 'w5e_send_to_lastmile';
    const META_WAREHOUSE_ID  = '_w5e_warehouse_id';

    private static $courierMap = [
        'bex'          => 1,
        'dexpress'     => 2,
        'd-express'    => 2,
        'd express'    => 2,
        'daily'        => 2,
        'aks'          => 3,
        'aks express'  => 3,
        'post'         => 4,
        'postexpress'  => 4,
        'post express' => 4,
        'post office'  => 4,
        'city'         => 5,
        'city express' => 5,
        'gls'          => 6,
    ];

    public static function init()
    {
        add_action('woocommerce_order_status_changed', [__CLASS__, 'maybe_export_order'], 20, 4);
        add_action('woocommerce_order_status_changed', [__CLASS__, 'maybe_set_manual_processing_status'], 15, 4);
        add_action('woocommerce_checkout_order_created', [__CLASS__, 'maybe_set_manual_processing_status_on_checkout'], 20, 1);
        add_filter('woocommerce_bacs_process_payment_order_status', [__CLASS__, 'filter_bacs_payment_status'], 10, 2);
        add_filter('woocommerce_cheque_process_payment_order_status', [__CLASS__, 'filter_bacs_payment_status'], 10, 2);
        add_action('woocommerce_order_status_pending', [__CLASS__, 'maybe_force_waiting_payment_status'], 10, 1);
        add_action('woocommerce_order_status_on-hold', [__CLASS__, 'maybe_force_waiting_payment_status'], 10, 1);
        add_action('woocommerce_order_status_checkout-draft', [__CLASS__, 'maybe_force_waiting_payment_status'], 10, 1);
        add_filter('woocommerce_order_actions', [__CLASS__, 'register_order_action'], 10, 2);
        add_action('woocommerce_order_action_' . self::ORDER_ACTION_SLUG, [__CLASS__, 'handle_manual_order_action']);
        add_action('add_meta_boxes_shop_order', [__CLASS__, 'register_order_meta_box'], 20);
        add_action('add_meta_boxes_woocommerce_page_wc-orders', [__CLASS__, 'register_order_meta_box'], 20);
        add_action('add_meta_boxes_shop_order', [__CLASS__, 'register_tracking_meta_box'], 21);
        add_action('add_meta_boxes_woocommerce_page_wc-orders', [__CLASS__, 'register_tracking_meta_box'], 21);
        add_action('add_meta_boxes_shop_order', [__CLASS__, 'reorder_order_meta_boxes'], 999);
        add_action('add_meta_boxes_woocommerce_page_wc-orders', [__CLASS__, 'reorder_order_meta_boxes'], 999);
        add_action('add_meta_boxes_shop_order', [__CLASS__, 'reorder_tracking_meta_boxes'], 1000);
        add_action('add_meta_boxes_woocommerce_page_wc-orders', [__CLASS__, 'reorder_tracking_meta_boxes'], 1000);
        add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue_admin_assets']);
        add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue_bulk_assets']);
        add_action('wp_ajax_w5e_send_order_to_5e', [__CLASS__, 'ajax_send_order_to_5e']);
        add_action('wp_ajax_w5e_convert_order_to_5e', [__CLASS__, 'ajax_convert_order_to_5e']);
        add_action('woocommerce_process_shop_order_meta', [__CLASS__, 'save_order_warehouse_meta'], 5, 2);
        add_action('woocommerce_order_status_cancelled', [__CLASS__, 'send_cancellation_to_5e'], 10, 2);
        add_filter('woocommerce_order_item_get_formatted_meta_data', [__CLASS__, 'format_shipping_item_meta'], 10, 2);
        add_filter('bulk_actions-woocommerce_page_wc-orders', [__CLASS__, 'register_bulk_actions']);
        add_filter('bulk_actions-edit-shop_order', [__CLASS__, 'register_bulk_actions']);
        add_filter('handle_bulk_actions-woocommerce_page_wc-orders', [__CLASS__, 'handle_bulk_actions'], 10, 3);
        add_filter('handle_bulk_actions-edit-shop_order', [__CLASS__, 'handle_bulk_actions'], 10, 3);
        add_action('admin_notices', [__CLASS__, 'render_bulk_notice']);
        add_action('admin_notices', [__CLASS__, 'render_missing_warehouse_notice']);
    }

    private static function get_import_mode()
    {
        $mode = sanitize_key((string)get_option('w5e_import_mode', 'auto'));
        return in_array($mode, ['auto', 'manual'], true) ? $mode : 'auto';
    }

    private static function should_enqueue_on_hook($hook)
    {
        if (!is_string($hook) || $hook === '') {
            return false;
        }

        if ($hook === 'post.php' || $hook === 'post-new.php') {
            if (function_exists('get_current_screen')) {
                $screen = get_current_screen();
                return $screen && !empty($screen->post_type) && $screen->post_type === 'shop_order';
            }
            return true;
        }

        if ($hook === 'woocommerce_page_wc-orders') {
            return isset($_GET['action']) && sanitize_key($_GET['action']) === 'edit';
        }

        return false;
    }

    private static function is_orders_list_screen($hook)
    {
        if (!is_string($hook) || $hook === '') {
            return false;
        }

        if ($hook === 'woocommerce_page_wc-orders') {
            return !isset($_GET['action']) || sanitize_key($_GET['action']) !== 'edit';
        }

        if ($hook === 'edit.php') {
            if (function_exists('get_current_screen')) {
                $screen = get_current_screen();
                return $screen && !empty($screen->post_type) && $screen->post_type === 'shop_order';
            }
            return true;
        }

        return false;
    }

    private static function is_enabled()
    {
        return get_option('w5e_order_forwarding_enabled', 'no') === 'yes';
    }

    private static function get_export_statuses()
    {
        $saved = get_option('w5e_order_export_statuses', []);
        if (!is_array($saved) || empty($saved)) {
            return ['processing', 'completed'];
        }
        return array_values(array_filter(array_map('sanitize_key', $saved)));
    }

    private static function should_export_on_status($status)
    {
        $status = sanitize_key($status);
        if ($status === '') {
            return false;
        }
        $allowed = self::get_export_statuses();
        return in_array($status, $allowed, true);
    }

    public static function maybe_set_manual_processing_status($order_id, $old_status, $new_status, $order)
    {
        self::log_debug('Hook woocommerce_order_status_changed (manual processing)', [
            'order_id'   => $order_id,
            'old_status' => $old_status,
            'new_status' => $new_status,
            'import_mode' => self::get_import_mode(),
        ]);

        if (self::get_import_mode() !== 'manual') {
            return;
        }

        if (!$order instanceof WC_Order) {
            $order = wc_get_order($order_id);
        }
        if (!$order) {
            return;
        }

        if (!self::get_w5e_shipping_item($order)) {
            return;
        }

        if (
            self::is_waiting_payment_method($order) &&
            in_array($new_status, ['pending', 'on-hold', 'checkout-draft'], true) &&
            $order->get_status() !== 'w5e_waiting_payme'
        ) {
            $order->set_status('w5e_waiting_payme');
            $order->save();
            return;
        }

        if (self::is_bacs_waiting_payment($order, $new_status)) {
            return;
        }
        if ($order->needs_payment()) {
            return;
        }

        if ($new_status === 'w5e_processing') {
            return;
        }
        if (in_array($new_status, ['cancelled', 'refunded', 'failed'], true)) {
            return;
        }

        $warehouseId = sanitize_text_field((string)$order->get_meta(self::META_WAREHOUSE_ID));
        if ($warehouseId !== '') {
            return;
        }

        $order->set_status('w5e_processing');
        $order->save();
    }

    public static function maybe_set_manual_processing_status_on_checkout($order)
    {
        if (!$order instanceof WC_Order) {
            return;
        }

        self::log_debug('Hook woocommerce_checkout_order_created', [
            'order_id' => $order->get_id(),
            'status'   => $order->get_status(),
        ]);
        self::maybe_set_manual_processing_status($order->get_id(), $order->get_status(), $order->get_status(), $order);
    }

    public static function filter_bacs_payment_status($status, $order)
    {
        if (!$order instanceof WC_Order) {
            return $status;
        }

        self::log_debug('Filter bacs/cheque payment status', [
            'order_id'  => $order->get_id(),
            'status_in' => $status,
        ]);

        if (!self::get_w5e_shipping_item($order)) {
            return $status;
        }

        return 'w5e_waiting_payme';
    }

    private static function is_bacs_waiting_payment(WC_Order $order, $status)
    {
        if (!self::is_waiting_payment_method($order)) {
            return false;
        }

        return in_array($status, ['pending', 'on-hold', 'w5e_waiting_payme'], true);
    }

    private static function is_waiting_payment_method(WC_Order $order)
    {
        $method = $order->get_payment_method();
        return in_array($method, ['bacs', 'cheque'], true);
    }

    public static function maybe_force_waiting_payment_status($order_id)
    {
        self::log_debug('Hook woocommerce_order_status_pending/on-hold/checkout-draft', [
            'order_id' => $order_id,
        ]);

        $order = wc_get_order($order_id);
        if (!$order) {
            return;
        }

        if (!self::get_w5e_shipping_item($order)) {
            return;
        }

        if (!self::is_waiting_payment_method($order)) {
            return;
        }

        if ($order->get_status() === 'w5e_waiting_payme') {
            return;
        }

        $order->set_status('w5e_waiting_payme');
        $order->save();
    }

    public static function maybe_export_order($order_id, $old_status, $new_status, $order)
    {
        self::log_debug('Hook woocommerce_order_status_changed (export check)', [
            'order_id'   => $order_id,
            'old_status' => $old_status,
            'new_status' => $new_status,
            'enabled'    => self::is_enabled(),
            'import_mode' => self::get_import_mode(),
        ]);

        if (!self::is_enabled()) {
            self::log_debug('Export skipped: forwarding disabled', [
                'order_id' => $order_id,
            ]);
            return;
        }

        if (!self::should_export_on_status($new_status)) {
            self::log_debug('Export skipped: status not enabled', [
                'order_id' => $order_id,
                'status'   => $new_status,
            ]);
            return;
        }

        if (!$order instanceof WC_Order) {
            $order = wc_get_order($order_id);
        }

        if (!$order) {
            return;
        }

        if (self::get_import_mode() === 'manual') {
            $warehouseId = sanitize_text_field((string)$order->get_meta(self::META_WAREHOUSE_ID));
            if ($warehouseId === '') {
                self::log_debug('Export skipped: missing warehouse in manual mode', [
                    'order_id' => $order->get_id(),
                ]);
                return;
            }
        }

        self::log_debug('Export attempt', [
            'order_id' => $order->get_id(),
            'status'   => $order->get_status(),
        ]);
        $result = self::export_order($order);
        if (is_wp_error($result)) {
            self::log_error($order->get_id(), '5E export failed: ' . $result->get_error_message());
        }
    }

    public static function register_order_action($actions, $order)
    {
        if (!$order instanceof WC_Order) {
            return $actions;
        }

        if (self::get_import_mode() !== 'auto') {
            return $actions;
        }

        if (!self::get_w5e_shipping_item($order)) {
            return $actions;
        }

        $actions[self::ORDER_ACTION_SLUG] = __('Send to 5E / Last Mile', 'woo-5e');
        return $actions;
    }

    public static function handle_manual_order_action($order)
    {
        if (self::get_import_mode() !== 'auto') {
            return;
        }

        if (!$order instanceof WC_Order) {
            $order = wc_get_order($order);
        }
        if (!$order) {
            return;
        }

        self::log_debug('Manual order action: send to 5E', [
            'order_id' => $order->get_id(),
        ]);
        $result = self::export_order($order, true);
        if (is_wp_error($result)) {
            $message = sprintf(__('Manual 5E export failed: %s', 'woo-5e'), $result->get_error_message());
            $order->add_order_note($message);
            self::log_error($order->get_id(), $message);
            if (class_exists('WC_Admin_Meta_Boxes')) {
                WC_Admin_Meta_Boxes::add_error($message);
            }
        } else {
            $order->add_order_note(__('Order sent to 5E / Last Mile.', 'woo-5e'));
            if (class_exists('WC_Admin_Meta_Boxes')) {
                WC_Admin_Meta_Boxes::add_message(__('Order sent to 5E / Last Mile.', 'woo-5e'));
            }
        }
    }

    private static function export_order(WC_Order $order, $force = false, $warehouseIdOverride = '')
    {
        if (!$force && self::already_sent($order)) {
            return true;
        }

        $shippingItem = self::get_w5e_shipping_item($order);
        if (!$shippingItem) {
            return new WP_Error('w5e_missing_shipping', __('Order is not using a 5E shipping rate.', 'woo-5e'));
        }

        $warehouseId = sanitize_text_field((string)$warehouseIdOverride);
        if ($warehouseId === '') {
            $warehouseId = sanitize_text_field((string)$order->get_meta(self::META_WAREHOUSE_ID));
        }
        if (self::get_import_mode() === 'manual' && $warehouseId === '') {
            return new WP_Error('w5e_warehouse', __('Select a warehouse for this order before sending to 5E.', 'woo-5e'));
        }
        if ($warehouseId === '') {
            $warehouseId = sanitize_text_field((string)get_option('w5e_default_warehouse_id', ''));
        }
        if ($warehouseId === '') {
            return new WP_Error('w5e_warehouse', __('Default warehouse is not configured.', 'woo-5e'));
        }

        try {
            $payload = self::build_payload($order, $shippingItem, $warehouseId);
            self::log_debug('Export payload built', [
                'order_id' => $order->get_id(),
                'payload'  => $payload,
            ]);
        } catch (Exception $e) {
            $order->update_meta_data(self::META_LAST_ERROR, $e->getMessage());
            $order->save_meta_data();
            self::log_debug('Export payload error', [
                'order_id' => $order->get_id(),
                'error'    => $e->getMessage(),
            ]);
            return new WP_Error('w5e_payload', $e->getMessage());
        }

        try {
            $api      = new W5E_API();
            $clientId = get_option('w5e_id', '');
            $apiKey   = get_option('w5e_api_key', '');
            if (!$clientId || !$apiKey) {
                throw new Exception(__('Missing 5E credentials (ID or API key).', 'woo-5e'));
            }
            $response = $api->process_order($clientId, $apiKey, $payload, get_option('w5e_env', 'staging'));
            self::log_debug('Export response received', [
                'order_id' => $order->get_id(),
                'response' => $response,
            ]);

            $order->update_meta_data(self::META_SENT_FLAG, current_time('mysql'));
            $order->update_meta_data(self::META_LAST_PAYLOAD, wp_json_encode($payload));
            $remoteIds = self::extract_remote_order_ids($response);
            if (!empty($remoteIds)) {
                $order->update_meta_data(self::META_REMOTE_IDS, $remoteIds);
            }
            $order->update_meta_data(self::META_LAST_ERROR, '');
            $order->save_meta_data();
            self::log_info($order->get_id(), '5E order exported: ' . wp_json_encode($response));
            return true;
        } catch (Exception $e) {
            $order->update_meta_data(self::META_LAST_ERROR, $e->getMessage());
            $order->update_meta_data(self::META_LAST_PAYLOAD, wp_json_encode($payload ?? []));
            $order->save_meta_data();
            self::log_debug('Export exception', [
                'order_id' => $order->get_id(),
                'error'    => $e->getMessage(),
            ]);
            return new WP_Error('w5e_api', $e->getMessage());
        }
    }

    private static function already_sent(WC_Order $order)
    {
        return (bool)$order->get_meta(self::META_SENT_FLAG);
    }

    private static function get_w5e_shipping_item(WC_Order $order)
    {
        foreach ($order->get_items('shipping') as $item) {
            $methodId = $item->get_method_id();
            if ($methodId === 'w5e' || strpos($methodId, 'w5e') === 0) {
                return $item;
            }
        }
        return null;
    }

    private static function build_payload(WC_Order $order, WC_Order_Item_Shipping $shippingItem, $warehouseId)
    {
        if ($warehouseId === '') {
            throw new Exception(__('Default warehouse is not configured.', 'woo-5e'));
        }

        $buyoutAccount = sanitize_text_field(get_option('w5e_buyout_account', ''));
        $buyoutPayer   = get_option('w5e_buyout_payer', 'recipient');
        $isPrepaid     = self::is_prepaid_payment($order, $buyoutPayer);

        if (!$isPrepaid && $buyoutAccount === '') {
            throw new Exception(__('Buy-out bank account is not configured.', 'woo-5e'));
        }

        $createdUser = absint(get_option('w5e_created_user_id', 1));
        $clientUser  = sanitize_text_field(get_option('w5e_client_user_id', ''));
        $deliveryType = absint(get_option('w5e_delivery_type', 1));
        $shipmentType = absint(get_option('w5e_shipment_type', 1));
        $delPaymentBy = ($buyoutPayer === 'buyer_bank') ? 3 : 2; // 3 = buyer via bank, 2 = recipient
        $preNotify    = absint(get_option('w5e_recipient_pre_notification', 0));
        $autoApprove  = get_option('w5e_auto_approve', 'no') === 'yes';
        $returnDoc    = get_option('w5e_return_doc', 'no') === 'yes';

        $weight = self::calculate_weight($order);
        $parcelType = self::determine_parcel_type($weight);
        $dimensions = self::calculate_dimensions($order);

        $address = self::resolve_address($order);
        $courierSelection = self::detect_courier_selection($shippingItem);

        $shipmentContent = self::build_shipment_content($order);
        $shipmentContentOption = sanitize_text_field(get_option('w5e_shipment_content', ''));
        if ($shipmentContentOption !== '') {
            $shipmentContent = $shipmentContentOption;
        } else {
            $shipmentContent = 'content';
        }
        if (function_exists('mb_substr')) {
            $shipmentContent = mb_substr($shipmentContent, 0, 250);
        } else {
            $shipmentContent = substr($shipmentContent, 0, 250);
        }
        $quantity = self::get_total_quantity($order);

        $currency = $order->get_currency();
        $orderTotal = (float)$order->get_total();
        $orderValue = self::calculate_goods_value($order);
        $buyoutAmount = $isPrepaid ? 0.0 : $orderTotal;

        $dates = self::resolve_dates($order);
        $orderStatus = self::map_order_status($order);

        $payload = [
            'OrderID'              => (int)$order->get_id(),
            'CreatedEditedByUser'  => $createdUser ?: 1,
            'ClientWarehouseID'    => $warehouseId,
            'ClientUserID'         => $clientUser,
            'ReferenceID'          => (string)$order->get_order_number(),
            'CourierSelection'     => $courierSelection,
            'AutoApprove'          => $autoApprove,
            'DateCreated'          => $dates['created'],
            'ParcelType'           => $parcelType,
            'DeliveryType'         => $deliveryType ?: 1,
            'ShipmentType'         => $shipmentType ?: 1,
            'BuyOutAmount'         => wc_format_decimal($buyoutAmount, 2),
            'BuyOutAccount'        => $buyoutAccount,
            'OrderValue'           => wc_format_decimal($orderValue > 0 ? $orderValue : $orderTotal, 2),
            'Currency'             => $currency,
            'ShipmentContent'      => $shipmentContent,
            'NetWeight'            => $weight,
            'BrutWeight'           => $weight,
            'Height'               => $dimensions['height'],
            'Width'                => $dimensions['width'],
            'Length'               => $dimensions['length'],
            'CBM'                  => 0,
            'ReturnDoc'            => $returnDoc,
            'RecipientCompany'     => $address['company'],
            'RecipientFirstName'   => $address['first_name'],
            'RecipientLastName'    => $address['last_name'],
            'RecipientMunicipality' => $address['city'] ?: $address['state'],
            'RecipientAddress'     => $address['street'],
            'RecipientTown'        => $address['place'] ?? $address['city'],
            'RecipientTownID'      => $address['place_id'] ?? '',
            'RecipientHouseNumber' => $address['house_number'],
            'RecipientApartment'   => $address['apartment'],
            'RecipientPhone'       => $address['phone'],
            'RecipientPostNumber'  => $address['postcode'],
            'RecipientEmail'       => $address['email'],
            'RecipientNote'        => $address['note'],
            'RecipientPreNotification' => $preNotify,
            'OrderStatus'          => $orderStatus,
            'Quantity'             => $quantity,
            'TrackingNumber'       => '',
            'DelPaymentBy'         => $delPaymentBy ?: 1,
            'CreatedAt'            => $dates['now'],
            'UpdatedAt'            => $dates['now'],
        ];

        return $payload;
    }

    private static function calculate_goods_value(WC_Order $order)
    {
        $value = 0.0;
        foreach ($order->get_items() as $item) {
            $value += (float)$item->get_total();
            $value += (float)$item->get_total_tax();
        }
        return $value;
    }

    private static function map_order_status(WC_Order $order)
    {
        $status = $order->get_status();
        switch ($status) {
            case 'w5e_waiting_payme':
            case 'w5e_processing':
            case 'processing':
            case 'completed':
                return 'Pending';
            case 'pending':
            case 'on-hold':
                return 'Pending';
            case 'cancelled':
            case 'canceled':
            case 'refunded':
            case 'failed':
                return 'Canceled';
            case 'in-progress':
            case 'in_progress':
                return 'In Progress';
            default:
                return 'Pending';
        }
    }

    private static function calculate_weight(WC_Order $order)
    {
        $weight = 0.0;
        foreach ($order->get_items() as $item) {
            $product = $item->get_product();
            if (!$product || !$product->needs_shipping()) {
                continue;
            }
            $productWeight = $product->get_weight();
            if ($productWeight === '' || $productWeight === null) {
                continue;
            }
            if (function_exists('wc_get_weight')) {
                $productWeight = wc_get_weight($productWeight, 'kg');
            }
            $qty = max(1, (int)$item->get_quantity());
            $weight += max(0, (float)$productWeight) * $qty;
        }
        return round($weight, 3);
    }

    private static function calculate_dimensions(WC_Order $order)
    {
        $height = 0.0;
        $width  = 0.0;
        $length = 0.0;

        foreach ($order->get_items() as $item) {
            $product = $item->get_product();
            if (!$product) {
                continue;
            }
            $qty = max(1, (int)$item->get_quantity());

            $itemHeight = self::normalize_dimension($product->get_height());
            $itemWidth  = self::normalize_dimension($product->get_width());
            $itemLength = self::normalize_dimension($product->get_length());

            $height += $itemHeight * $qty;
            $width  += $itemWidth * $qty;
            $length += $itemLength * $qty;
        }

        return [
            'height' => round($height, 3),
            'width'  => round($width, 3),
            'length' => round($length, 3),
        ];
    }

    private static function normalize_dimension($value)
    {
        if ($value === '' || $value === null) {
            return 0.0;
        }
        if (function_exists('wc_get_dimension')) {
            return (float)wc_get_dimension($value, 'cm');
        }
        return (float)$value;
    }

    private static function determine_parcel_type($weight)
    {
        if ($weight <= 0.5) {
            return 3;
        }
        if ($weight <= 1) {
            return 4;
        }
        if ($weight <= 2) {
            return 5;
        }
        if ($weight <= 5) {
            return 6;
        }
        if ($weight <= 10) {
            return 7;
        }
        return 8;
    }

    private static function detect_courier_selection(WC_Order_Item_Shipping $item)
    {
        $label = strtolower($item->get_name());
        foreach (self::$courierMap as $needle => $code) {
            if (strpos($label, $needle) !== false) {
                return $code;
            }
        }

        $partnerId = (int)$item->get_meta('w5e_partner_id');
        if ($partnerId) {
            $name = self::lookup_partner_name($partnerId, $item->get_meta('w5e_env'));
            if ($name) {
                $name = strtolower($name);
                foreach (self::$courierMap as $needle => $code) {
                    if (strpos($name, $needle) !== false) {
                        return $code;
                    }
                }
            }
        }

        return 1;
    }

    private static function lookup_partner_name($partnerId, $env)
    {
        $cache = get_option('w5e_couriers_cache', []);
        if (!is_array($cache)) {
            return '';
        }
        foreach ($cache as $row) {
            if ((int)$row['PartnerID'] === $partnerId) {
                return isset($row['CourierName']) ? (string)$row['CourierName'] : '';
            }
        }
        return '';
    }

    public static function format_shipping_item_meta($formatted_meta, $item)
    {
        if (!is_admin()) {
            return $formatted_meta;
        }

        if (!$item instanceof WC_Order_Item_Shipping) {
            return $formatted_meta;
        }

        $methodId = $item->get_method_id();
        if ($methodId === '' || ($methodId !== 'w5e' && strpos($methodId, 'w5e') !== 0)) {
            return $formatted_meta;
        }

        $partnerId = (int)$item->get_meta('w5e_partner_id');
        $env = sanitize_text_field((string)$item->get_meta('w5e_env'));
        $courierKey = sanitize_text_field((string)$item->get_meta('w5e_key'));
        $courierName = $partnerId ? self::lookup_partner_name($partnerId, $env) : '';
        $courierLabel = $courierName !== '' ? $courierName : $courierKey;
        if ($courierLabel !== '' && $env !== '') {
            $courierLabel .= ' (' . $env . ')';
        }

        foreach ($formatted_meta as $meta_id => $meta) {
            if (!isset($meta->key)) {
                continue;
            }
            if (in_array($meta->key, ['w5e_key', 'w5e_partner_id', 'w5e_env', 'w5e_cost_source'], true)) {
                unset($formatted_meta[$meta_id]);
            }
        }

        if ($courierLabel !== '') {
            $formatted_meta[] = (object)[
                'key'           => 'w5e_courier',
                'value'         => $courierLabel,
                'display_key'   => __('5E Courier', 'woo-5e'),
                'display_value' => $courierLabel,
            ];
        }

        if ($partnerId) {
            $formatted_meta[] = (object)[
                'key'           => 'w5e_partner_id',
                'value'         => (string)$partnerId,
                'display_key'   => __('5E Courier ID', 'woo-5e'),
                'display_value' => (string)$partnerId,
            ];
        }

        if ($env !== '') {
            $formatted_meta[] = (object)[
                'key'           => 'w5e_env',
                'value'         => $env,
                'display_key'   => __('5E Environment', 'woo-5e'),
                'display_value' => $env,
            ];
        }

        $costSource = sanitize_text_field((string)$item->get_meta('w5e_cost_source'));
        if ($costSource !== '') {
            $sourceLabels = [
                'instance_fixed' => __('Fixed rate (instance)', 'woo-5e'),
                'free'           => __('Free shipping', 'woo-5e'),
                'flat'           => __('Flat rate', 'woo-5e'),
                'matrix'         => __('Pricing matrix', 'woo-5e'),
                'recipient_tariff' => __('Recipient pays on delivery', 'woo-5e'),
                'sender_pays'    => __('Sender pays shipping.', 'woo-5e'),
            ];
            $costLabel = isset($sourceLabels[$costSource]) ? $sourceLabels[$costSource] : $costSource;
            $formatted_meta[] = (object)[
                'key'           => 'w5e_cost_source',
                'value'         => $costSource,
                'display_key'   => __('5E Cost Source', 'woo-5e'),
                'display_value' => $costLabel,
            ];
        }

        return $formatted_meta;
    }

    private static function build_shipment_content(WC_Order $order)
    {
        $parts = [];
        foreach ($order->get_items() as $item) {
            $name = $item->get_name();
            $qty  = $item->get_quantity();
            if ($name) {
                $parts[] = trim($name) . ' x' . $qty;
            }
        }
        $content = implode(', ', $parts);
        if ($content === '') {
            $content = __('Web order', 'woo-5e');
        }

        if (function_exists('mb_substr')) {
            return mb_substr($content, 0, 250);
        }

        return substr($content, 0, 250);
    }

    private static function get_total_quantity(WC_Order $order)
    {
        return 1;
    }

    private static function resolve_dates(WC_Order $order)
    {
        $created = $order->get_date_created();
        $createdIso = $created ? gmdate('c', $created->getTimestamp()) : gmdate('c');
        $now = gmdate('c');
        return [
            'created' => $createdIso,
            'now'     => $now,
        ];
    }

    private static function resolve_address(WC_Order $order)
    {
        $firstName = $order->get_shipping_first_name() ?: $order->get_billing_first_name();
        $lastName  = $order->get_shipping_last_name() ?: $order->get_billing_last_name();
        $company   = $order->get_shipping_company() ?: $order->get_billing_company();
        $state     = $order->get_shipping_state() ?: $order->get_billing_state();
        $city      = $order->get_shipping_city() ?: $order->get_billing_city();
        $postcode  = $order->get_shipping_postcode() ?: $order->get_billing_postcode();
        $phone     = $order->get_billing_phone();
        $email     = $order->get_billing_email();
        $address1  = $order->get_shipping_address_1() ?: $order->get_billing_address_1();
        $address2  = $order->get_shipping_address_2() ?: $order->get_billing_address_2();
        $note      = $order->get_customer_note();

        // House number now comes from address_2 (required). If it contains a pattern like 99/11 or 99-22, split the apartment from it.
        list($houseNumber, $apartment) = self::parse_house_and_apartment($address2);
        if (!$houseNumber) {
            $houseNumber = self::extract_house_number($address1);
            $street      = self::strip_house_number($address1, $houseNumber);
        } else {
            $street = $address1;
        }

        $place_source = $city ?: $state;
        $place_id = '';
        $place = $place_source;
        $municipality = $state;
        if ($place_source && $postcode && class_exists('W5E_Address_Directory') && method_exists('W5E_Address_Directory', 'resolve_place_from_city')) {
            $resolved = W5E_Address_Directory::resolve_place_from_city((string) $place_source, (string) $postcode);
            if (is_array($resolved)) {
                $place_id = isset($resolved['place_id']) ? (string) $resolved['place_id'] : '';
                $place = isset($resolved['place']) ? (string) $resolved['place'] : $place_source;
                $municipality = isset($resolved['municipality']) && $resolved['municipality'] !== ''
                    ? (string) $resolved['municipality']
                    : $state;
            }
        }

        return [
            'first_name'   => $firstName ?: __('Customer', 'woo-5e'),
            'last_name'    => $lastName ?: __('Webshop', 'woo-5e'),
            'company'      => $company,
            'state'        => $municipality ?: $state ?: $city,
            'city'         => $city ?: $state,
            'place'        => $place ?: ($city ?: $state),
            'place_id'     => $place_id,
            'postcode'     => $postcode ?: '00000',
            'phone'        => $phone ?: '0',
            'email'        => $email ?: get_option('admin_email'),
            'street'       => $street ?: $address1,
            'house_number' => $houseNumber ?: 0,
            'apartment'    => $apartment,
            'note'         => $note,
        ];
    }

    /**
     * Parse house number and apartment from address_2 when formatted as "99/11", "99-22", "99 / 22",
     * or even "/22" where only apartment is provided. Removes slashes/dashes from outputs.
     */
    private static function parse_house_and_apartment($address2)
    {
        $addr = trim((string)$address2);
        if ($addr === '') {
            return ['', ''];
        }

        $house = '';
        $apt   = '';

        // Common patterns: "99/11", "99-22", "99 / 22".
        if (preg_match('/^([0-9A-Za-z]+[^\s\/\-]*)\s*[\/\-]\s*([0-9A-Za-z]+[^\s]*)$/u', $addr, $m)) {
            $house = $m[1];
            $apt   = $m[2];
        } elseif (preg_match('/^[\/\-]\s*([0-9A-Za-z]+[^\s]*)$/u', $addr, $m)) {
            // Pattern: "/22" or "-22" (apartment only).
            $house = '';
            $apt   = $m[1];
        } else {
            // Fallback: split on first slash or dash if present.
            $house = $addr;
            $parts = preg_split('/[\/\-]/', $addr, 2);
            if (is_array($parts) && count($parts) === 2) {
                $house = trim($parts[0]);
                $apt   = trim($parts[1]);
            }
        }

        $house = ltrim($house, "/- 	");
        $apt   = ltrim($apt, "/- 	");

        return [$house, $apt];
    }

    private static function extract_house_number($address)
    {
        if (!$address) {
            return '';
        }
        if (preg_match('/(\d+[\w\/\-]*)$/u', $address, $m)) {
            return $m[1];
        }
        return '';
    }

    private static function strip_house_number($address, $number)
    {
        if (!$address) {
            return '';
        }
        if (!$number) {
            return $address;
        }
        return trim(str_replace($number, '', $address));
    }

    /**
     * Extract remote order IDs from 5E API response.
     */
    private static function extract_remote_order_ids($response)
    {
        $ids = [];
        if (is_array($response)) {
            $flat = new RecursiveIteratorIterator(new RecursiveArrayIterator($response));
            foreach ($flat as $key => $value) {
                if (!is_string($key)) {
                    continue;
                }
                $k = strtolower($key);
                if (in_array($k, ['orderid', 'order_id', 'id'], true) && is_scalar($value)) {
                    $val = trim((string)$value);
                    if ($val !== '') {
                        $ids[] = $val;
                    }
                }
            }
        }
        return array_values(array_unique(array_filter($ids, function ($id) {
            return is_scalar($id) && trim((string)$id) !== '';
        })));
    }

    public static function send_cancellation_to_5e($order_id, $order)
    {
        self::log_debug('Hook woocommerce_order_status_cancelled', [
            'order_id' => $order_id,
        ]);

        if (!$order instanceof WC_Order) {
            $order = wc_get_order($order_id);
        }
        if (!$order) {
            return;
        }

        $clientId = get_option('w5e_id', '');
        $apiKey   = get_option('w5e_api_key', '');
        if (!$clientId || !$apiKey) {
            return;
        }

        $reason  = apply_filters('w5e_cancel_reason', '', $order);
        $env     = get_option('w5e_env', 'staging');

        // Prefer remote IDs stored from last export response; then payload OrderID; fallback Woo ID.
        $orderIds = [];
        $remoteStored = $order->get_meta(self::META_REMOTE_IDS, true);
        if (is_array($remoteStored) && !empty($remoteStored)) {
            $orderIds = array_merge($orderIds, $remoteStored);
        }
        $lastPayload = json_decode((string)$order->get_meta(self::META_LAST_PAYLOAD), true);
        if (is_array($lastPayload) && !empty($lastPayload['OrderID'])) {
            $orderIds[] = (string)$lastPayload['OrderID'];
        }
        $orderIds[] = (string)$order->get_id();
        $orderIds = array_values(array_unique(array_filter($orderIds, function ($id) {
            return is_scalar($id) && trim((string)$id) !== '';
        })));
        if (empty($orderIds)) {
            return;
        }

        try {
            $api = new W5E_API();
            $api->update_order_status($clientId, $apiKey, $orderIds, 'Canceled', $reason, $env);
            $order->add_order_note(__('Cancellation sent to 5E.', 'woo-5e'));
            self::log_debug('Cancellation sent to 5E', [
                'order_id' => $order->get_id(),
                'orderIds' => $orderIds,
                'reason'   => $reason,
                'env'      => $env,
            ]);
            self::log_info($order->get_id(), 'Cancellation pushed to 5E: ' . wp_json_encode([
                'orderIds' => $orderIds,
                'status'   => 'Canceled',
                'reason'   => $reason,
                'env'      => $env,
            ]));
        } catch (Exception $e) {
            self::log_debug('Cancellation sync failed', [
                'order_id' => $order->get_id(),
                'error'    => $e->getMessage(),
            ]);
            self::log_error($order->get_id(), 'Cancel sync failed: ' . $e->getMessage());
            $order->add_order_note(sprintf(__('Failed to send cancellation to 5E: %s', 'woo-5e'), $e->getMessage()));
        }
    }

    /**
     * Detect if the chosen 5E rate is free (cost 0 or meta marked as free).
     */
    private static function is_free_shipping_rate($shippingItem)
    {
        if (!$shippingItem || !is_object($shippingItem)) {
            return false;
        }

        $metaSource = '';
        if (method_exists($shippingItem, 'get_meta')) {
            $metaSource = $shippingItem->get_meta('w5e_cost_source', true);
        }

        $total = method_exists($shippingItem, 'get_total') ? (float)$shippingItem->get_total() : 0.0;

        return ($metaSource === 'free') || ($total <= 0);
    }

    /**
     * Detect if the order should be treated as prepaid/card for buy-out amount calculation.
     * Returns true when buy-out amount should be 0.00.
     *
     * @param WC_Order $order
     * @param string   $configuredPayer w5e_buyout_payer option.
     * @return bool
     */
    private static function is_prepaid_payment(WC_Order $order, $configuredPayer)
    {
        $method     = $order->get_payment_method();
        return self::is_card_method($order, $method);
    }

    /**
     * Determine if a gateway represents card payment (prepaid).
     */
    private static function is_card_method(WC_Order $order, $method)
    {
        $cardMethods = apply_filters('w5e_card_gateways', [
            'stripe',
            'woocommerce_payments',
            'wcpay',
            'wc_payment',
            'credit_card',
            'braintree_cc',
            'braintree',
            'mollie_wc_gateway_creditcard',
            'payplug_creditcard',
            'checkout_com',
            'payu_card',
        ]);

        if ($method && in_array($method, $cardMethods, true)) {
            return true;
        }

        $title = strtolower((string)$order->get_payment_method_title());
        $keywords = [
            'card',
            'kartic',
            'visa',
            'master',
            'maestro',
            'amex',
            'diners',
            'discover',
            'unionpay',
        ];
        foreach ($keywords as $kw) {
            if ($title && strpos($title, $kw) !== false) {
                return true;
            }
        }

        return false;
    }

    private static function log_error($order_id, $message)
    {
        if (!function_exists('wc_get_logger')) {
            return;
        }
        wc_get_logger()->error('[Woo 5E] Order ' . $order_id . ': ' . $message, ['source' => 'woo-5e']);
    }

    private static function log_info($order_id, $message)
    {
        if (!function_exists('wc_get_logger')) {
            return;
        }
        wc_get_logger()->info('[Woo 5E] Order ' . $order_id . ': ' . $message, ['source' => 'woo-5e']);
    }

    private static function log_debug($message, array $context = [])
    {
        if (function_exists('w5e_log_debug')) {
            w5e_log_debug($message, $context);
        }
    }

    public static function enqueue_admin_assets($hook)
    {
        if (self::get_import_mode() !== 'manual') {
            return;
        }

        if (!self::should_enqueue_on_hook($hook)) {
            return;
        }

        wp_enqueue_script(
            'w5e-order-admin',
            W5E_URL . 'assets/order-admin.js',
            ['jquery'],
            W5E_VER,
            true
        );

        wp_localize_script('w5e-order-admin', 'W5EOrder', [
            'ajax'  => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('w5e_order_send'),
            'i18n'  => [
                'sending'      => __('Sending…', 'woo-5e'),
                'sent'         => __('Order sent to 5E / Last Mile.', 'woo-5e'),
                'selectFirst'  => __('Select a warehouse first.', 'woo-5e'),
                'selectCourierFirst' => __('Select a courier first.', 'woo-5e'),
                'converting'   => __('Switching to 5E…', 'woo-5e'),
                'converted'    => __('Order shipping switched to 5E.', 'woo-5e'),
                'errorPrefix'  => __('Error:', 'woo-5e'),
                'unknownError' => __('Unknown', 'woo-5e'),
                'requestError' => __('Request failed.', 'woo-5e'),
            ],
        ]);
    }

    public static function enqueue_bulk_assets($hook)
    {
        if (self::get_import_mode() !== 'manual') {
            return;
        }

        if (!self::is_orders_list_screen($hook)) {
            return;
        }

        wp_enqueue_script(
            'w5e-order-bulk',
            W5E_URL . 'assets/order-bulk.js',
            ['jquery'],
            W5E_VER,
            true
        );

        wp_localize_script('w5e-order-bulk', 'W5EBulkOrder', [
            'action' => 'w5e_send_to_5e_bulk',
            'warehouses' => self::get_warehouse_options(),
            'i18n' => [
                'label' => __('Warehouse', 'woo-5e'),
                'selectWarehouse' => __('Select warehouse', 'woo-5e'),
                'missingWarehouse' => __('Select a warehouse before applying this bulk action.', 'woo-5e'),
            ],
        ]);
    }

    public static function register_bulk_actions($actions)
    {
        if (self::get_import_mode() !== 'manual') {
            return $actions;
        }

        $actions['w5e_send_to_5e_bulk'] = __('Send to 5E (select warehouse)', 'woo-5e');
        return $actions;
    }

    public static function handle_bulk_actions($redirect_to, $action, $order_ids)
    {
        if ($action !== 'w5e_send_to_5e_bulk') {
            return $redirect_to;
        }

        if (self::get_import_mode() !== 'manual') {
            return self::redirect_with_bulk_notice($redirect_to, [
                'type' => 'error',
                'lines' => [__('Manual import is not enabled.', 'woo-5e')],
            ]);
        }

        $warehouseId = isset($_REQUEST['w5e_bulk_warehouse_id']) ? sanitize_text_field((string)wp_unslash($_REQUEST['w5e_bulk_warehouse_id'])) : '';
        $warehouseOptions = self::get_warehouse_options();
        $allowed = array_filter(array_map(function ($row) {
            return isset($row['value']) ? $row['value'] : '';
        }, $warehouseOptions));

        if ($warehouseId === '' || !in_array($warehouseId, $allowed, true)) {
            return self::redirect_with_bulk_notice($redirect_to, [
                'type' => 'error',
                'lines' => [__('Select a warehouse before applying this bulk action.', 'woo-5e')],
            ]);
        }

        $sent = [];
        $skippedNot5e = [];
        $skippedSent = [];
        $skippedWaiting = [];
        $missing = [];
        $failed = [];

        foreach ($order_ids as $order_id) {
            $order_id = absint($order_id);
            if (!$order_id) {
                continue;
            }

            $order = wc_get_order($order_id);
            if (!$order) {
                $missing[] = $order_id;
                continue;
            }

            if (!self::get_w5e_shipping_item($order)) {
                $skippedNot5e[] = $order_id;
                continue;
            }

            if (self::already_sent($order)) {
                $skippedSent[] = $order_id;
                continue;
            }

            if (self::is_bacs_waiting_payment($order, $order->get_status())) {
                $skippedWaiting[] = $order_id;
                continue;
            }

            $order->update_meta_data(self::META_WAREHOUSE_ID, $warehouseId);
            $order->save_meta_data();

            $result = self::export_order($order, true, $warehouseId);
            if (is_wp_error($result)) {
                $failed[] = [
                    'id' => $order_id,
                    'message' => $result->get_error_message(),
                ];
                continue;
            }

            if ($order->get_status() === 'w5e_processing') {
                $order->set_status('processing');
                $order->save();
            }

            $sent[] = $order_id;
        }

        $lines = [];
        $hasErrors = false;
        if (!empty($sent)) {
            $lines[] = sprintf(__('Sent to 5E: %s', 'woo-5e'), self::format_order_list($sent));
        }
        if (!empty($skippedNot5e)) {
            $hasErrors = true;
            $lines[] = sprintf(__('Skipped (not 5E shipping): %s', 'woo-5e'), self::format_order_list($skippedNot5e));
        }
        if (!empty($skippedSent)) {
            $hasErrors = true;
            $lines[] = sprintf(__('Skipped (already sent): %s', 'woo-5e'), self::format_order_list($skippedSent));
        }
        if (!empty($skippedWaiting)) {
            $hasErrors = true;
            $lines[] = sprintf(__('Skipped (awaiting payment): %s', 'woo-5e'), self::format_order_list($skippedWaiting));
        }
        if (!empty($missing)) {
            $hasErrors = true;
            $lines[] = sprintf(__('Skipped (order not found): %s', 'woo-5e'), self::format_order_list($missing));
        }
        if (!empty($failed)) {
            $hasErrors = true;
            foreach ($failed as $row) {
                $message = sanitize_text_field((string)($row['message'] ?? ''));
                $lines[] = sprintf(__('Failed #%1$s: %2$s', 'woo-5e'), absint($row['id']), $message);
            }
        }

        if (empty($lines)) {
            $lines[] = __('No orders processed.', 'woo-5e');
            $hasErrors = true;
        }

        return self::redirect_with_bulk_notice($redirect_to, [
            'type' => $hasErrors ? 'warning' : 'success',
            'lines' => $lines,
        ]);
    }

    public static function render_bulk_notice()
    {
        if (empty($_GET['w5e_bulk_notice'])) {
            return;
        }
        $key = sanitize_text_field((string)$_GET['w5e_bulk_notice']);
        if ($key === '') {
            return;
        }

        $data = get_transient($key);
        if (!is_array($data)) {
            return;
        }
        delete_transient($key);

        $lines = isset($data['lines']) && is_array($data['lines']) ? $data['lines'] : [];
        if (empty($lines)) {
            return;
        }

        $type = isset($data['type']) ? $data['type'] : 'success';
        $class = 'notice notice-success';
        if ($type === 'error') {
            $class = 'notice notice-error';
        } elseif ($type === 'warning') {
            $class = 'notice notice-warning';
        }

        echo '<div class="' . esc_attr($class) . '">';
        foreach ($lines as $line) {
            echo '<p>' . esc_html($line) . '</p>';
        }
        echo '</div>';
    }

    public static function render_missing_warehouse_notice()
    {
        if (self::get_import_mode() !== 'manual') {
            return;
        }

        if (!current_user_can('manage_woocommerce')) {
            return;
        }

        if (!function_exists('get_current_screen')) {
            return;
        }

        $screen = get_current_screen();
        if (!$screen || empty($screen->id)) {
            return;
        }

        $isOrderEdit = false;
        if ($screen->id === 'shop_order') {
            $isOrderEdit = true;
        } elseif ($screen->id === 'woocommerce_page_wc-orders') {
            $action = isset($_GET['action']) ? sanitize_key($_GET['action']) : '';
            $isOrderEdit = $action === 'edit';
        }

        if (!$isOrderEdit) {
            return;
        }

        $orderId = self::resolve_order_id_from_context(null);
        if (!$orderId) {
            return;
        }

        $order = wc_get_order($orderId);
        if (!$order) {
            return;
        }

        if (!self::get_w5e_shipping_item($order)) {
            return;
        }

        $warehouseId = sanitize_text_field((string)$order->get_meta(self::META_WAREHOUSE_ID));
        if ($warehouseId !== '') {
            return;
        }

        if ($order->get_status() !== 'w5e_processing') {
            return;
        }

        echo '<div class="notice notice-warning"><p>' . esc_html__(
            "Order is in status 'Awaiting warehouse selection' because no warehouse is selected. Select a warehouse and save the order to send it to 5E.",
            'woo-5e'
        ) . '</p></div>';
    }

    private static function format_order_list($ids)
    {
        $ids = array_filter(array_map('absint', (array)$ids));
        if (empty($ids)) {
            return '';
        }
        $labels = array_map(function ($id) {
            return '#' . $id;
        }, $ids);
        return implode(', ', $labels);
    }

    private static function redirect_with_bulk_notice($redirect_to, $data)
    {
        $key = 'w5e_bulk_notice_' . get_current_user_id() . '_' . time();
        set_transient($key, $data, 60);
        return add_query_arg('w5e_bulk_notice', $key, $redirect_to);
    }

    public static function register_order_meta_box($post = null)
    {
        if (self::get_import_mode() !== 'manual') {
            return;
        }

        if (!function_exists('add_meta_box')) {
            return;
        }

        $screenId = 'shop_order';
        if (function_exists('get_current_screen')) {
            $screen = get_current_screen();
            if ($screen && !empty($screen->id)) {
                $screenId = $screen->id;
            }
        }

        add_meta_box(
            'w5e-import',
            __('5E Import', 'woo-5e'),
            [__CLASS__, 'render_order_meta_box'],
            $screenId,
            'side',
            'high'
        );
    }

    public static function register_tracking_meta_box($post = null)
    {
        if (!function_exists('add_meta_box')) {
            return;
        }

        $screenId = 'shop_order';
        if (function_exists('get_current_screen')) {
            $screen = get_current_screen();
            if ($screen && !empty($screen->id)) {
                $screenId = $screen->id;
            }
        }

        add_meta_box(
            'w5e-tracking',
            __('5E Tracking', 'woo-5e'),
            [__CLASS__, 'render_tracking_meta_box'],
            $screenId,
            'side',
            'high'
        );
    }

    public static function reorder_order_meta_boxes($post = null)
    {
        if (self::get_import_mode() !== 'manual') {
            return;
        }

        if (!function_exists('get_current_screen')) {
            return;
        }

        $screen = get_current_screen();
        if (!$screen || empty($screen->id)) {
            return;
        }

        global $wp_meta_boxes;
        $screenId = $screen->id;

        if (
            empty($wp_meta_boxes[$screenId]['side']['high']) ||
            !is_array($wp_meta_boxes[$screenId]['side']['high']) ||
            empty($wp_meta_boxes[$screenId]['side']['high']['w5e-import'])
        ) {
            return;
        }

        $boxes = $wp_meta_boxes[$screenId]['side']['high'];
        $our   = $boxes['w5e-import'];
        unset($boxes['w5e-import']);

        $new = [];
        foreach ($boxes as $id => $box) {
            $new[$id] = $box;
            if ($id === 'woocommerce-order-actions') {
                $new['w5e-import'] = $our;
            }
        }

        if (!isset($new['w5e-import'])) {
            $new = ['w5e-import' => $our] + $boxes;
        }

        $wp_meta_boxes[$screenId]['side']['high'] = $new;
    }

    public static function reorder_tracking_meta_boxes($post = null)
    {
        if (!function_exists('get_current_screen')) {
            return;
        }

        $screen = get_current_screen();
        if (!$screen || empty($screen->id)) {
            return;
        }

        global $wp_meta_boxes;
        $screenId = $screen->id;

        if (
            empty($wp_meta_boxes[$screenId]['side']['high']) ||
            !is_array($wp_meta_boxes[$screenId]['side']['high']) ||
            empty($wp_meta_boxes[$screenId]['side']['high']['w5e-tracking'])
        ) {
            return;
        }

        $boxes = $wp_meta_boxes[$screenId]['side']['high'];
        $trackingBox = $boxes['w5e-tracking'];
        unset($boxes['w5e-tracking']);

        $new = [];
        $inserted = false;
        $hasImport = isset($boxes['w5e-import']);

        foreach ($boxes as $id => $box) {
            $new[$id] = $box;
            if ($hasImport && $id === 'w5e-import') {
                $new['w5e-tracking'] = $trackingBox;
                $inserted = true;
            } elseif (!$hasImport && $id === 'woocommerce-order-actions') {
                $new['w5e-tracking'] = $trackingBox;
                $inserted = true;
            }
        }

        if (!$inserted) {
            $new = ['w5e-tracking' => $trackingBox] + $boxes;
        }

        $wp_meta_boxes[$screenId]['side']['high'] = $new;
    }

    private static function resolve_order_id_from_context($post)
    {
        if ($post instanceof WP_Post) {
            return (int)$post->ID;
        }
        if (is_numeric($post)) {
            return (int)$post;
        }
        if (isset($_GET['id'])) {
            $id = absint($_GET['id']);
            if ($id) return $id;
        }
        if (isset($_GET['post'])) {
            $id = absint($_GET['post']);
            if ($id) return $id;
        }
        return 0;
    }

    private static function get_cached_warehouses()
    {
        $cache = get_option('w5e_warehouses_cache', []);
        return is_array($cache) ? $cache : [];
    }

    private static function get_warehouse_options()
    {
        $options = [];
        foreach (self::get_cached_warehouses() as $row) {
            if (!is_array($row)) {
                continue;
            }
            $clientWid = isset($row['ClientWarehouseID']) ? sanitize_text_field($row['ClientWarehouseID']) : '';
            $wid = isset($row['WarehouseID']) ? sanitize_text_field($row['WarehouseID']) : '';
            $value = $clientWid !== '' ? $clientWid : $wid;
            if ($value === '') {
                continue;
            }

            $name = isset($row['WarehouseName']) ? sanitize_text_field($row['WarehouseName']) : '';
            $town = isset($row['WarehouseTown']) ? sanitize_text_field($row['WarehouseTown']) : '';
            $addr = isset($row['WarehouseAddress']) ? sanitize_text_field($row['WarehouseAddress']) : '';
            $active = !empty($row['WarehouseStatus']);

            $label = $name !== '' ? $name : $value;
            if ($town !== '') {
                $label .= ' (' . $town . ')';
            }
            if ($addr !== '') {
                $label .= ' — ' . $addr;
            }
            if (!$active) {
                $label .= ' [' . __('inactive', 'woo-5e') . ']';
            }

            $options[] = [
                'value' => $value,
                'label' => $label,
            ];
        }
        return $options;
    }

    private static function get_cached_couriers()
    {
        $cache = get_option('w5e_couriers_cache', []);
        return is_array($cache) ? $cache : [];
    }

    private static function get_default_courier_key()
    {
        $enabled = get_option('w5e_enabled_partners', []);
        if (!is_array($enabled)) {
            return '';
        }
        foreach ($enabled as $key => $row) {
            if (!empty($row['default'])) {
                return (string)$key;
            }
        }
        return '';
    }

    public static function render_order_meta_box($post)
    {
        if (!current_user_can('manage_woocommerce')) {
            echo '<p>' . esc_html__('Not allowed.', 'woo-5e') . '</p>';
            return;
        }

        $orderId = self::resolve_order_id_from_context($post);
        $order   = $orderId ? wc_get_order($orderId) : null;

        if (!$order) {
            echo '<p>' . esc_html__('Order not found.', 'woo-5e') . '</p>';
            return;
        }

        $w5eShippingItem = self::get_w5e_shipping_item($order);
        if (!$w5eShippingItem) {
            echo '<p>' . esc_html__('This order is not using 5E shipping.', 'woo-5e') . '</p>';

            $couriers = self::get_cached_couriers();
            if (empty($couriers)) {
                $settingsUrl = admin_url('admin.php?page=wc-settings&tab=shipping&section=w5e');
                echo '<p>' . esc_html__('No couriers cached.', 'woo-5e') . '</p>';
                echo '<p><a href="' . esc_url($settingsUrl) . '">' . esc_html__('Go to 5E settings to fetch couriers', 'woo-5e') . '</a></p>';
                return;
            }

            $defaultCourier = self::get_default_courier_key();
            echo '<p><label for="w5e-order-courier"><strong>' . esc_html__('Courier', 'woo-5e') . '</strong></label><br />';
            echo '<select id="w5e-order-courier" name="w5e_order_courier_key" style="width:100%;">';
            echo '<option value="">' . esc_html__('— Select —', 'woo-5e') . '</option>';
            foreach ($couriers as $row) {
                if (!is_array($row)) continue;
                $partnerId = isset($row['PartnerID']) ? intval($row['PartnerID']) : 0;
                if (!$partnerId) continue;
                $env = isset($row['Env']) ? sanitize_text_field((string)$row['Env']) : '';
                $name = isset($row['CourierName']) ? sanitize_text_field((string)$row['CourierName']) : '';
                $key = $partnerId . '|' . $env;
                $label = $name !== '' ? $name : $key;
                if ($env !== '') {
                    $label .= ' (' . $env . ')';
                }
                echo '<option value="' . esc_attr($key) . '" ' . selected($defaultCourier, $key, false) . '>' . esc_html($label) . '</option>';
            }
            echo '</select></p>';

            echo '<p>';
            echo '<button type="button" class="button" id="w5e-convert-to-5e-btn" data-order-id="' . esc_attr($orderId) . '">' . esc_html__('Switch to 5E shipping', 'woo-5e') . '</button>';
            echo '<span id="w5e-convert-to-5e-status" style="display:block;margin-top:6px;"></span>';
            echo '</p>';
            return;
        }

        $warehouseOptions = self::get_warehouse_options();
        $selected   = sanitize_text_field((string)$order->get_meta(self::META_WAREHOUSE_ID));

        echo '<div class="notice notice-warning inline" style="margin:0 0 10px 0;"><p>' . esc_html__(
            "Please select a warehouse and save changes. In manual mode, new orders remain in 'Awaiting warehouse selection' until you send them to 5E; after a successful send the status moves to 'Processing'. Warehouse corrections can be made later in the 5E dashboard.",
            'woo-5e'
        ) . '</p></div>';

        if (empty($warehouseOptions)) {
            $settingsUrl = admin_url('admin.php?page=wc-settings&tab=shipping&section=w5e');
            echo '<p>' . esc_html__('No warehouses cached.', 'woo-5e') . '</p>';
            echo '<p><a href="' . esc_url($settingsUrl) . '">' . esc_html__('Go to 5E settings to fetch warehouses', 'woo-5e') . '</a></p>';
            return;
        }

        echo '<p><label for="w5e-order-warehouse"><strong>' . esc_html__('Warehouse', 'woo-5e') . '</strong></label><br />';
        echo '<select id="w5e-order-warehouse" name="w5e_order_warehouse_id" style="width:100%;">';
        echo '<option value="">' . esc_html__('— Select —', 'woo-5e') . '</option>';
        foreach ($warehouseOptions as $row) {
            $value = isset($row['value']) ? $row['value'] : '';
            $label = isset($row['label']) ? $row['label'] : $value;
            if ($value === '') {
                continue;
            }
            echo '<option value="' . esc_attr($value) . '" ' . selected($selected, $value, false) . '>' . esc_html($label) . '</option>';
        }
        echo '</select></p>';

        echo '<p>';
        echo '<button type="button" class="button button-primary" id="w5e-send-to-5e-btn" data-order-id="' . esc_attr($orderId) . '">' . esc_html__('Send to 5E / Last Mile', 'woo-5e') . '</button>';
        echo '<span id="w5e-send-to-5e-status" style="display:block;margin-top:6px;"></span>';
        echo '</p>';

        $lastError = sanitize_text_field((string)$order->get_meta(self::META_LAST_ERROR));
        if ($lastError !== '') {
            echo '<p style="color:#b32d2e;"><strong>' . esc_html__('Last error:', 'woo-5e') . '</strong> ' . esc_html($lastError) . '</p>';
        }
    }

    public static function render_tracking_meta_box($post)
    {
        if (!current_user_can('manage_woocommerce')) {
            echo '<p>' . esc_html__('Not allowed.', 'woo-5e') . '</p>';
            return;
        }

        $orderId = self::resolve_order_id_from_context($post);
        $order   = $orderId ? wc_get_order($orderId) : null;

        if (!$order) {
            echo '<p>' . esc_html__('Order not found.', 'woo-5e') . '</p>';
            return;
        }

        $trackingRaw = $order->get_meta('_w5e_tracking_number');
        $trackingPrefix = $order->get_meta('_w5e_tracking_prefix');
        if (function_exists('w5e_format_tracking_number')) {
            $tracking = w5e_format_tracking_number($trackingRaw, $trackingPrefix);
        } else {
            $tracking = trim((string)$trackingPrefix . ' ' . (string)$trackingRaw);
        }

        if ($trackingPrefix !== '' && $trackingRaw !== '') {
            echo '<p><strong>' . esc_html__('Prefix', 'woo-5e') . '</strong></p>';
            echo '<p><code>' . esc_html($trackingPrefix) . '</code></p>';
            echo '<p><strong>' . esc_html__('Tracking number', 'woo-5e') . '</strong></p>';
            echo '<p><code>' . esc_html($trackingRaw) . '</code></p>';
            return;
        }

        if ($tracking !== '') {
            echo '<p><strong>' . esc_html__('Tracking number', 'woo-5e') . '</strong></p>';
            echo '<p><code>' . esc_html($tracking) . '</code></p>';
            return;
        }

        if (!self::get_w5e_shipping_item($order)) {
            echo '<p>' . esc_html__('This order is not using 5E shipping.', 'woo-5e') . '</p>';
            return;
        }

        echo '<p>' . esc_html__('Broj za pracenje jos nije primljen.', 'woo-5e') . '</p>';
    }

    public static function ajax_send_order_to_5e()
    {
        check_ajax_referer('w5e_order_send', 'nonce');

        if (!current_user_can('manage_woocommerce')) {
            wp_send_json_error(['message' => __('Not allowed.', 'woo-5e')]);
        }

        if (self::get_import_mode() !== 'manual') {
            wp_send_json_error(['message' => __('Manual import is not enabled.', 'woo-5e')]);
        }

        $orderId = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0;
        $warehouseId = isset($_POST['warehouse_id']) ? sanitize_text_field((string)wp_unslash($_POST['warehouse_id'])) : '';
        if (!$orderId) {
            wp_send_json_error(['message' => __('Missing order ID.', 'woo-5e')]);
        }
        if ($warehouseId === '') {
            wp_send_json_error(['message' => __('Select a warehouse first.', 'woo-5e')]);
        }

        $order = wc_get_order($orderId);
        if (!$order) {
            wp_send_json_error(['message' => __('Order not found.', 'woo-5e')]);
        }
        if (!self::get_w5e_shipping_item($order)) {
            wp_send_json_error(['message' => __('Order is not using a 5E shipping rate.', 'woo-5e')]);
        }

        self::log_debug('AJAX send order to 5E', [
            'order_id'    => $order->get_id(),
            'warehouseId' => $warehouseId,
        ]);

        if (self::is_bacs_waiting_payment($order, $order->get_status())) {
            wp_send_json_error(['message' => __('Awaiting bank transfer payment confirmation.', 'woo-5e')]);
        }

        $order->update_meta_data(self::META_WAREHOUSE_ID, $warehouseId);
        $order->save_meta_data();

        $result = self::export_order($order, true, $warehouseId);
        if (is_wp_error($result)) {
            wp_send_json_error(['message' => $result->get_error_message()]);
        }

        if ($order->get_status() === 'w5e_processing') {
            $order->set_status('processing');
            $order->save();
        }

        self::log_debug('AJAX order sent to 5E', [
            'order_id' => $order->get_id(),
        ]);
        wp_send_json_success(['message' => __('Order sent to 5E / Last Mile.', 'woo-5e')]);
    }

    public static function ajax_convert_order_to_5e()
    {
        check_ajax_referer('w5e_order_send', 'nonce');

        if (!current_user_can('manage_woocommerce')) {
            wp_send_json_error(['message' => __('Not allowed.', 'woo-5e')]);
        }

        if (self::get_import_mode() !== 'manual') {
            wp_send_json_error(['message' => __('Manual import is not enabled.', 'woo-5e')]);
        }

        $orderId = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0;
        $courierKey = isset($_POST['courier_key']) ? sanitize_text_field((string)wp_unslash($_POST['courier_key'])) : '';
        if (!$orderId) {
            wp_send_json_error(['message' => __('Missing order ID.', 'woo-5e')]);
        }
        if ($courierKey === '') {
            wp_send_json_error(['message' => __('Select a courier first.', 'woo-5e')]);
        }

        $order = wc_get_order($orderId);
        if (!$order) {
            wp_send_json_error(['message' => __('Order not found.', 'woo-5e')]);
        }

        if (self::get_w5e_shipping_item($order)) {
            wp_send_json_error(['message' => __('Order already uses 5E shipping.', 'woo-5e')]);
        }

        self::log_debug('AJAX convert order to 5E', [
            'order_id'   => $order->get_id(),
            'courierKey' => $courierKey,
        ]);

        $couriers = self::get_cached_couriers();
        $selectedCourier = null;
        $parts = explode('|', $courierKey);
        if (count($parts) === 2) {
            $pid = absint($parts[0]);
            $env = sanitize_text_field((string)$parts[1]);
            foreach ($couriers as $row) {
                if (!is_array($row)) continue;
                $rowPid = isset($row['PartnerID']) ? intval($row['PartnerID']) : 0;
                $rowEnv = isset($row['Env']) ? (string)$row['Env'] : '';
                if ($rowPid === $pid && strcasecmp($rowEnv, $env) === 0) {
                    $selectedCourier = [
                        'partner_id' => $rowPid,
                        'env'        => sanitize_text_field($rowEnv),
                        'name'       => isset($row['CourierName']) ? sanitize_text_field((string)$row['CourierName']) : '',
                    ];
                    break;
                }
            }
        }
        if (!$selectedCourier) {
            wp_send_json_error(['message' => __('Selected courier is not available.', 'woo-5e')]);
        }

        $shippingTotal = 0.0;
        $shippingTaxes = ['total' => [], 'subtotal' => []];
        foreach ($order->get_items('shipping') as $itemId => $item) {
            $shippingTotal += (float)$item->get_total();
            $taxes = $item->get_taxes();
            if (is_array($taxes)) {
                foreach (['total', 'subtotal'] as $bucket) {
                    if (empty($taxes[$bucket]) || !is_array($taxes[$bucket])) {
                        continue;
                    }
                    foreach ($taxes[$bucket] as $rateId => $amount) {
                        if (!isset($shippingTaxes[$bucket][$rateId])) {
                            $shippingTaxes[$bucket][$rateId] = 0;
                        }
                        $shippingTaxes[$bucket][$rateId] += (float)$amount;
                    }
                }
            }
            $order->remove_item($itemId);
        }

        $label = $selectedCourier['name'] !== '' ? sprintf(__('Delivery - %s', 'woo-5e'), $selectedCourier['name']) : __('Delivery (5E)', 'woo-5e');
        if ($selectedCourier['env'] !== '') {
            $label .= ' (' . $selectedCourier['env'] . ')';
        }

        $newItem = new WC_Order_Item_Shipping();
        $newItem->set_method_id('w5e');
        $newItem->set_instance_id(0);
        $newItem->set_method_title($label);
        $newItem->set_total($shippingTotal);
        if (!empty($shippingTaxes['total']) || !empty($shippingTaxes['subtotal'])) {
            $newItem->set_taxes($shippingTaxes);
        }
        $newItem->add_meta_data('w5e_key', $courierKey, true);
        $newItem->add_meta_data('w5e_partner_id', $selectedCourier['partner_id'], true);
        $newItem->add_meta_data('w5e_env', $selectedCourier['env'], true);
        $order->add_item($newItem);
        $order->calculate_totals(false);
        $order->save();

        self::maybe_set_manual_processing_status($order->get_id(), $order->get_status(), $order->get_status(), $order);

        self::log_debug('AJAX order converted to 5E', [
            'order_id' => $order->get_id(),
        ]);
        wp_send_json_success(['message' => __('Order shipping switched to 5E.', 'woo-5e')]);
    }

    public static function save_order_warehouse_meta($orderId, $order)
    {
        if (self::get_import_mode() !== 'manual') {
            return;
        }

        if (!$order instanceof WC_Order) {
            $order = wc_get_order($orderId);
        }
        if (!$order) {
            return;
        }

        if (!current_user_can('manage_woocommerce')) {
            return;
        }

        self::log_debug('Hook woocommerce_process_shop_order_meta', [
            'order_id' => $orderId,
        ]);

        $warehouseId = isset($_POST['w5e_order_warehouse_id']) ? sanitize_text_field((string)wp_unslash($_POST['w5e_order_warehouse_id'])) : '';
        if ($warehouseId !== '') {
            $order->update_meta_data(self::META_WAREHOUSE_ID, $warehouseId);
        } else {
            $order->delete_meta_data(self::META_WAREHOUSE_ID);
        }
        $order->save_meta_data();

        if ($warehouseId === '') {
            return;
        }

        if (self::already_sent($order)) {
            return;
        }

        if (!self::should_export_on_status($order->get_status())) {
            return;
        }

        $result = self::export_order($order);
        if (is_wp_error($result)) {
            self::log_error($order->get_id(), '5E export failed: ' . $result->get_error_message());
        }
    }
}
