<?php

namespace BWDK\Integrations\Digify\Routes;

use BWDK\Enums\BWDK_OrderStatus;
use BWDK\Integrations\Digify\Client;
use BWDK\Support\Constant;
use BWDK\Support\OrderNoteHelper;
use BWDK\Support\PhoneHelper;
use BWDK\Support\ReturnUrlHelper;
use BWDK\Support\WooCompatibility;
use WC_Order_Item_Coupon;
use WC_Order_Item_Fee;
use WC_Order_Item_Shipping;
use WC_Session_Handler;
use WP_REST_Request;
use WP_REST_Response;
use WC_Order;


class CallbackRoute
{
    use OrderNoteHelper;
    protected string $ns = 'bwdk/v1';

    protected string $route = '/digify/callback';

    public function register(): void
    {
/*        add_action('rest_api_init', function () {
            register_rest_route($this->ns, $this->route, [
                'methods' => 'GET',
                'callback' => [$this, 'handle'],
                'permission_callback' => '__return_true',

            ]);
        });*/


            add_rewrite_rule(
                '^bwdk-callback/?$',
                'index.php?__bwdk_callback=1',
                'top'
            );


        add_filter('query_vars', function($vars) {
            $vars[] = '__bwdk_callback';
            return $vars;
        });

        add_action('template_redirect', function() {
            if ( get_query_var('__bwdk_callback') ) {

                $this->handle();
                exit;
            }
        });



    }


    public function handle( ): WP_REST_Response
    {
        // PART 1: HIGH-VISIBILITY diagnostic logging
        $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
        if ($logger) {
            $logger->info(
                sprintf(
                    'BWDK RETURN TRACE: HIT /bwdk-callback - REQUEST_URI=%s query_string=%s GET_keys=%s POST_keys=%s timestamp=%s',
                    $_SERVER['REQUEST_URI'] ?? 'N/A',
                    $_SERVER['QUERY_STRING'] ?? 'N/A',
                    wp_json_encode(array_keys($_GET)),
                    wp_json_encode(array_keys($_POST)),
                    current_time('mysql')
                ),
                ['source' => 'bwdk_return_trace']
            );
        }

        // Normalize callback parameters - check both GET and POST
        $req_get = $_GET;
        $req_post = $_POST;
        $all_params = array_merge($req_get, $req_post);

        // Normalize order UUID from multiple possible keys
        $order_uuid = '';
        $uuid_keys = ['order_uuid', 'order-uuid', 'orderUuid'];
        foreach ($uuid_keys as $key) {
            if (!empty($all_params[$key])) {
                $order_uuid = (string) $all_params[$key];
                break;
            }
        }

        // Normalize reason from multiple possible keys
        $reason = '';
        $reason_keys = ['reason', 'failure_reason'];
        foreach ($reason_keys as $key) {
            if (!empty($all_params[$key])) {
                $reason = (string) $all_params[$key];
                break;
            }
        }

        $phone_number = (string)($all_params["phone_number"] ?? '');
        $merchant_order_id = (int)($all_params["merchant_order_id"] ?? 0);

        // Log normalized parameters with high-visibility trace
        if ($logger) {
            $logger->info(
                sprintf(
                    'BWDK RETURN TRACE: /bwdk-callback params normalized - incoming_keys=%s normalized_order_uuid=%s normalized_reason=%s merchant_order_id=%d timestamp=%s',
                    wp_json_encode(array_keys($all_params)),
                    $order_uuid ?: 'EMPTY',
                    $reason ?: 'EMPTY',
                    $merchant_order_id,
                    current_time('mysql')
                ),
                ['source' => 'bwdk_return_trace']
            );
        }

        $order = null;
        $order_lookup_method = '';

        // Try to find order by merchant_order_id first (if provided)
        if ($merchant_order_id) {
            $order = wc_get_order($merchant_order_id);
            if ($order instanceof WC_Order) {
                // Validate it belongs to BWDK flow
                $bwdk_uuid = $order->get_meta(\BWDK\Support\Constant::META_BWDK_UUID);
                if (!empty($bwdk_uuid)) {
                    $order_lookup_method = 'merchant_order_id_with_bwdk_meta';
                } else {
                    // Order found but no BWDK meta - might not be BWDK order
                    $order = null;
                }
            }
        }

        // If order not found and we have UUID, try lookup by UUID
        if (!$order instanceof WC_Order && !empty($order_uuid)) {
            $orders = wc_get_orders([
                'limit' => 1,
                'meta_key' => \BWDK\Support\Constant::META_BWDK_UUID,
                'meta_value' => $order_uuid,
                'return' => 'objects',
            ]);

            if (!empty($orders)) {
                $order = reset($orders);
                $order_lookup_method = 'order_uuid_meta_lookup';
            }
        }

        // Log order lookup result
        if ($logger) {
            if ($order instanceof WC_Order) {
                $logger->info(
                    sprintf(
                        'BWDK Order lookup SUCCESS: order_id=%d lookup_method=%s order_uuid=%s order_status=%s',
                        $order->get_id(),
                        $order_lookup_method ?: 'unknown',
                        $order_uuid ?: 'N/A',
                        $order->get_status()
                    ),
                    ['source' => 'bwdk_callback']
                );
            } else {
                $logger->warning(
                    sprintf(
                        'BWDK Order lookup FAILED: order_uuid=%s merchant_order_id=%d reason=%s',
                        $order_uuid ?: 'EMPTY',
                        $merchant_order_id,
                        $reason ?: 'EMPTY'
                    ),
                    ['source' => 'bwdk_callback']
                );
            }
        }


        if (!$order instanceof WC_Order) {
            return new WP_REST_Response([
                'ok' => false,
                'error' => 'order_not_found',
            ], 404);
        }

        if (!empty($order_uuid)) {
            $order->update_meta_data(\BWDK\Support\Constant::META_BWDK_UUID, $order_uuid);
        }

        if (!empty($phone_number)) {
            $current_phone = $order->get_billing_phone();


            if (empty($current_phone)) {
                $order->set_billing_phone($phone_number);
            } else {
                $order->set_billing_phone($phone_number);
            }
        }

        // Call Digify API to verify status (only if we have UUID)
        $raw = [];
        if (!empty($order_uuid)) {
            $client = new Client();
            $data = $client->orderGetStatus($order_uuid);
            $raw = $data['raw'] ?? [];
        } else {
            // UUID missing - log and skip verification
            if ($logger) {
                $logger->warning(
                    'BWDK Callback: order_uuid missing, cannot verify with Digify API',
                    ['source' => 'bwdk_callback']
                );
            }
        }

        // Log callback received
        if ($logger) {
            $logger->info(
                sprintf(
                    'BWDK Callback received: order_id=%d order_uuid=%s reason=%s',
                    $order->get_id(),
                    $order_uuid ?: 'EMPTY',
                    $reason ?: 'EMPTY'
                ),
                ['source' => 'bwdk_callback']
            );
        }

        // Handle cancel-on-return for reason=back_from_shipping_page
        if ($reason === 'back_from_shipping_page') {
            // If UUID is missing, log and stop (cannot verify)
            if (empty($order_uuid)) {
                if ($logger) {
                    $logger->warning(
                        sprintf(
                            'BWDK Cancel-on-return: order_uuid missing, cannot verify status. order_id=%d reason=%s',
                            $order->get_id(),
                            $reason
                        ),
                        ['source' => 'bwdk_callback']
                    );
                }
            } else {
                // We have UUID - verify with Digify API
                $remote_status = isset($raw['status']) ? (int) $raw['status'] : 0;
                $is_paid = isset($raw['is_paid']) ? (bool) $raw['is_paid'] : false;
                $paid_status = 7; // BWDK_OrderStatus::PAID_BY_USER

                if ($logger) {
                    $logger->info(
                        sprintf(
                            'BWDK Cancel-on-return check: order_id=%d order_uuid=%s remote_status=%d is_paid=%s',
                            $order->get_id(),
                            $order_uuid,
                            $remote_status,
                            $is_paid ? 'true' : 'false'
                        ),
                        ['source' => 'bwdk_callback']
                    );
                }

                // Only cancel if not paid
                if ($remote_status !== $paid_status && !$is_paid) {
                    $old_status = $order->get_status();
                    $cancellable_statuses = ['pending', 'on-hold', 'failed'];
                    
                    // Check if order is in cancellable status and not already cancelled
                    if (in_array($old_status, $cancellable_statuses, true) && $order->get_status() !== 'cancelled') {
                        // Cancel the order
                        $order->update_status('cancelled', __('سفارش به دلیل بازگشت از صفحه ارسال لغو شد.', 'bwdk'));
                        $order->save();

                        if ($logger) {
                            $logger->info(
                                sprintf(
                                    'BWDK Order cancelled on return: order_id=%d old_status=%s new_status=cancelled order_uuid=%s',
                                    $order->get_id(),
                                    $old_status,
                                    $order_uuid
                                ),
                                ['source' => 'bwdk_callback']
                            );
                        }

                        // Clear active BWDK flow session/meta
                        if (function_exists('WC') && WC()->session) {
                            WC()->session->__unset('bwdk_return_to');
                            WC()->session->__unset('order_awaiting_payment');
                        }
                    } else {
                        if ($logger) {
                            $logger->info(
                                sprintf(
                                    'BWDK Cancel-on-return: order not cancelled - order_id=%d current_status=%s (not in cancellable statuses or already cancelled)',
                                    $order->get_id(),
                                    $old_status
                                ),
                                ['source' => 'bwdk_callback']
                            );
                        }
                    }
                } else {
                    if ($logger) {
                        $logger->info(
                            sprintf(
                                'BWDK Cancel-on-return: order not cancelled - order_id=%d remote_status=%d is_paid=%s (order is paid)',
                                $order->get_id(),
                                $remote_status,
                                $is_paid ? 'true' : 'false'
                            ),
                            ['source' => 'bwdk_callback']
                        );
                    }
                }
            }
        }


        if (json_last_error() !== JSON_ERROR_NONE || empty($raw)) {
            return new WP_REST_Response(['ok' => false, 'error' => 'invalid_api_response'], 502);
        }


        if (!empty($raw)) {
            $order->update_meta_data(\BWDK\Support\Constant::META_BWDK_JSON_CB, $raw);

            // Also persist flattened reference code and Digify order id for better admin search.
            $reference_code = isset($raw['reference_code']) ? (string) $raw['reference_code'] : '';
            if ($reference_code !== '') {
                $order->update_meta_data('_bwdk_reference_code', $reference_code);
            }
            $digify_id = isset($raw['id']) ? (string) $raw['id'] : '';
            if ($digify_id !== '') {
                $order->update_meta_data('_bwdk_digify_order_id', $digify_id);
            }

            // Build aggregated search tokens for reliable admin search
            $ref = $reference_code;
            $digify_id_meta = $digify_id;

            // Normalize helpers
            $norm = static function($s) {
                $s = trim((string)$s);
                $s = preg_replace('/\s+/', '', $s);
                return $s;
            };

            // Build tokens: original + variants (case-insensitive search uses LIKE)
            $tokens = [];
            if ($ref !== '') {
                $ref_trimmed = trim($ref);
                $ref_norm = preg_replace('/\s+/', '', $ref_trimmed);
                $ref_upper = strtoupper($ref_norm);      // e.g. BD-61564156
                $ref_lower = strtolower($ref_norm);      // e.g. bd-61564156
                $ref_stripped = preg_replace('/^BD[-:]?/i', '', $ref_upper); // 61564156
                
                // Add all variants
                $tokens[] = $ref_trimmed;      // Original with spaces if any
                $tokens[] = $ref_norm;        // Original without spaces
                $tokens[] = $ref_upper;       // Uppercase
                $tokens[] = $ref_lower;       // Lowercase
                if ($ref_stripped !== $ref_upper) {
                    $tokens[] = $ref_stripped; // Without BD prefix
                }
            }
            if ($digify_id_meta !== '') {
                $id_norm = $norm($digify_id_meta);             // 122406
                $tokens[] = $id_norm;
                $tokens[] = 'id:' . $id_norm;
                $tokens[] = 'ID:' . $id_norm;
            }

            // Dedup and join to a single searchable blob
            $tokens = array_values(array_unique(array_filter($tokens)));
            $blob = implode(' ', $tokens);

            // Store searchable blob meta
            if ($blob !== '') {
                $order->update_meta_data('_bwdk_search_tokens', $blob);
            }

            $this->log_payment_status_note($order, $raw);
            // ثبت نوت وضعیت از وب‌هوک/کالبک
            $this->bwdk_note_webhookStatus($order, $raw);
        }

        $dest = $raw['destination_address'] ?? null;

        $this->bwdk_save_receiver_meta_from_response( $order, $dest );

        if ($dest) {
            $apply = $this->bwdk_apply_address_shipping_billing($order, $dest, []);

            if (!$apply['ok']) {
                error_log('Dest map failed: ' . $apply['error']);
            }
        }

        // ---- BWDK: set shipping phone from Digify callback ----
        $receiver_phone_raw = $raw['destination_address']['receiver_phone'] ?? '';
        $has_receiver_phone = false;

        $normalize_e164 = static function($p) {
            $d = preg_replace('/\D+/', '', (string)$p);
            if ($d && $d[0] === '0') $d = '98' . substr($d, 1);  // 09.. -> 989..
            if ($d && strpos($d, '98') !== 0) $d = '98' . $d;
            return $d; // 98912...
        };
        $to_local_0 = static function($e164) {
            if (strpos($e164, '98') === 0) return '0' . substr($e164, 2);
            return $e164;
        };

        if (!empty($receiver_phone_raw)) {
            $e164  = $normalize_e164($receiver_phone_raw);
            $local = $to_local_0($e164);

            // save as meta + shipping_phone (for templates)
            $order->update_meta_data('_bwdk_receiver_phone_e164', $e164);

            if (method_exists($order, 'set_shipping_phone')) {
                $order->set_shipping_phone($local);
            } else {
                $order->update_meta_data('_shipping_phone', $local);
            }

            $order->add_order_note('BWDK: Shipping phone set from Digify receiver_phone → ' . $local);
            $order->save();
            $has_receiver_phone = true;
        }
        // ---- /BWDK shipping phone ----

        $ship = $raw['selected_shipping_method'] ?? null;
        if ($ship) {
            $this->bwdk_apply_shipping_method(
                $order,
                'digify_shipping:' . (string)$ship['id'],
                (string)($ship['name'] ?? ''),
                (float)($ship['cost'] ?? 0),
                [
                    'get_shipping_type_display' => $ship['get_shipping_type_display'] ?? null,
                    'cost' => $ship['cost'] ?? null,
                    'minimum_time_sending' => $ship['minimum_time_sending'] ?? null,
                    'maximum_time_sending' => $ship['maximum_time_sending'] ?? null,
                    'delivery_time_display' => $ship['delivery_time_display'] ?? null,
                    'delivery_time_range_display' => $ship['delivery_time_range_display'] ?? null,
                ],
                true,
                true
            );
        }

        $selected_packing = $raw['selected_packing'] ?? null;



        if ($selected_packing) {
            $this->bwdk_apply_packing($order,$selected_packing);
        }

        // Handle BWDK promotion discount
        $promotion_discount_amount = isset($raw['promotion_discount_amount']) ? (float) $raw['promotion_discount_amount'] : 0.0;
        $promotion_data = isset($raw['promotion_data']) && is_array($raw['promotion_data']) ? $raw['promotion_data'] : [];
        
        // Save promotion data to order meta
        if ($promotion_discount_amount > 0 || !empty($promotion_data)) {
            $order->update_meta_data('_bwdk_promotion_discount_amount', $promotion_discount_amount);
            $order->update_meta_data('_bwdk_promotion_data', $promotion_data);
            
            // Apply promotion as WooCommerce coupon item if valid
            if (!empty($promotion_data) && $promotion_discount_amount > 0) {
                $this->bwdk_apply_promotion_discount($order, $promotion_data, $promotion_discount_amount);
            }
        }

        // Handle BWDK shipping promotion discount (Free Shipping / Shipping discount)
        $shipping_promotion_discount_amount = isset($raw['shipping_promotion_discount_amount']) ? (float) $raw['shipping_promotion_discount_amount'] : 0.0;
        $shipping_promotion_data = isset($raw['shipping_promotion_data']) && is_array($raw['shipping_promotion_data']) ? $raw['shipping_promotion_data'] : [];
        
        // Save shipping promotion data to order meta (defensive: allow empty object/array)
        if ($shipping_promotion_discount_amount > 0 || !empty($shipping_promotion_data)) {
            $order->update_meta_data('_bwdk_shipping_promotion_discount_amount', $shipping_promotion_discount_amount);
            $order->update_meta_data('_bwdk_shipping_promotion_data', $shipping_promotion_data);
        }

        // Store final_amount and total_paid_amount from manager response
        $final_amount = isset($raw['final_amount']) ? (float) $raw['final_amount'] : 0.0;
        $total_paid_amount = isset($raw['total_paid_amount']) ? (float) $raw['total_paid_amount'] : 0.0;
        
        if ($final_amount > 0) {
            $order->update_meta_data('_bwdk_final_amount', $final_amount);
        }
        if ($total_paid_amount > 0) {
            $order->update_meta_data('_bwdk_total_paid_amount', $total_paid_amount);
            
            // Set order total to total_paid_amount (amount actually paid by user)
            // Convert from BWDK currency to store currency
            $total_paid_amount_store = WooCompatibility::convert_to_store_currency($total_paid_amount);
            $order->set_total($total_paid_amount_store);
        }

        $user = $raw['user'] ?? null;

        if ($user) {
            $order->set_billing_first_name($user['first_name'] ?? null);
            $order->set_billing_last_name($user['last_name'] ?? null);
            $order->set_billing_phone($user['phone_number'] ?? null);

            $order->set_shipping_first_name($user['first_name'] ?? null);
            $order->set_shipping_last_name($user['last_name'] ?? null);
            
            // Only set shipping phone from user if receiver_phone was not set from destination_address
            // This ensures that receiver_phone (from Digify address selection) takes priority
            if (!$has_receiver_phone) {
            $order->set_shipping_phone($user['phone_number'] ?? null);
            }
        }

        $status  = $raw['status'] ?? 2;
        $is_paid = isset($raw['is_paid']) ? (bool) $raw['is_paid'] : false;

        // Map BWDK statuses to WooCommerce statuses.
        // IMPORTANT: Never override admin-finalized statuses (e.g. completed/refunded),
        // and only auto-move to processing once after successful payment/verify.
        $map = [
            7 => 'processing',
            3 => 'cancelled',
            2 => 'pending',
            8 => 'failed',
            6 => 'cancelled',
            5 => 'cancelled', // EXPIRED
            9 => 'processing',
        ];

        $target_status = (!empty($status) && isset($map[$status])) ? (string) $map[$status] : '';
        if ($target_status !== '') {
            $current_woo_status = (string) $order->get_status();

            // Hard guard: do not override terminal/final statuses.
            if (!in_array($current_woo_status, ['completed', 'refunded'], true)) {
                // Avoid redundant updates (also helps prevent status-change loops).
                if ($current_woo_status !== $target_status) {
                    // Ensure this is a BWDK order.
                    $order_uuid = (string) $order->get_meta(\BWDK\Support\Constant::META_BWDK_UUID, true);

                    if ($order_uuid !== '') {
                        if ($target_status === 'processing') {
                            // Only auto-move to processing from early statuses and only once.
                            // Do NOT force processing just because _bwdk_verified_by_merchant exists.
                            if (function_exists('bwdk_is_admin_manual_status_change_to_final') && bwdk_is_admin_manual_status_change_to_final($order)) {
                                $order->add_order_note('BWDK debug: skipped auto-processing because admin manually changed status.', false);
                            } elseif (function_exists('bwdk_has_recent_admin_manual_status_lock') && bwdk_has_recent_admin_manual_status_lock($order, 300)) {
                                $order->add_order_note('BWDK debug: skipped auto-processing because admin manual status lock is active.', false);
                            } elseif (function_exists('bwdk_can_auto_move_to_processing') && bwdk_can_auto_move_to_processing($order)) {

                                // Prevent recursion into our own order-status handlers.
                                if (class_exists(\BWDK\Core\App::class) && \BWDK\Core\App::has(\BWDK\Woo\Order\OrderCancelSync::class)) {
                                    $cancel_sync = \BWDK\Core\App::get(\BWDK\Woo\Order\OrderCancelSync::class);
                                    if (is_object($cancel_sync)) {
                                        remove_action('woocommerce_order_status_changed', [$cancel_sync, 'handleOrderStatusChange'], 5);
                                        remove_action('woocommerce_order_status_pending_to_cancelled', [$cancel_sync, 'handlePendingToCancelled'], 5);
                                    }
                                }

                                $order->add_order_note(
                                    'BWDK DEBUG STATUS WRITE: source=' . __METHOD__ .
                                    ' current=' . $order->get_status() .
                                    ' target=processing' .
                                    ' posted=' . print_r($_POST['order_status'] ?? null, true)
                                );
                                error_log(
                                    'BWDK DEBUG STATUS WRITE source=' . __METHOD__ .
                                    ' order_id=' . $order->get_id() .
                                    ' current=' . $order->get_status() .
                                    ' target=processing' .
                                    ' posted=' . print_r($_POST['order_status'] ?? null, true)
                                );
                                $order->set_status('processing');
                                $order->update_meta_data('_bwdk_auto_processing_done', 'yes');
                                $order->save();

                                if (class_exists(\BWDK\Core\App::class) && \BWDK\Core\App::has(\BWDK\Woo\Order\OrderCancelSync::class)) {
                                    $cancel_sync = \BWDK\Core\App::get(\BWDK\Woo\Order\OrderCancelSync::class);
                                    if (is_object($cancel_sync)) {
                                        add_action('woocommerce_order_status_changed', [$cancel_sync, 'handleOrderStatusChange'], 5, 4);
                                        add_action('woocommerce_order_status_pending_to_cancelled', [$cancel_sync, 'handlePendingToCancelled'], 5, 2);
                                    }
                                }
                            }
                        } else {
                            // For non-processing targets, still avoid overriding final states.
                            $order->set_status($target_status);
                        }
                    }
                }
            }
        }


        $options = get_option(Constant::BWDK_OPTION_NAME);

        $force_login=$options["force-login"]['status']??0;


        $order_received_url ="";

        // Check if this is a single-item flow
        $is_single_flow = $order->get_meta(Constant::META_BWDK_SINGLE_FLOW);

        // Handle user cancellations (only for browser callbacks, not webhooks)
        // Check for cancellation statuses when payment is not completed
        $is_cancelled = (
            $status == BWDK_OrderStatus::CANCELLED ||
            $status == BWDK_OrderStatus::EXPIRED ||
            $status == BWDK_OrderStatus::FAILED_TO_PAY ||
            $status == 5 || // EXPIRED (numeric)
            $status == 6    // CANCELLED (numeric)
        ) && !$is_paid;

        if ($is_cancelled) {
            // Get return_to URL (prefer order meta, then session, then fallback)
            $redirect_url = $this->getReturnToUrl($order);
            
            // Fallback to ReturnUrlHelper if return_to not found
            if (!$redirect_url) {
                $redirect_url = ReturnUrlHelper::pop_url();
            }

            // For single flow, add query args if it's a product URL
            if ($is_single_flow && $redirect_url) {
                $product_id = $order->get_meta('_bwdk_single_product_id');
                if ($product_id && get_post_type($product_id) === 'product') {
                    $product_url = get_permalink($product_id);
                    // If the redirect URL matches the product URL, add query args
                    if ($redirect_url === $product_url || strpos($redirect_url, $product_url) !== false) {
                        $redirect_url = add_query_arg(
                            [
                                'bwdk_back' => 1,
                                'pid'       => (int) $product_id,
                            ],
                            $product_url
                        );
                    }
                }
            }

            // If no entry point found, fallback to callback URL or checkout
            if (!$redirect_url) {
                // For single flow, try to get product URL
                if ($is_single_flow) {
                    $product_id = $order->get_meta('_bwdk_single_product_id');
                    if ($product_id && get_post_type($product_id) === 'product') {
                        $product_url = get_permalink($product_id);
                        $redirect_url = add_query_arg(
                            [
                                'bwdk_back' => 1,
                                'pid'       => (int) $product_id,
                            ],
                            $product_url
                        );
                    }
                }
                
                if (!$redirect_url) {
                    $redirect_url = $order->get_meta(Constant::META_BWDK_CB_URL);
                    if (!$redirect_url) {
                        $redirect_url = function_exists('wc_get_checkout_url') ? wc_get_checkout_url() : home_url('/');
                    }
                }
            }

            // Add WooCommerce notice about cancellation
            if (function_exists('wc_add_notice')) {
                wc_add_notice(
                    __('پرداخت از طریق دیجی‌کالا توسط شما لغو شد. می‌توانید دوباره تلاش کنید.', 'bwdk'),
                    'notice'
                );
            }

            // Resolve return_to URL (prefer order meta, then session, then fallback)
            $redirect_url = $this->getReturnToUrl($order);
            
            // Fallback: determine from cart contents
            if (empty($redirect_url)) {
                if (function_exists('WC') && WC()->cart && !WC()->cart->is_empty()) {
                    $redirect_url = function_exists('wc_get_checkout_url') ? wc_get_checkout_url() : home_url('/checkout/');
                } else {
                    $redirect_url = function_exists('wc_get_cart_url') ? wc_get_cart_url() : home_url('/cart/');
                }
            }
            
            if ($logger) {
                $logger->info(
                    sprintf(
                        'BWDK RETURN TRACE: /bwdk-callback redirecting to origin (cancelled) - order_id=%d reason=%s return_to=%s timestamp=%s',
                        $order->get_id(),
                        $reason,
                        $redirect_url,
                        current_time('mysql')
                    ),
                    ['source' => 'bwdk_return_trace']
                );
            }

            // Save order and redirect to origin (cart/checkout)
            $order->save();
            nocache_headers();
            wp_safe_redirect($redirect_url);
            exit;
        }

        // Handle cancelled/expired orders for single-item flow
        if ($is_single_flow && ($status == BWDK_OrderStatus::CANCELLED || $status == 5 || $status == BWDK_OrderStatus::FAILED_TO_PAY)) {
            // Get the product ID stored when creating the order
            $product_id = $order->get_meta('_bwdk_single_product_id');
            if ($product_id && get_post_type($product_id) === 'product') {
                // Redirect to product page with marker
                $product_url = get_permalink($product_id);
                $order_received_url = add_query_arg(
                    [
                        'bwdk_back' => 1,
                        'pid'       => (int) $product_id,
                    ],
                    $product_url
                );
            } else {
                // Fallback to home if product not found
                $order_received_url = home_url();
            }
        } elseif ($is_single_flow && $status == BWDK_OrderStatus::PENDING) {
            // For pending orders in single flow, also redirect to product
            $product_id = $order->get_meta('_bwdk_single_product_id');
            if ($product_id && get_post_type($product_id) === 'product') {
                $product_url = get_permalink($product_id);
                $order_received_url = add_query_arg(
                    [
                        'bwdk_back' => 1,
                        'pid'       => (int) $product_id,
                    ],
                    $product_url
                );
            } else {
                $order_received_url = $order->get_meta(Constant::META_BWDK_CB_URL);
            }
        } elseif ($status==BWDK_OrderStatus::PENDING) {
            $order_received_url= $order->get_meta(Constant::META_BWDK_CB_URL);
        }

        if ($status==BWDK_OrderStatus::FAILED_TO_PAY) {
            $order->update_meta_data('_bwdk_error', 'پرداخت ناموفق بود');
            wc_setcookie( 'bwdk_error', "پرداخت ناموفق بود", time() + HOUR_IN_SECONDS );
            if (empty($order_received_url)) {
                $order_received_url= $order->get_meta(Constant::META_BWDK_CB_URL);
            }
        }

        if($status==BWDK_OrderStatus::PAID_BY_USER){
            $paid_bwdk = isset($raw['total_paid_amount']) ? (float) $raw['total_paid_amount'] : 0.0;
            $wc_total_tm = WooCompatibility::convert_to_BWDK_currency((float) $order->get_total());

            // ذخیره مبلغ پرداختی برای رجوع بعدی
            $order->update_meta_data('_bwdk_total_paid_amount_tm', $paid_bwdk);

            // فقط برای دیباگ – نه شرط بازدارنده
            if (abs($paid_bwdk - $wc_total_tm) > 0.01) {
                if (function_exists('wc_get_logger')) {
                    wc_get_logger()->info(
                        sprintf(
                            'BWDK amount mismatch before verify: paid=%.2f, wc_total_tm=%.2f, oid=%d, uuid=%s',
                            $paid_bwdk, $wc_total_tm, $order->get_id(), (string) $order_uuid
                        ),
                        ['source' => 'bwdk_callback']
                    );
                }
            }

            // همیشه تلاش به verify
            $verify = (new Client())->verifyOrder($order, $order_uuid);

            if (is_array($verify) && (($verify['status'] ?? null) === BWDK_OrderStatus::VERIFIED_BY_MERCHANT)) {
                $raw['status'] = BWDK_OrderStatus::VERIFIED_BY_MERCHANT;
                $raw['status_display'] = BWDK_OrderStatus::label(BWDK_OrderStatus::VERIFIED_BY_MERCHANT);
                
                // Update raw with verify response data (markup fields may be in verify response)
                if (is_array($verify)) {
                    $raw = array_merge($raw, $verify);
                }

                $order->update_meta_data(Constant::META_BWDK_JSON_CB, $raw);

                $this->bwdk_save_receiver_meta_from_response( $order, $raw['destination_address'] ?? null );

                // Keep flattened search metas in sync after verify.
                $reference_code = isset($raw['reference_code']) ? (string) $raw['reference_code'] : '';
                if ($reference_code !== '') {
                    $order->update_meta_data('_bwdk_reference_code', $reference_code);
                }
                $digify_id = isset($raw['id']) ? (string) $raw['id'] : '';
                if ($digify_id !== '') {
                    $order->update_meta_data('_bwdk_digify_order_id', $digify_id);
                }

                // Build aggregated search tokens for reliable admin search
                $ref = $reference_code;
                $digify_id_meta = $digify_id;

                // Normalize helpers
                $norm = static function($s) {
                    $s = trim((string)$s);
                    $s = preg_replace('/\s+/', '', $s);
                    return $s;
                };

                // Build tokens: original + variants (case-insensitive search uses LIKE)
                $tokens = [];
                if ($ref !== '') {
                    $ref_trimmed = trim($ref);
                    $ref_norm = preg_replace('/\s+/', '', $ref_trimmed);
                    $ref_upper = strtoupper($ref_norm);      // e.g. BD-61564156
                    $ref_lower = strtolower($ref_norm);      // e.g. bd-61564156
                    $ref_stripped = preg_replace('/^BD[-:]?/i', '', $ref_upper); // 61564156
                    
                    // Add all variants
                    $tokens[] = $ref_trimmed;      // Original with spaces if any
                    $tokens[] = $ref_norm;        // Original without spaces
                    $tokens[] = $ref_upper;       // Uppercase
                    $tokens[] = $ref_lower;       // Lowercase
                    if ($ref_stripped !== $ref_upper) {
                        $tokens[] = $ref_stripped; // Without BD prefix
                    }
                }
                if ($digify_id_meta !== '') {
                    $id_norm = $norm($digify_id_meta);             // 122406
                    $tokens[] = $id_norm;
                    $tokens[] = 'id:' . $id_norm;
                    $tokens[] = 'ID:' . $id_norm;
                }

                // Dedup and join to a single searchable blob
                $tokens = array_values(array_unique(array_filter($tokens)));
                $blob = implode(' ', $tokens);

                // Store searchable blob meta
                if ($blob !== '') {
                    $order->update_meta_data('_bwdk_search_tokens', $blob);
                }

                // Extract and save markup fields from verify response (after merge, check both $raw and nested structures)
                // CRITICAL: Only apply markup for Digipay orders
                $payment = is_array($raw['payment'] ?? null) ? $raw['payment'] : [];
                $gateway_name = sanitize_text_field((string) ($payment['gateway_name'] ?? ''));
                $is_digipay = ($gateway_name !== '' && strcasecmp($gateway_name, 'digipay') === 0);
                
                // Also check order payment method as fallback (if gateway_name not available)
                if (!$is_digipay) {
                    $order_payment_method = $order->get_payment_method();
                    // Check if payment method contains 'digipay' (case-insensitive)
                    $is_digipay = (stripos($order_payment_method, 'digipay') !== false);
                }
                
                // Only proceed with markup if this is a Digipay order
                if (!$is_digipay) {
                    // Not Digipay - skip markup processing entirely
                    if (function_exists('wc_get_logger')) {
                        wc_get_logger()->info(
                            sprintf('BWDK Markup: Skipped - not Digipay order (gateway_name=%s, payment_method=%s)', $gateway_name, $order->get_payment_method()),
                            ['source' => 'bwdk_markup']
                        );
                    }
                } else {
                    // This is a Digipay order - proceed with markup extraction
                    $markup_percentage = 0.0;
                    $markup_amount = 0.0;
                    
                    // Debug: Log verify response structure (temporary, remove after testing)
                    if (function_exists('wc_get_logger')) {
                        wc_get_logger()->info(
                            'BWDK Markup Debug - Verify keys: ' . implode(', ', array_keys($verify ?? [])),
                            ['source' => 'bwdk_markup_debug']
                        );
                        if (isset($raw['payment'])) {
                            wc_get_logger()->info(
                                'BWDK Markup Debug - Payment keys: ' . implode(', ', array_keys($raw['payment'] ?? [])),
                                ['source' => 'bwdk_markup_debug']
                            );
                        }
                    }
                    
                    // Try multiple possible locations for markup fields
                    if (isset($raw['markup_commission_percentage'])) {
                        $markup_percentage = (float) $raw['markup_commission_percentage'];
                    } elseif (isset($verify['markup_commission_percentage'])) {
                        $markup_percentage = (float) $verify['markup_commission_percentage'];
                    } elseif (isset($raw['payment']['markup_commission_percentage'])) {
                        $markup_percentage = (float) $raw['payment']['markup_commission_percentage'];
                    }
                    
                    if (isset($raw['digipay_markup_amount'])) {
                        $markup_amount = (float) $raw['digipay_markup_amount'];
                    } elseif (isset($verify['digipay_markup_amount'])) {
                        $markup_amount = (float) $verify['digipay_markup_amount'];
                    } elseif (isset($raw['payment']['digipay_markup_amount'])) {
                        $markup_amount = (float) $raw['payment']['digipay_markup_amount'];
                    }
                    
                    // Debug: Log extracted values
                    if (function_exists('wc_get_logger')) {
                        wc_get_logger()->info(
                            sprintf('BWDK Markup Debug - Extracted: percentage=%.2f, amount=%.2f', $markup_percentage, $markup_amount),
                            ['source' => 'bwdk_markup_debug']
                        );
                    }
                    
                    if ($markup_percentage > 0 || $markup_amount > 0) {
                        $order->update_meta_data('_markup_commission_percentage', $markup_percentage);
                        $order->update_meta_data('_digipay_markup_amount', $markup_amount);
                        
                        // Add or update fee line item for markup amount
                        $this->bwdk_apply_digipay_markup_fee($order, $markup_amount, $markup_percentage);
                    }
                }

                $this->log_payment_status_note($order, $raw);
                // ثبت نوت وضعیت به‌روزرسانی شده از verify
                $this->bwdk_note_webhookStatus($order, $raw);
                
                // ساخت/اتصال کاربر بر اساس موبایل
                $this->bwdk_create_or_attach_user($order, $raw);
                
                $order_received_url = $order->get_checkout_order_received_url();
            }

            // Only empty cart for full-cart flow, not single-item flow
            if (!$is_single_flow) {
                wc()->cart->empty_cart();
            }
        }


        if ($force_login && $status==BWDK_OrderStatus::PAID_BY_USER) {
            $order_received_url =get_page_link($options['thankyou-page']['id']) ;
            $order_received_url = add_query_arg( 'order', $order->get_id(), $order_received_url );
            $order_received_url = add_query_arg( 'key', $order->get_order_key(), $order_received_url );
            // Only empty cart for full-cart flow, not single-item flow
            if (!$is_single_flow) {
                wc()->cart->empty_cart();
            }
        }

        if ($status==BWDK_OrderStatus::VERIFIED_BY_MERCHANT) {
            // ساخت/اتصال کاربر بر اساس موبایل (اگر قبلاً انجام نشده)
            $this->bwdk_create_or_attach_user($order, $raw);
            $order_received_url= $order->get_meta(Constant::META_BWDK_CB_URL);
        }

        // Define abort/cancel reasons that should redirect to origin (cart/checkout)
        $abort_reasons = [
            'back_from_shipping_page',
            'user_cancelled',
            'cancelled',
            'cancel',
            'abort',
            'back',
        ];
        
        // Check if this is a back/cancel scenario
        $is_abort_scenario = false;
        if (!empty($reason)) {
            $normalized_reason = strtolower(trim($reason));
            $is_abort_scenario = in_array($normalized_reason, $abort_reasons, true);
        }
        
        // Also check status-based cancellation (already handled above, but double-check)
        if (!$is_abort_scenario) {
            $cancelled_statuses = [
                BWDK_OrderStatus::CANCELLED,
                BWDK_OrderStatus::EXPIRED,
                BWDK_OrderStatus::FAILED_TO_PAY,
                5, // EXPIRED (numeric)
                6, // CANCELLED (numeric)
            ];
            if (in_array($status, $cancelled_statuses, true)) {
                $is_abort_scenario = true;
            }
        }
        
        // Only redirect to origin (cart/checkout) for back/cancel scenarios
        if ($is_abort_scenario) {
            // Resolve return_to URL (prefer order meta, then session, then fallback)
            $redirect_url = $this->getReturnToUrl($order);
            
            // Fallback: determine from cart contents
            if (empty($redirect_url)) {
                if (function_exists('WC') && WC()->cart && !WC()->cart->is_empty()) {
                    $redirect_url = function_exists('wc_get_checkout_url') ? wc_get_checkout_url() : home_url('/checkout/');
                } else {
                    $redirect_url = function_exists('wc_get_cart_url') ? wc_get_cart_url() : home_url('/cart/');
                }
            }
            
            if ($logger) {
                $logger->info(
                    sprintf(
                        'BWDK RETURN TRACE: /bwdk-callback redirecting to origin (back/cancel) - order_id=%d reason=%s return_to=%s timestamp=%s',
                        $order->get_id(),
                        $reason ?: 'N/A',
                        $redirect_url,
                        current_time('mysql')
                    ),
                    ['source' => 'bwdk_return_trace']
                );
            }
            
            // Set nocache headers to prevent back-cache issues
            nocache_headers();
            
            $order->save();
            wp_safe_redirect($redirect_url);
            exit;
        }
        
        // For success/paid/verify flows: use existing order_received_url (unchanged behavior)
        // If order_received_url is not set, fallback to checkout
        if (empty($order_received_url)) {
            $order_received_url = function_exists('wc_get_checkout_url') ? wc_get_checkout_url() : home_url('/checkout/');
        }
        
        if ($logger) {
            $logger->info(
                sprintf(
                    'BWDK RETURN TRACE: /bwdk-callback redirecting to success URL (not back/cancel) - order_id=%d reason=%s order_received_url=%s timestamp=%s',
                    $order->get_id(),
                    $reason ?: 'N/A',
                    $order_received_url,
                    current_time('mysql')
                ),
                ['source' => 'bwdk_return_trace']
            );
        }
        
        // Set nocache headers to prevent back-cache issues
        nocache_headers();
        
        $order->save();
        wp_safe_redirect($order_received_url);
        exit;
    }

    /**
     * Save receiver_name and receiver_phone from BWDK Manager API response.destination_address.
     * HPOS-safe: uses WC_Order meta methods only.
     *
     * @param WC_Order $order
     * @param array|null $dest destination_address from response
     */
    protected function bwdk_save_receiver_meta_from_response( WC_Order $order, $dest ): void
    {
        $response = is_array( $dest ) ? $dest : [];
        $receiver_name  = $response['receiver_name'] ?? '';
        $receiver_phone = $response['receiver_phone'] ?? '';

        $receiver_name  = is_string( $receiver_name ) ? trim( $receiver_name ) : '';
        $receiver_phone = is_string( $receiver_phone ) ? trim( $receiver_phone ) : '';

        if ( $receiver_name !== '' ) {
            $order->update_meta_data( '_bwdk_receiver_name', $receiver_name );
        }
        if ( $receiver_phone !== '' ) {
            $order->update_meta_data( '_bwdk_receiver_phone', $receiver_phone );
        }
        $order->save();

        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            error_log( '[BWDK] saved receiver meta for order_id=' . $order->get_id() . ' name=' . $receiver_name );
        }
    }

    /**
     * Get return_to URL from order meta or session, with validation
     */
    protected function getReturnToUrl(WC_Order $order): ?string
    {
        // Try order meta first
        $return_to = $order->get_meta('_bwdk_return_to');
        if (empty($return_to) && function_exists('WC') && WC()->session) {
            // Try session
            $return_to = WC()->session->get('bwdk_return_to');
            // Clear session after reading
            if ($return_to) {
                WC()->session->__unset('bwdk_return_to');
            }
        }

        if (empty($return_to)) {
            return null;
        }

        // Validate and sanitize return_to (must be same host to prevent open redirect)
        $parsed = wp_parse_url($return_to);
        $home_parsed = wp_parse_url(home_url());

        if (!$parsed || !isset($parsed['host'])) {
            return null;
        }

        // Ensure same host
        if (isset($home_parsed['host']) && $parsed['host'] !== $home_parsed['host']) {
            return null;
        }

        // Rebuild URL with only path and query (no host/port)
        $safe_url = '';
        if (isset($parsed['path'])) {
            $safe_url = $parsed['path'];
        }
        if (isset($parsed['query'])) {
            $safe_url .= '?' . $parsed['query'];
        }
        if (isset($parsed['fragment'])) {
            $safe_url .= '#' . $parsed['fragment'];
        }

        if (empty($safe_url)) {
            return null;
        }

        // Use home_url to ensure same domain
        return home_url($safe_url);
    }

    protected function log_payment_status_note(WC_Order $order, array $body): void
    {
        if (!$order instanceof WC_Order || empty($body)) {
            return;
        }

        $status = isset($body['status']) ? (int) $body['status'] : 0;
        $status_display = strtolower((string) ($body['status_display'] ?? ''));
        $is_paid_flag = isset($body['is_paid']) ? (bool) $body['is_paid'] : false;

        $success_statuses = [
            BWDK_OrderStatus::PAID_BY_USER,
            BWDK_OrderStatus::VERIFIED_BY_MERCHANT,
        ];

        $failure_statuses = [
            BWDK_OrderStatus::FAILED_TO_PAY,
            BWDK_OrderStatus::FAILED_TO_VERIFY,
            BWDK_OrderStatus::FAILED_BY_MERCHANT,
            BWDK_OrderStatus::CANCELLED,
            BWDK_OrderStatus::CANCELLED_BY_MERCHANT,
            BWDK_OrderStatus::EXPIRED,
        ];

        $is_success = $is_paid_flag || in_array($status, $success_statuses, true);

        if ($is_success) {
            $payment = is_array($body['payment'] ?? null) ? $body['payment'] : [];
            $gateway_name = sanitize_text_field((string) ($payment['gateway_name'] ?? ''));
            $reference_code = (string) ($body['reference_code'] ?? '');
            $invoice_id = (string) ($payment['invoice_id'] ?? $body['invoice_id'] ?? '');

            if ($gateway_name !== '' && strcasecmp($gateway_name, 'digipay') === 0) {
                $display = sanitize_text_field((string) ($payment['gateway_type_display'] ?? ''));
                $mode = $this->resolve_digipay_mode($display);
                $this->bwdk_note_digipayPaid($order, $mode, $invoice_id, $reference_code);
            } else {
                $gw = $gateway_name !== '' ? $gateway_name : sanitize_text_field((string) ($payment['gateway_type_display'] ?? ''));
                if ($gw === '') {
                    $gw = 'درگاه پرداخت';
                }
                $this->bwdk_note_cashPaid($order, $gw, $invoice_id, $reference_code);
            }
            return;
        }

        $is_failed = in_array($status, $failure_statuses, true) ||
            ($status_display !== '' && (strpos($status_display, 'fail') !== false || strpos($status_display, 'cancel') !== false));

        if ($is_failed) {
            $gateway_name = '';
            $payment = is_array($body['payment'] ?? null) ? $body['payment'] : [];
            if (is_array($payment)) {
                $gateway_name = sanitize_text_field((string) ($payment['gateway_name'] ?? ''));
            }
            $this->bwdk_note_gatewayCanceled($order, $gateway_name);
        }
    }

    protected function resolve_digipay_subtype(string $gateway_type_display): string
    {
        $normalized = strtolower($gateway_type_display);

        if (strpos($normalized, 'wallet') !== false) {
            return 'کیف پول';
        }

        if (strpos($normalized, 'credit') !== false) {
            return 'اعتباری';
        }

        if (strpos($normalized, 'installment') !== false) {
            return 'اقساطی';
        }

        return 'دیجی‌پی';
    }

    /**
     * تشخیص حالت دیجی‌پی (wallet/credit/installment)
     *
     * @param string $gateway_type_display
     * @return string
     */
    protected function resolve_digipay_mode(string $gateway_type_display): string
    {
        $normalized = strtolower($gateway_type_display);

        if (strpos($normalized, 'wallet') !== false) {
            return 'wallet';
        }

        if (strpos($normalized, 'credit') !== false) {
            return 'credit';
        }

        if (strpos($normalized, 'installment') !== false) {
            return 'installment';
        }

        return '';
    }


    function bwdk_get_order_meta_dynamic( $order, string $meta_key, bool $single = true ) {


        if ( function_exists('wc_hpos_is_enabled') && wc_hpos_is_enabled() ) {
            return $order->get_meta( $meta_key, $single );
        } else {
            return get_post_meta( $order->get_id(), $meta_key, $single );
        }
    }

    public function bwdk_apply_address_shipping_billing(WC_Order $order, array $destination, array $opts = []): array
    {


        if (!$order instanceof WC_Order) {
            return ['ok' => false, 'error' => 'invalid_order', 'updated_fields' => []];
        }
        if (empty($destination) || !is_array($destination)) {
            return ['ok' => false, 'error' => 'invalid_destination', 'updated_fields' => []];
        }


        $country = $opts['country'] ?? 'IR';
        $billing_mode = $opts['billing_mode'] ?? 'always';
        $phone = isset($opts['phone']) ? (string)$opts['phone'] : '';
        $save_meta = array_key_exists('save_meta', $opts) ? (bool)$opts['save_meta'] : true;

        $clean = static function ($v) {
            return wc_clean(trim((string)$v));
        };


        $addr1 = $clean($destination['address'] ?? '');
        $building = $clean($destination['building_number'] ?? '');
        $unit = $clean($destination['unit'] ?? '');
        $district = $clean($destination['district_name'] ?? '');
        $city = $clean($destination['city_name'] ?? '');
        $stateName = $clean($destination['state_name'] ?? '');
        $postal = preg_replace('/\D+/', '', (string)($destination['postal_code'] ?? ''));
        $lat = $clean($destination['latitude'] ?? '');
        $lng = $clean($destination['longitude'] ?? '');
        $dkId = $clean($destination['digikala_id'] ?? '');


        $addr2Parts = array_filter([
            $district ?: null,
            $building ? "پلاک {$building}" : null,
            $unit ? "واحد {$unit}" : null,
        ]);
        $addr2 = $clean(implode('، ', $addr2Parts));


        $stateCode = $stateName;
        if (function_exists('WC') && WC()->countries) {
            $states = WC()->countries->get_states($country) ?: [];
            $found = array_search($stateName, $states, true);
            if ($found !== false) {
                $stateCode = $found;
            }
        }

        $updated = [];

        // ---------- Shipping ----------
        if ($addr1) {
            $order->set_shipping_address_1($addr1);
            $updated[] = 'shipping_address_1';
        }
        $order->set_shipping_address_2($addr2);
        $updated[] = 'shipping_address_2';
        if ($city) {
            $order->set_shipping_city($city);
            $updated[] = 'shipping_city';
        }
        if ($stateCode) {
            $order->set_shipping_state($stateCode);
            $updated[] = 'shipping_state';
        }
        if ($postal !== '') {
            $order->set_shipping_postcode($postal);
            $updated[] = 'shipping_postcode';
        }
        if ($country) {
            $order->set_shipping_country($country);
            $updated[] = 'shipping_country';
        }


        if ($phone !== '' && !$order->get_billing_phone()) {
            $order->set_billing_phone($phone);
            $updated[] = 'billing_phone';
            $updated[] = 'billing_phone';
        }

        // ---------- Billing ----------
        $should_fill_billing =
            ($billing_mode === 'always') ||
            ($billing_mode === 'if_empty' && !$order->get_billing_address_1());

        if ($should_fill_billing) {
            if ($addr1) {
                $order->set_billing_address_1($addr1);
                $updated[] = 'billing_address_1';
            }
            $order->set_billing_address_2($addr2);
            $updated[] = 'billing_address_2';
            if ($city) {
                $order->set_billing_city($city);
                $updated[] = 'billing_city';
            }
            if ($stateCode) {
                $order->set_billing_state($stateCode);
                $updated[] = 'billing_state';
            }
            if ($postal !== '') {
                $order->set_billing_postcode($postal);
                $updated[] = 'billing_postcode';
            }
            if ($country) {
                $order->set_billing_country($country);
                $updated[] = 'billing_country';
            }


            if ($phone !== '') {
                $order->set_billing_phone($phone);
                $updated[] = 'billing_phone';
            }
        }

        // ---------- Extra meta ----------
        if ($save_meta) {
            if ($dkId !== '') {
                $order->update_meta_data('_dest_digikala_id', $dkId);
                $updated[] = '_dest_digikala_id';
            }
            if ($district !== '') {
                $order->update_meta_data('_dest_district_name', $district);
                $updated[] = '_dest_district_name';
            }
            if ($lat !== '') {
                $order->update_meta_data('_dest_latitude', $lat);
                $updated[] = '_dest_latitude';
            }
            if ($lng !== '') {
                $order->update_meta_data('_dest_longitude', $lng);
                $updated[] = '_dest_longitude';
            }
        }

        return ['ok' => true, 'error' => null, 'updated_fields' => array_values(array_unique($updated))];
    }

    public function bwdk_apply_payment_method(WC_Order $order, string $method_id, string $method_title): array
    {
        if (!$order instanceof WC_Order) {
            return ['ok' => false, 'error' => 'invalid_order', 'updated_fields' => []];
        }
        if ($method_id === '' && $method_title === '') {
            return ['ok' => false, 'error' => 'no_payment_info', 'updated_fields' => []];
        }

        $updated = [];

        if ($method_id !== '') {
            $order->set_payment_method($method_id);
            $updated[] = '_payment_method';
        }
        if ($method_title !== '') {
            $order->set_payment_method_title($method_title);
            $updated[] = '_payment_method_title';
        }

        return ['ok' => true, 'error' => null, 'updated_fields' => $updated];
    }

    public function bwdk_apply_packing($order,$selected_packing)
    {

        $label=$selected_packing["name"]??"";
        $amount=$selected_packing['price']??0;
        foreach ($order->get_fees() as $packing) {

            if ("هزینه بسته بندی:". $label==$packing["name"]){
                return false;
            }

        }

        $item = new WC_Order_Item_Fee();
        $item->set_name( "هزینه بسته بندی:". $label );
        $packing_amount = WooCompatibility::convert_to_store_currency((float)$amount);
        $item->set_total($packing_amount);
        
        // Set fee as non-taxable to prevent WooCommerce from adding VAT
        // Packing amount from Digify already includes tax in the final_amount sent to gateway
        $item->set_tax_class(''); // Empty tax class = non-taxable
        if (method_exists($item, 'set_tax_status')) {
            $item->set_tax_status('none'); // Explicitly set as non-taxable
        }
        $item->set_total_tax(0); // Ensure tax is zero
        // Note: set_subtotal_tax() doesn't exist for WC_Order_Item_Fee, only set_total_tax() is available
        
        $order->add_item( $item );
        
        // Manually update order total without recalculating taxes
        // This prevents WooCommerce from adding VAT to the packing fee
        $current_total = (float) $order->get_total();
        $new_total = $current_total + $packing_amount;
        $order->set_total($new_total);
        
        // Save the order
        $order->save();
        
        return true;
    }

    /**
     * Add Digipay markup fee to order (with duplicate prevention)
     *
     * @param WC_Order $order
     * @param float $markup_amount Amount in backend currency (will be converted to store currency)
     * @param float $markup_percentage Percentage value (optional, for display in fee name)
     * @return bool
     */
    public function bwdk_apply_digipay_markup_fee(WC_Order $order, float $markup_amount, float $markup_percentage = 0.0): bool
    {
        if (!$order instanceof WC_Order || $markup_amount <= 0) {
            return false;
        }

        // CRITICAL: Only apply fee for Digipay orders
        // Check payment method from order
        $order_payment_method = $order->get_payment_method();
        $is_digipay_by_method = (stripos($order_payment_method, 'digipay') !== false);
        
        // Also check gateway_name from stored callback data as fallback
        $is_digipay_by_gateway = false;
        $raw_json = $order->get_meta(\BWDK\Support\Constant::META_BWDK_JSON_CB);
        if (!empty($raw_json)) {
            if (is_string($raw_json)) {
                $raw_json = json_decode($raw_json, true);
            }
            if (is_array($raw_json) && isset($raw_json['payment']['gateway_name'])) {
                $gateway_name = strtolower((string) $raw_json['payment']['gateway_name']);
                $is_digipay_by_gateway = ($gateway_name === 'digipay');
            }
        }
        
        // Must be Digipay by either method or gateway check
        if (!$is_digipay_by_method && !$is_digipay_by_gateway) {
            // Not Digipay - do not add fee
            if (function_exists('wc_get_logger')) {
                wc_get_logger()->info(
                    sprintf('BWDK Markup Fee: Skipped - not Digipay order (payment_method=%s)', $order_payment_method),
                    ['source' => 'bwdk_markup']
                );
            }
            return false;
        }

        // Build fee name with percentage if available
        $fee_name = 'کارمزد دیجی‌پی (مارک‌آپ)';
        if ($markup_percentage > 0) {
            $fee_name = 'کارمزد دیجی‌پی (مارک‌آپ ' . sprintf('%.2f%%', $markup_percentage) . ')';
        }

        // Check if markup fee already exists and update it instead of removing
        $existing_fee_id = null;
        foreach ($order->get_fees() as $fee_id => $fee) {
            if ($fee->get_meta('_bwdk_digipay_markup_fee') === '1') {
                $existing_fee_id = $fee_id;
                break;
            }
        }

        // Convert markup amount from backend currency to store currency
        $markup_amount_store = WooCompatibility::convert_to_store_currency($markup_amount);

        if ($existing_fee_id !== null) {
            // Update existing fee
            $fee_item = $order->get_item($existing_fee_id);
            if ($fee_item instanceof WC_Order_Item_Fee) {
                $fee_item->set_name($fee_name);
                $fee_item->set_total($markup_amount_store);
                $fee_item->set_total_tax(0);
                $fee_item->save();
            }
        } else {
            // Create new fee item
            $fee_item = new WC_Order_Item_Fee();
            $fee_item->set_name($fee_name);
            $fee_item->set_total($markup_amount_store);
            
            // Set fee as non-taxable
            $fee_item->set_tax_class('');
            if (method_exists($fee_item, 'set_tax_status')) {
                $fee_item->set_tax_status('none');
            }
            $fee_item->set_total_tax(0);
            
            // Mark fee with meta flag for duplicate detection
            $fee_item->add_meta_data('_bwdk_digipay_markup_fee', '1', true);
            
            // Add fee to order
            $order->add_item($fee_item);
        }
        
        // Recalculate totals and save
        $order->calculate_totals(false);
        $order->save();
        
        return true;
    }

    public function bwdk_apply_shipping_method(
        WC_Order $order,
        string   $method_id,
        string   $method_title,
        float    $cost = 0.0,
        array    $extras = [],
        bool     $also_save_on_order = false,
        bool     $recalculate_totals = true
    ): array
    {
        if (!$order instanceof WC_Order) {
            return ['ok' => false, 'error' => 'invalid_order', 'updated_fields' => []];
        }
        if ($method_id === '' && $method_title === '') {
            return ['ok' => false, 'error' => 'no_shipping_info', 'updated_fields' => []];
        }


        foreach ($order->get_items('shipping') as $item_id => $item) {
            $order->remove_item($item_id);
        }



        $shipping_item = new WC_Order_Item_Shipping();
        if ($method_id !== '') {
            $shipping_item->set_method_id($method_id);
        }
        if ($method_title !== '') {
            $shipping_item->set_method_title($method_title);
        }

        if (isset($extras['cost'])) {
            $cost = WooCompatibility::convert_to_store_currency((float)$extras['cost']);
        }
        $shipping_item->set_total((float)$cost);

        $meta_key_map = [
            'get_shipping_type_display' => '_ship_type_display',
            'cost' => '_ship_cost_api',
            'minimum_time_sending' => '_ship_min_days',
            'maximum_time_sending' => '_ship_max_days',
            'delivery_time_display' => '_ship_delivery_display',
            'delivery_time_range_display' => '_ship_delivery_range',
        ];

        $updated_meta_keys = [];


        foreach ($meta_key_map as $api_key => $meta_key) {
            if (!array_key_exists($api_key, $extras)) {
                continue;
            }
            $value = $extras[$api_key];


            if (is_array($value)) {
                $value = wp_json_encode($value, JSON_UNESCAPED_UNICODE);
            }


            if (is_string($value)) {
                $value = wc_clean($value);
            }

            $shipping_item->add_meta_data($meta_key, $value, true);
            $updated_meta_keys[] = $meta_key;

            if ($also_save_on_order) {
                $order->update_meta_data($meta_key, $value);
            }
        }

        $order->add_item($shipping_item);


        if ($recalculate_totals) {

            $order->calculate_totals(false);
        }

        return [
            'ok' => true,
            'error' => null,
            'updated_fields' => array_merge(
                ['shipping_method', 'shipping_method_title', 'shipping_cost'],
                $updated_meta_keys
            ),
        ];
    }

    /**
     * Apply BWDK promotion discount as WooCommerce coupon item
     *
     * @param WC_Order $order
     * @param array $promotion_data Promotion data from BWDK API (must contain 'code' key)
     * @param float $discount_amount Discount amount in BWDK currency
     * @return array Result array with 'ok' status
     */
    public function bwdk_apply_promotion_discount(
        WC_Order $order,
        array $promotion_data,
        float $discount_amount
    ): array
    {
        if (!$order instanceof WC_Order) {
            return ['ok' => false, 'error' => 'invalid_order'];
        }

        // Validate promotion_data is not empty object
        if (empty($promotion_data)) {
            return ['ok' => false, 'error' => 'empty_promotion_data'];
        }

        // Validate discount amount
        if ($discount_amount <= 0) {
            return ['ok' => false, 'error' => 'invalid_discount_amount'];
        }

        // Get promotion code from promotion_data
        $promotion_code = isset($promotion_data['code']) ? (string) $promotion_data['code'] : '';
        if (empty($promotion_code)) {
            return ['ok' => false, 'error' => 'missing_promotion_code'];
        }

        // Check promotion_type - only handle DISCOUNT (ignore GIFT/FREE_SHIPPING)
        $promotion_type = isset($promotion_data['type']) ? (string) $promotion_data['type'] : '';
        if (!empty($promotion_type) && $promotion_type !== 'DISCOUNT') {
            return ['ok' => false, 'error' => 'unsupported_promotion_type', 'type' => $promotion_type];
        }

        // Idempotency check: scan existing coupon items for BWDK promotion
        $has_bwdk_promotion = false;
        foreach ($order->get_items('coupon') as $item_id => $item) {
            if (!$item instanceof WC_Order_Item_Coupon) {
                continue;
            }
            
            // Check if this is already a BWDK promotion coupon (try new meta key first, then legacy)
            $is_bwdk_promotion = $item->get_meta('_bwdk_promo') === '1' || $item->get_meta('_bwdk_promotion_coupon') === '1';
            if ($is_bwdk_promotion) {
                $has_bwdk_promotion = true;
                break;
            }
        }

        // If BWDK promotion coupon already exists, skip adding
        if ($has_bwdk_promotion) {
            return ['ok' => true, 'error' => null, 'skipped' => true, 'reason' => 'already_exists'];
        }

        // Check if merchant coupon with same code exists
        $merchant_coupon_exists = false;
        $coupon_code_to_use = $promotion_code;
        foreach ($order->get_items('coupon') as $item_id => $item) {
            if (!$item instanceof WC_Order_Item_Coupon) {
                continue;
            }
            
            // Skip BWDK promotion coupons (check both new and legacy meta keys)
            if ($item->get_meta('_bwdk_promo') === '1' || $item->get_meta('_bwdk_promotion_coupon') === '1') {
                continue;
            }
            
            // Check if merchant coupon has same code
            if ($item->get_code() === $promotion_code) {
                $merchant_coupon_exists = true;
                // Use suffix for uniqueness
                $coupon_code_to_use = $promotion_code . '-bwdk';
                break;
            }
        }

        // Convert discount amount from BWDK currency to store currency
        $discount_amount_store = WooCompatibility::convert_to_store_currency($discount_amount);

        // Create coupon item
        $coupon_item = new WC_Order_Item_Coupon();
        $coupon_item->set_props([
            'code' => $coupon_code_to_use,
            'discount' => $discount_amount_store,
            'discount_tax' => 0.0, // Set to 0 unless tax allocation is needed
        ]);

        // Store meta to identify this as BWDK promotion coupon
        $coupon_item->add_meta_data('_bwdk_promo', '1', true);
        // Keep legacy meta for backward compatibility
        $coupon_item->add_meta_data('_bwdk_promotion_coupon', '1', true);
        
        // Store original code if different from coupon_code_to_use
        if ($merchant_coupon_exists && $coupon_code_to_use !== $promotion_code) {
            $coupon_item->add_meta_data('_bwdk_promotion_original_code', $promotion_code, true);
        }

        // Store full promotion_data for reference
        $coupon_item->add_meta_data('_bwdk_promotion_data', wp_json_encode($promotion_data, JSON_UNESCAPED_UNICODE), true);
        
        // Store discount_type and discount_value for tooltip display
        if (isset($promotion_data['discount_type'])) {
            $coupon_item->add_meta_data('_bwdk_discount_type', (int) $promotion_data['discount_type'], true);
            // Keep legacy meta for backward compatibility
            $coupon_item->add_meta_data('_bwdk_promotion_discount_type', (int) $promotion_data['discount_type'], true);
        }
        if (isset($promotion_data['discount_value'])) {
            $coupon_item->add_meta_data('_bwdk_discount_value', (float) $promotion_data['discount_value'], true);
            // Keep legacy meta for backward compatibility
            $coupon_item->add_meta_data('_bwdk_promotion_discount_value', (float) $promotion_data['discount_value'], true);
        }

        // Add coupon item to order
        $order->add_item($coupon_item);
        
        // Update order discount_total to include promotion discount
        // This ensures the discount appears in order totals
        $existing_discount_total = (float) $order->get_discount_total();
        $new_discount_total = $existing_discount_total + $discount_amount_store;
        $order->set_discount_total($new_discount_total);

        // Note: We do NOT call calculate_totals() here because:
        // 1. The plugin uses final_amount from BWDK API as the source of truth
        // 2. calculate_totals() would recalculate based on WooCommerce logic, which may conflict
        // 3. The final_amount already includes the promotion discount
        // However, we update discount_total manually so it appears in the Coupons section

        return [
            'ok' => true,
            'error' => null,
            'coupon_code' => $coupon_code_to_use,
            'original_code' => $promotion_code,
            'merchant_coupon_exists' => $merchant_coupon_exists,
        ];
    }

    /**
     * ساخت یا اتصال کاربر بر اساس موبایل پس از پرداخت موفق
     *
     * @param WC_Order $order
     * @param array $payload
     */
    protected function bwdk_create_or_attach_user(WC_Order $order, array $payload): void
    {
        // بررسی اینکه آیا قبلاً کاربر متصل شده است
        if ($order->get_meta(Constant::META_USER_ATTACHED)) {
            return; // Idempotent: اگر قبلاً متصل شده، دوباره انجام نده
        }

        $opts = (array) get_option(Constant::BWDK_OPTION_NAME);
        $format = $opts[Constant::OPT_PHONE_FORMAT] ?? 'country_no_plus';

        // 1) استخراج موبایل از payload
        $raw_phone = (string) ($payload['user']['phone_number'] ?? '');
        $e164 = PhoneHelper::normalize_to_e164_no_plus($raw_phone);
        
        if (!$e164) {
            // اگر موبایل معتبر نداریم، از ساخت کاربر عبور کن، فقط نوت بزن
            $this->bwdk_note_debug($order, 'User phone missing/invalid', $raw_phone);
            return;
        }

        // 2) سعی کن کاربر موجود را پیدا کنی
        $user_id = 0;

        // 2-1) جستجو بر اساس مرجع خودمان (phone meta)
        $q = new \WP_User_Query([
            'meta_key'   => Constant::META_PHONE_E164,
            'meta_value' => $e164,
            'number'     => 1,
            'fields'     => 'ID',
        ]);
        if (!empty($q->get_results())) {
            $user_id = (int) $q->get_results()[0];
        }

        // 2-2) اگر نبود، جستجو بر اساس email = phone@domain
        if (!$user_id) {
            $email_candidate = PhoneHelper::format_email($e164);
            if ($email_candidate) {
                $user_by_email = get_user_by('email', $email_candidate);
                if ($user_by_email) {
                    $user_id = $user_by_email->ID;
                }
            }
        }

        // 2-3) اگر نبود، جستجو بر اساس formatted login (selected format)
        if (!$user_id) {
            $formatted_login = PhoneHelper::format_username($e164, $format);
            if ($formatted_login) {
                $user_by_login = get_user_by('login', $formatted_login);
                if ($user_by_login) {
                    // Verify it's the same phone (by meta)
                    $existing_phone = get_user_meta($user_by_login->ID, Constant::META_PHONE_E164, true);
                    if ($existing_phone === $e164) {
                        $user_id = $user_by_login->ID;
                    }
                }
            }
        }

        // 2-4) اگر نبود، جستجو بر اساس legacy patterns (backward compatibility)
        if (!$user_id) {
            $legacy_login = 'bwdk_' . $e164;
            $legacy_email = 'bwdk+' . $e164 . '@no-mail.local';
            
            $user_by_legacy_login = get_user_by('login', $legacy_login);
            if ($user_by_legacy_login) {
                $existing_phone = get_user_meta($user_by_legacy_login->ID, Constant::META_PHONE_E164, true);
                if ($existing_phone === $e164) {
                    $user_id = $user_by_legacy_login->ID;
                }
            }
            
            if (!$user_id) {
                $user_by_legacy_email = get_user_by('email', $legacy_email);
                if ($user_by_legacy_email) {
                    $existing_phone = get_user_meta($user_by_legacy_email->ID, Constant::META_PHONE_E164, true);
                    if ($existing_phone === $e164) {
                        $user_id = $user_by_legacy_email->ID;
                    }
                }
            }
        }

        // 2-5) اگر نبود، روی billing_phone و (اختیاری) digits_phone با چند فرمت تست کن
        if (!$user_id) {
            $candidates = [
                PhoneHelper::format_for_site($e164, 'with_zero'),
                PhoneHelper::format_for_site($e164, 'no_zero'),
                PhoneHelper::format_for_site($e164, 'country_plus'),
                PhoneHelper::format_for_site($e164, 'country_no_plus'),
            ];
            $candidates = array_values(array_unique(array_filter($candidates)));

            foreach (['billing_phone', 'digits_phone'] as $meta_key) {
                $q = new \WP_User_Query([
                    'meta_query' => [[
                        'key'   => $meta_key,
                        'value' => $candidates,
                        'compare' => 'IN',
                    ]],
                    'number' => 1,
                    'fields' => 'ID',
                ]);
                if (!empty($q->get_results())) {
                    $user_id = (int) $q->get_results()[0];
                    break;
                }
            }
        }

        // 3) اگر نبود، بساز با فرمت انتخاب شده
        if (!$user_id) {
            $login = PhoneHelper::format_username($e164, $format);
            $email = PhoneHelper::format_email($e164);
            
            if (empty($login) || empty($email)) {
                $this->bwdk_note_debug($order, 'User create failed: invalid phone format', ['e164' => $e164, 'format' => $format]);
                return;
            }
            
            // Log format-based generation
            $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
            if ($logger) {
                $logger->info(
                    sprintf(
                        'BWDK USER CREATION: Format-based generation - order_id=%d e164=%s format=%s login=%s email=%s',
                        $order->get_id(),
                        $e164,
                        $format,
                        $login,
                        $email
                    ),
                    ['source' => 'bwdk_user_creation']
                );
            }
            
            // Handle username collision: if username exists, check if it's the same phone
            $original_login = $login;
            $suffix = 2;
            while (username_exists($login)) {
                $existing_user = get_user_by('login', $login);
                if ($existing_user) {
                    $existing_phone = get_user_meta($existing_user->ID, Constant::META_PHONE_E164, true);
                    if ($existing_phone === $e164) {
                        // Same phone - reuse this user
                        $user_id = $existing_user->ID;
                        if ($logger) {
                            $logger->info(
                                sprintf(
                                    'BWDK USER CREATION: Reusing existing user - order_id=%d user_id=%d login=%s (same phone)',
                                    $order->get_id(),
                                    $user_id,
                                    $login
                                ),
                                ['source' => 'bwdk_user_creation']
                            );
                        }
                        break;
                    }
                }
                // Different phone - append suffix
                $login = $original_login . '_' . $suffix;
                $suffix++;
            }
            
            // If we didn't find a user to reuse, create a new one
            if (!$user_id) {
                $pwd = wp_generate_password(16);
                
                $user_id = wp_insert_user([
                    'user_login' => $login,
                    'user_pass'  => $pwd,
                    'user_email' => $email,
                    'display_name' => $e164, // Use phone as display name
                    'role'       => 'customer',
                ]);

                if (!is_wp_error($user_id) && $user_id) {
                    update_user_meta($user_id, Constant::META_AUTO_CREATED, 1);
                    update_user_meta($user_id, Constant::META_PHONE_E164, $e164);
                    update_user_meta($user_id, 'billing_phone', PhoneHelper::format_for_site($e164, $format));
                    
                    if ($logger) {
                        $logger->info(
                            sprintf(
                                'BWDK USER CREATION: New user created - order_id=%d user_id=%d login=%s email=%s format=%s',
                                $order->get_id(),
                                $user_id,
                                $login,
                                $email,
                                $format
                            ),
                            ['source' => 'bwdk_user_creation']
                        );
                    }
                } else {
                    $this->bwdk_note_debug($order, 'User create failed', is_wp_error($user_id) ? $user_id->get_error_message() : $user_id);
                    $user_id = 0;
                }
            }
        }

        if ($user_id) {
            // 4) پرکردن فیلدها فقط اگر خالی هستند
            $site_phone = PhoneHelper::format_for_site($e164, $format);
            if (!get_user_meta($user_id, 'billing_phone', true)) {
                update_user_meta($user_id, 'billing_phone', $site_phone);
            }
            if (!get_user_meta($user_id, 'digits_phone', true)) {
                update_user_meta($user_id, 'digits_phone', $site_phone); // اختیاری ولی مفید
            }
            if (!get_user_meta($user_id, Constant::META_PHONE_E164, true)) {
                update_user_meta($user_id, Constant::META_PHONE_E164, $e164);
            }

            // first/last name & address از payload اگر خالی
            $first = (string)($payload['user']['first_name'] ?? '');
            $last  = (string)($payload['user']['last_name'] ?? '');
            if ($first && !get_user_meta($user_id, 'first_name', true)) {
                update_user_meta($user_id, 'first_name', $first);
            }
            if ($last && !get_user_meta($user_id, 'last_name', true)) {
                update_user_meta($user_id, 'last_name', $last);
            }

            // 5) اتصال سفارش به کاربر (Idempotent)
            if (!$order->get_customer_id()) {
                $order->set_customer_id($user_id);
                $order->save();
            }

            if (!$order->get_meta(Constant::META_USER_ATTACHED)) {
                $order->update_meta_data(Constant::META_USER_ATTACHED, 1);
                $order->save();
                $this->bwdk_add_note($order, "سفارش به کاربر با موبایل {$site_phone} متصل شد.", 'user_attached');
            }
        }
    }

}
