<?php

namespace BWDK\Integrations\Digify;

use BWDK\Support\Constant;
use BWDK\Support\OrderNoteHelper;
use BWDK\Support\WooCompatibility;
use WC_Order;
use WC_Order_Item_Product;

class Client
{
    use OrderNoteHelper;
    /**
     * Write verbose verify debug to PHP error log (only when WP_DEBUG is enabled).
     * This prevents large/sensitive payloads from ending up in Woo order notes.
     */
    private function bwdk_verify_error_log(string $message, array $context = []): void
    {
        if (!defined('WP_DEBUG') || !WP_DEBUG) {
            return;
        }

        $line = '[BWDK][VERIFY] ' . $message;
        if (!empty($context)) {
            $line .= ' ' . wp_json_encode($context, JSON_UNESCAPED_UNICODE);
        }

        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
        error_log($line);
    }
    /**
     * Normalize API key input to raw key (no "Authorization:" / "Api-Key" prefixes).
     *
     * Accepts values like:
     * - "dk123"
     * - "Api-Key dk123"
     * - "Authorization: Api-Key dk123"
     */
    protected function normalizeApiKey(string $value): string
    {
        $v = trim($value);
        if ($v === '') {
            return '';
        }

        // Remove optional leading "Authorization:" (case-insensitive)
        $v = preg_replace('/^\s*Authorization\s*:\s*/i', '', $v);
        if (!is_string($v)) {
            $v = '';
        }

        // Remove optional leading "Api-Key" prefix (case-insensitive)
        $v = preg_replace('/^\s*Api-Key\s+/i', '', $v);
        if (!is_string($v)) {
            $v = '';
        }

        return trim($v);
    }
    private function build_back_urls(WC_Order $order, string $base_url, array $extra = [], ?string $origin = null): array
    {
        $oid = (int) $order->get_id();

        $return_params = array_merge($extra, [
            'bwdk_back' => 1,
            'oid'       => $oid,
        ]);
        
        // Append origin parameter if provided
        if (!empty($origin) && in_array($origin, ['cart', 'checkout'], true)) {
            $return_params['origin'] = $origin;
        }

        $cancel_params = array_merge($extra, [
            'bwdk_cancel' => 1,
            'oid'         => $oid,
        ]);
        
        // Append origin parameter if provided
        if (!empty($origin) && in_array($origin, ['cart', 'checkout'], true)) {
            $cancel_params['origin'] = $origin;
        }

        $return_url = add_query_arg($return_params, $base_url);
        $cancel_url = add_query_arg($cancel_params, $base_url);

        return [$return_url, $cancel_url];
    }

    protected $base_url;
    protected $apiKey;
    protected $timeout;

    /** Default HTTP timeout (seconds). Use getRequestArgs() for filtered value. */
    private const DEFAULT_TIMEOUT = 15;

    public function __construct(
         $endpoint = null,
         $apiKey = null,
         $timeout = 10
    ) {
        $this->base_url = $endpoint ?: (get_option(Constant::BWDK_OPTION_NAME)["api"]["endpoint"]);
        $this->apiKey   = $apiKey   ?: (get_option(Constant::BWDK_OPTION_NAME)["api"]["api-key"]);
        $this->timeout  = is_numeric($timeout) && $timeout > 0 ? (float) $timeout : self::DEFAULT_TIMEOUT;
    }

    /**
     * Build request args for wp_remote_* so all BWDK API calls use a consistent timeout.
     * Filter: bwdk_http_timeout (int|float $timeout, string $endpoint, ?int $order_id)
     * Filter: bwdk_http_request_args (array $args, string $endpoint, ?int $order_id)
     *
     * @param string $endpoint Full URL or path identifier for logging
     * @param int|null $order_id Optional order ID for filter context
     * @param string $method 'GET' or 'POST'
     * @return array Args for wp_remote_request / wp_remote_post / wp_remote_get
     */
    protected function getRequestArgs(string $endpoint, ?int $order_id, string $method = 'GET'): array
    {
        $api_key = $this->normalizeApiKey((string) $this->apiKey);
        if ($api_key === '') {
            // Return args that will cause a clear error
            return [
                'timeout' => 1,
                'headers' => [],
            ];
        }

        $timeout = apply_filters('bwdk_http_timeout', $this->timeout, $endpoint, $order_id);
        $timeout = is_numeric($timeout) && $timeout > 0 ? (float) $timeout : self::DEFAULT_TIMEOUT;

        $args = [
            'timeout' => $timeout,
            'headers' => [
                'Authorization' => 'Api-Key ' . $api_key,
                'Content-Type'  => 'application/json; charset=utf-8',
                'Accept'        => 'application/json',
            ],
        ];
        if ($method === 'POST') {
            $args['data_format'] = 'body';
        }
        return apply_filters('bwdk_http_request_args', $args, $endpoint, $order_id);
    }

    /**
     * Extract backend error message from API response body.
     *
     * @param string $body_raw Raw response body
     * @return string|null Backend message or null
     */
    protected function extractApiErrorMessage(string $body_raw): ?string {
        $body = json_decode($body_raw, true);
        if (!is_array($body)) {
            return null;
        }

        $keys = ['detail', 'message', 'error'];
        foreach ($keys as $key) {
            if (isset($body[$key]) && is_string($body[$key]) && trim($body[$key]) !== '') {
                return trim($body[$key]);
            }
        }

        return null;
    }

    /**
     * Map transport errors from WP_Error to user-friendly messages.
     *
     * @param string $error_message WP_Error message
     * @return string User-friendly Persian message
     */
    protected function mapTransportError(string $error_message): string {
        $msg = strtolower($error_message);

        if (strpos($msg, 'timed out') !== false || strpos($msg, 'timeout') !== false) {
            return 'اتصال به سرویس خرید با دیجی‌کالا برقرار نشد. سرور سایت در زمان مناسب پاسخی دریافت نکرد (Timeout). احتمال اختلال در هاست، فایروال یا اینترنت سرور وجود دارد.';
        }

        if (strpos($msg, 'ssl') !== false || strpos($msg, 'certificate') !== false) {
            return 'اتصال امن به سرویس خرید با دیجی‌کالا برقرار نشد. گواهی SSL یا تنظیمات cURL/OpenSSL سرور را بررسی کنید.';
        }

        if (strpos($msg, 'could not resolve host') !== false || strpos($msg, 'name lookup timed out') !== false) {
            return 'سرور سایت نتوانست آدرس سرویس خرید با دیجی‌کالا را resolve کند. DNS یا محدودیت شبکه هاست را بررسی کنید.';
        }

        if (strpos($msg, 'failed to connect') !== false || strpos($msg, 'connection refused') !== false) {
            return 'سرور سایت نتوانست به سرویس خرید با دیجی‌کالا وصل شود. احتمال مسدود بودن اتصال خروجی روی هاست وجود دارد.';
        }

        return 'اتصال به سرویس خرید با دیجی‌کالا برقرار نشد. لطفاً لاگ خطا را بررسی کنید.';
    }

    /**
     * Process API response and return categorized error or success.
     *
     * @param mixed $response wp_remote_* response or WP_Error
     * @param string $endpoint URL for logging
     * @param string $method HTTP method
     * @return array ['success' => bool, 'data' => mixed, 'error' => string, 'code' => int]
     */
    protected function processApiResponse($response, string $endpoint, string $method = 'GET'): array {
        if (is_wp_error($response)) {
            $error_code = $response->get_error_code();
            $message = $response->get_error_message();

            // Fallback to all errors array if message is empty
            if (empty($message) && !empty($response->errors)) {
                $message = wp_json_encode($response->errors, JSON_UNESCAPED_UNICODE);
            }

            $message_lower = strtolower($message);

            $transient_error = false;
            $transient_reason = '';

            if (strpos($message_lower, 'timed out') !== false || strpos($message_lower, 'timeout') !== false) {
                $user_message = 'اتصال به سرویس خرید با دیجی‌کالا برقرار نشد (Timeout). احتمال اختلال در هاست وجود دارد.';
                $transient_error = true;
                $transient_reason = 'timeout';
            } elseif (strpos($message_lower, 'could not resolve host') !== false || strpos($message_lower, 'name lookup') !== false || strpos($message_lower, 'resolve') !== false) {
                $user_message = 'مشکل در DNS سرور. هاست نمی‌تواند به سرویس متصل شود.';
                $transient_error = true;
                $transient_reason = 'dns';
            } elseif (strpos($message_lower, 'ssl') !== false || strpos($message_lower, 'certificate') !== false) {
                $user_message = 'مشکل در SSL سرور. گواهی یا OpenSSL بررسی شود.';
                $transient_error = true;
                $transient_reason = 'ssl';
            } elseif (strpos($message_lower, 'failed to connect') !== false || strpos($message_lower, 'connection refused') !== false || strpos($message_lower, 'connect') !== false) {
                $user_message = 'اتصال به سرور BWDK برقرار نشد. احتمالاً outbound بسته است.';
                $transient_error = true;
                $transient_reason = 'connect';
            } else {
                $user_message = 'خطای ارتباط با سرویس خرید با دیجی‌کالا: ' . $message;
                $transient_error = true;
                $transient_reason = 'network';
            }

            $this->logApiError($endpoint, $method, 0, $error_code, $message, '');

            return [
                'success' => false,
                'data' => null,
                'error' => $user_message,
                'code' => 0,
                'transient_error' => $transient_error,
                'transient_reason' => $transient_reason,
            ];
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $body_raw = wp_remote_retrieve_body($response);

        if ($code === 401 || $code === 403) {
            $backend_msg = $this->extractApiErrorMessage($body_raw);
            $error = 'احراز هویت با سرویس خرید با دیجی‌کالا ناموفق بود. API Key نامعتبر است یا دسترسی این مرچنت/دامنه تأیید نشده است.';
            if ($backend_msg) {
                $error .= ' ' . $backend_msg;
            }

            $this->logApiError($endpoint, $method, $code, '', '', $body_raw);

            return [
                'success' => false,
                'data' => null,
                'error' => $error,
                'code' => $code,
            ];
        }

        if ($code >= 400) {
            $backend_msg = $this->extractApiErrorMessage($body_raw);
            if ($backend_msg) {
                $error = 'درخواست توسط سرویس خرید با دیجی‌کالا رد شد: ' . $backend_msg;
            } else {
                $error = 'از سرویس خرید با دیجی‌کالا پاسخ معتبر دریافت نشد. لطفاً لاگ خطا را بررسی کنید.';
            }

            $this->logApiError($endpoint, $method, $code, '', '', $body_raw);

            return [
                'success' => false,
                'data' => null,
                'error' => $error,
                'code' => $code,
            ];
        }

        $body = json_decode($body_raw, true);
        if ($body === null && $body_raw !== '') {
            $this->logApiError($endpoint, $method, $code, '', 'Invalid JSON', $body_raw);

            return [
                'success' => false,
                'data' => null,
                'error' => 'از سرویس خرید با دیجی‌کالا پاسخ معتبر دریافت نشد. لطفاً لاگ خطا را بررسی کنید.',
                'code' => $code,
            ];
        }

        return [
            'success' => true,
            'data' => $body,
            'error' => null,
            'code' => $code,
        ];
    }

    /**
     * Log API errors safely (without API key).
     *
     * @param string $endpoint
     * @param string $method
     * @param int $status_code
     * @param string $wp_error_code
     * @param string $wp_error_message
     * @param string $response_body
     */
    protected function logApiError(string $endpoint, string $method, int $status_code, string $wp_error_code, string $wp_error_message, string $response_body): void {
        $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
        if (!$logger) {
            return;
        }

        $log_data = [
            'endpoint' => $endpoint,
            'method' => $method,
            'status_code' => $status_code,
            'wp_error_code' => $wp_error_code,
            'wp_error_message' => $wp_error_message,
            'response_body_length' => strlen($response_body),
        ];

        $logger->error('BWDK API Error: ' . wp_json_encode($log_data, JSON_UNESCAPED_UNICODE), ['source' => 'bwdk_api']);
    }
    public function verifyOrder($order, $order_uuid)
    {
        if (!$order instanceof WC_Order) {
            return false;
        }

        // Resolve merchant_unique_id ONLY from _bwdk_merchant_unique_id meta
        // This is the exact value that was sent to BWDK API during Order Create
        // NEVER read from Sync/Update responses - only from _bwdk_merchant_unique_id (saved at order creation)
        $merchant_unique_id = $order->get_meta('_bwdk_merchant_unique_id');
        
        // If merchant_unique_id is empty, verify cannot proceed
        if (empty($merchant_unique_id)) {
            $error_message = 'merchant_unique_id هنگام ساخت سفارش ذخیره نشده، Verify ممکن نیست.';
            $this->bwdk_note_debug($order, 'BWDK Verify: ' . $error_message, [
                'order_uuid' => $order_uuid,
                'order_id' => $order->get_id(),
                'meta_key_checked' => '_bwdk_merchant_unique_id',
            ]);
            $order->add_order_note('BWDK Verify: ' . $error_message);
            return false;
        }

        // Log the merchant_unique_id that will be used for Verify BEFORE making the request
        $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
        if ($logger) {
            $logger->info(
                sprintf(
                    'BWDK Verify: merchant_unique_id_used - order_id=%d order_uuid=%s merchant_unique_id=%s',
                    $order->get_id(),
                    $order_uuid,
                    $merchant_unique_id
                ),
                ['source' => 'bwdk_verify']
            );
        }

        $verify_url = $this->base_url . 'orders/api/v1/manager/' . $order_uuid . '/verify/';
        $request_body = wp_json_encode(["merchant_unique_id" => $merchant_unique_id], JSON_UNESCAPED_UNICODE);
        $order_id = $order->get_id();

        // Order note: short, human-readable (no request payload / merchant_unique_id)
        $this->bwdk_note_debug(
            $order,
            sprintf('BWDK Verify: ارسال درخواست verify (Order: %s)', (string) $order_id)
        );
        // Full request details ONLY in PHP error log (when WP_DEBUG enabled)
        $this->bwdk_verify_error_log('request', [
            'order_id' => (int) $order_id,
            'order_uuid' => (string) $order_uuid,
            'url' => (string) $verify_url,
            'request_body' => (string) $request_body,
        ]);

        $request_args = $this->getRequestArgs($verify_url, $order_id, 'POST');
        $request_args['method'] = 'POST';
        $request_args['body']  = $request_body;
        $response = wp_remote_post($verify_url, $request_args);

        // Request not sent (network/DNS timeout or connection failure)
        if (is_wp_error($response)) {
            $error_code = $response->get_error_code();
            $error_message = $response->get_error_message();

            $this->bwdk_note_debug($order, 'BWDK Verify: request not sent (network/DNS)', [
                'error_code' => $error_code,
                'error_message' => $error_message,
                'order_uuid' => $order_uuid,
                'merchant_unique_id' => $merchant_unique_id,
            ]);

            $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
            if ($logger) {
                $logger->error(
                    sprintf(
                        'BWDK Verify: request not sent (network/DNS timeout) - order_id=%d order_uuid=%s error_code=%s error_message=%s',
                        $order_id,
                        $order_uuid,
                        $error_code,
                        $error_message
                    ),
                    ['source' => 'bwdk_verify']
                );
            }

            $order->add_order_note(
                sprintf('Verify ارسال نشد (خطای شبکه/DNS): %s', $error_message),
                false
            );

            return [
                'network_error' => true,
                'error_code'    => $error_code,
                'error_message' => $error_message,
            ];
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $body_raw = wp_remote_retrieve_body($response);
        $body = json_decode($body_raw, true);
        
        // Extract minimal summary fields for order note (avoid huge/sensitive payload)
        $status_display = is_array($body) ? (string) ($body['status_display'] ?? '') : '';
        if ($status_display === '' && is_array($body) && isset($body['status'])) {
            $status_display = (string) $body['status'];
        }
        if ($status_display === '') {
            $status_display = 'unknown';
        }

        $merchant_order_id = is_array($body) && isset($body['merchant_order_id'])
            ? (string) $body['merchant_order_id']
            : (string) $order_id;

        $note = sprintf('[DEBUG] BWDK Verify: %s (Order: %s)', $status_display, $merchant_order_id);
        if (!empty($order_uuid)) {
            $note .= sprintf(' (UUID: %s)', (string) $order_uuid);
        }

        // Full response ONLY in PHP error log (when WP_DEBUG enabled)
        $this->bwdk_verify_error_log('response', [
            'order_id' => (int) $order_id,
            'order_uuid' => (string) $order_uuid,
            'http_status_code' => (int) $code,
            'response_body' => (string) $body_raw,
        ]);
        
        if ($code == 200) {
            // Order note: short summary only (no full response JSON/body)
            $this->bwdk_add_note($order, $note);
            
            // Also log to WooCommerce logger
            $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
            if ($logger) {
                $logger->info(
                    sprintf(
                        'BWDK Verify Success - order_id=%d order_uuid=%s http_code=%d response_status=%s',
                        $order->get_id(),
                        $order_uuid,
                        $code,
                        isset($body['status']) ? (string)$body['status'] : 'N/A'
                    ),
                    ['source' => 'bwdk_verify']
                );
            }
            
            return $body;
        } else {
            // Verify rejected by server (non-2xx)
            $error_detail = '';
            if (is_array($body)) {
                if (isset($body['error']) && is_string($body['error'])) {
                    $error_detail = $body['error'];
                } elseif (isset($body['message']) && is_string($body['message'])) {
                    $error_detail = $body['message'];
                } elseif (isset($body['detail']) && is_string($body['detail'])) {
                    $error_detail = $body['detail'];
                } elseif (isset($body['non_field_errors']) && is_array($body['non_field_errors']) && !empty($body['non_field_errors'])) {
                    $error_detail = is_string($body['non_field_errors'][0]) ? $body['non_field_errors'][0] : '';
                }
            }
            if (empty($error_detail)) {
                $error_detail = !empty($body_raw) ? substr($body_raw, 0, 500) : sprintf('HTTP %d', $code);
            }

            $body_excerpt = !empty($body_raw) ? substr(wp_strip_all_tags($body_raw), 0, 300) : '';

            // Order note: short summary only (no full response JSON/body)
            $this->bwdk_add_note($order, $note);

            $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
            if ($logger) {
                $logger->error(
                    sprintf(
                        'BWDK Verify: rejected by server - order_id=%d order_uuid=%s status_code=%d error_detail=%s body_excerpt=%s',
                        $order_id,
                        $order_uuid,
                        $code,
                        $error_detail,
                        $body_excerpt
                    ),
                    ['source' => 'bwdk_verify']
                );
            }
            
            // Add specific note for 400 errors with actual error message from API
            if ($code == 400) {
                $order->add_order_note(sprintf(
                    'BWDK Verify Error: HTTP 400 - %s. order_uuid=%s merchant_unique_id_used=%s',
                    $error_detail,
                    $order_uuid,
                    $merchant_unique_id
                ));
            } else {
                // Log other HTTP errors
                $order->add_order_note(sprintf(
                    'BWDK Verify Error: HTTP %d - %s',
                    $code,
                    $error_detail
                ));
            }
        }

        return false;
    }
    public function refundOrder($order_uuid)
    {
        $url = $this->base_url . 'orders/api/v1/manager/'.$order_uuid.'/refund/';
        $args = $this->getRequestArgs($url, null, 'POST');
        $args['method'] = 'POST';
        $response = wp_remote_post($url, $args);


        if (is_wp_error($response)) {
            // Note: We can't add order note here because we don't have the order object
            // The note will be added in AjaxHandler::refund_digipay
            return false;
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $body_raw = wp_remote_retrieve_body($response);
        $body = json_decode($body_raw, true);

        if ($code==200 || $code==400){
            return $body;
        }

        // Note: We can't add order note here because we don't have the order object
        // The note will be added in AjaxHandler::refund_digipay

        return false;


    }
    public function createRemoteOrder($order, ?string $origin = null): array
    {

        $payload = $this->buildPayload($order);

        // IMPORTANT: Save merchant_unique_id that will be sent to BWDK API
        // This is the value that must be used later for Verify endpoint
        // Store it in _bwdk_merchant_unique_id meta key (never overwrite this after order creation)
        $merchant_unique_id = isset($payload['merchant_unique_id']) ? (string) $payload['merchant_unique_id'] : '';
        if (!empty($merchant_unique_id)) {
            // Only save if not already set (protect against overwrites, especially for reused orders)
            $existing_merchant_unique_id = $order->get_meta('_bwdk_merchant_unique_id');
            if (empty($existing_merchant_unique_id)) {
                // New order - save merchant_unique_id
                $order->update_meta_data('_bwdk_merchant_unique_id', $merchant_unique_id);
                $order->save();
                
                // Log the saved merchant_unique_id
                $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
                if ($logger) {
                    $logger->info(
                        sprintf(
                            'BWDK Order Create: merchant_unique_id saved - order_id=%d merchant_unique_id=%s (new order)',
                            $order->get_id(),
                            $merchant_unique_id
                        ),
                        ['source' => 'bwdk_order_create']
                    );
                }
            } else {
                // Reused order - keep existing merchant_unique_id (paired with existing order_uuid)
                $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
                if ($logger) {
                    $logger->info(
                        sprintf(
                            'BWDK Order Create: merchant_unique_id reused - order_id=%d existing_merchant_unique_id=%s payload_merchant_unique_id=%s (order reused, keeping existing)',
                            $order->get_id(),
                            $existing_merchant_unique_id,
                            $merchant_unique_id
                        ),
                        ['source' => 'bwdk_order_create']
                    );
                }
            }
        }

		// Temporary debug log before posting (single-flow only)
		if ((bool) $order->get_meta(Constant::META_BWDK_SINGLE_FLOW)) {
			$logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
			if ($logger && is_array($payload)) {
				$ctx = ['source' => 'bwdk_single_price_fix'];
				$logger->info(
					sprintf(
						'Single flow: order_id=%d primary=%d final=%d discount=%d',
						$order->get_id(),
						(int) ($payload['main_amount'] ?? 0),
						(int) ($payload['final_amount'] ?? 0),
						(int) ($payload['discount_amount'] ?? 0)
					),
					$ctx
				);
			}
		}


        $this->log_back_urls($order, $payload);

        // Set reservation_expired_at right before request to ensure it's at least 15 minutes in the future
        $now = new \DateTime('now', new \DateTimeZone('UTC'));
        $reservation_expired = clone $now;
        $reservation_expired->modify('+30 minutes');
        $payload['reservation_expired_at'] = (int) $reservation_expired->getTimestamp(); // Unix timestamp (seconds)

        // Debug log for reservation timing
        $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
        if ($logger) {
            $ctx = ['source' => 'bwdk_reservation_timing'];
            $logger->info(
                sprintf(
                    'Reservation timing: order_id=%d now=%s reservation_expired_at=%d (%s)',
                    $order->get_id(),
                    $now->format('c'),
                    $payload['reservation_expired_at'],
                    $reservation_expired->format('c')
                ),
                $ctx
            );
        }

        // Sanitize monetary amounts to strict integers before API call
        $payload = $this->sanitizeAmountsToIntegers($payload, $order);

        // Generate request ID and start time for diagnostic logging
        $req_id = function_exists('wp_generate_uuid') ? wp_generate_uuid() : uniqid('req_', true);
        $start_time = microtime(true);
        $endpoint_url = $this->base_url . 'orders/api/v1/create-order/';
        $payload_snapshot = $this->createPayloadSnapshot($payload);

        // Prepare request body
        $request_body = wp_json_encode($payload, JSON_UNESCAPED_UNICODE);

        // Log request body for debugging
        $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
        if ($logger) {
            $ctx = ['source' => 'bwdk_digify_request'];
            $logger->info(
                sprintf(
                    'Digify API Request Body - order_id=%d req_id=%s: %s',
                    $order->get_id(),
                    $req_id,
                    $request_body
                ),
                $ctx
            );
        }

        $request_args = $this->getRequestArgs($endpoint_url, $order->get_id(), 'POST');
        $request_args['method'] = 'POST';
        $request_args['body']   = $request_body;
        $response = wp_remote_post($endpoint_url, $request_args);

        $duration_ms = round((microtime(true) - $start_time) * 1000, 2);

        if (is_wp_error($response)) {
            // Log WP_Error diagnostic
            $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
            if ($logger) {
                $ctx = ['source' => 'bwdk_digify_http'];
                $log_data = [
                    'req_id' => $req_id,
                    'order_id' => $order->get_id(),
                    'endpoint' => $endpoint_url,
                    'duration_ms' => $duration_ms,
                    'wp_error_code' => $response->get_error_code(),
                    'wp_error_message' => $response->get_error_message(),
                    'site_url' => home_url(),
                    'php_version' => PHP_VERSION,
                    'currency' => get_option('woocommerce_currency'),
                    'payload_snapshot' => $payload_snapshot,
                ];
                $logger->error(
                    'Digify API WP_Error: ' . wp_json_encode($log_data, JSON_UNESCAPED_UNICODE),
                    $ctx
                );
            }

            if ($order instanceof WC_Order) {
                $this->bwdk_note_debug($order, 'خطا در ساخت سفارش ریموت', [
                    'error' => $response->get_error_message(),
                    'code' => $response->get_error_code(),
                ]);
            }
            $order->delete();
            return ['ok' => false, 'code' => 0, 'error' => $response->get_error_message()];
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $body_raw = wp_remote_retrieve_body($response);
        $body = json_decode($body_raw, true);
        
        if ($code >= 400) {
            // Log HTTP error diagnostic
            $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
            if ($logger) {
                $ctx = ['source' => 'bwdk_digify_http'];
                
                // Extract useful headers for WAF/CDN detection
                $headers = wp_remote_retrieve_headers($response);
                $selected_headers = [];
                $header_keys = ['server', 'content-type', 'cf-ray', 'cf-cache-status', 'x-request-id', 'x-correlation-id', 'x-powered-by'];
                foreach ($header_keys as $key) {
                    if ($headers->offsetExists($key)) {
                        $selected_headers[$key] = $headers->offsetGet($key);
                    }
                }

                $log_data = [
                    'req_id' => $req_id,
                    'order_id' => $order->get_id(),
                    'endpoint' => $endpoint_url,
                    'duration_ms' => $duration_ms,
                    'http_code' => $code,
                    'response_body' => $body_raw,
                    'response_headers' => $selected_headers,
                    'currency' => get_option('woocommerce_currency'),
                    'payload_snapshot' => $payload_snapshot,
                ];
                $logger->error(
                    'Digify API HTTP Error: ' . wp_json_encode($log_data, JSON_UNESCAPED_UNICODE),
                    $ctx
                );
            }

            if ($order instanceof WC_Order) {
                $this->bwdk_note_debug($order, 'خطا در ساخت سفارش ریموت (HTTP)', [
                    'code' => $code,
                    'body' => $body_raw,
                ]);
            }
        }



        if ($code >= 200 && $code < 300 && is_array($body)) {
            $this->log_payload_snapshot($payload);

            return [
                'ok'              => true,
                'code'            => $code,
                'order_start_url'=> $body['order_start_url'] ?? null,
                'order_uuid'      => $body['order_uuid'] ?? null,
                'raw'             => $body,
            ];
        }


        $order->delete();
        return ['ok' => false, 'code' => $code, 'error' => $body_raw ?: ('HTTP '.$code)];
    }
    public function merchantsAuthStatus($apiKey)
    {
        $url = rtrim($this->base_url, '/') . '/merchants/api/v1/auth/status';
        $args = $this->getRequestArgs($url, null, 'GET');
        $rawKey = $this->normalizeApiKey((string) $apiKey);
        $args['headers']['Authorization'] = $rawKey !== '' ? ('Api-Key ' . $rawKey) : '';
        $response = wp_remote_get($url, $args);

        $result = $this->processApiResponse($response, $url, 'GET');

        if ($result['success']) {
            return [
                'success' => true,
                'authenticated' => (bool) ($result['data']['is_authenticated'] ?? false),
                'error' => null,
                'transient_error' => false,
            ];
        }

        // If transport/network failed (timeout, DNS, SSL, connect), expose a detectable transient state
        if (!empty($result['transient_error'])) {
            return [
                'success' => false,
                'authenticated' => false,
                'transient_error' => true,
                'error' => $result['transient_reason'] ?: 'network',
            ];
        }

        return [
            'success' => false,
            'authenticated' => false,
            'error' => $result['error'] ?? 'احراز هویت ناموفق',
            'transient_error' => false,
        ];
    }
    public function orderGetStatus($order_uuid): array
    {
        $url = $this->base_url.'orders/api/v1/manager/'.$order_uuid.'/';
        $args = $this->getRequestArgs($url, null, 'GET');
        $response = wp_remote_get($url, $args);

        $result = $this->processApiResponse($response, $url, 'GET');

        if ($result['success']) {
            return [
                'ok' => true,
                'code' => $result['code'],
                'raw' => $result['data'],
            ];
        }

        return [
            'ok' => false,
            'code' => $result['code'],
            'error' => $result['error'],
        ];
    }



    protected function buildPayload($order): array
    {
        $items = [];
        $is_single_flow = (bool) $order->get_meta(Constant::META_BWDK_SINGLE_FLOW);
        $single_main_amount = 0.0;
        $single_final_amount = 0.0;
        $single_discount_amount = 0.0;

        $line_context = [];

        foreach ($order->get_items('line_item') as $item) {

            if (!$item instanceof WC_Order_Item_Product) continue;


            $product = $item->get_product();
			$qty           = max(1, (int) $item->get_quantity());
			$line_subtotal = (float) $item->get_subtotal(); // Woo major unit
			$line_total    = (float) $item->get_total();    // Woo major unit

			$primary_amount_unit  = $qty ? round($line_subtotal / $qty, 2) : $line_subtotal; // Woo float
			$final_amount_unit    = $qty ? round($line_total    / $qty, 2) : $line_total;    // Woo float
			$discount_total_line  = max(0.0, round($line_subtotal - $line_total, 2));        // Woo float

			$primary_amount  = WooCompatibility::convert_to_BWDK_currency($primary_amount_unit);
			$amount          = WooCompatibility::convert_to_BWDK_currency($final_amount_unit);
			$discount_amount = WooCompatibility::convert_to_BWDK_currency($discount_total_line);
			// FIX: Use subtotal_tax (tax BEFORE discount) instead of total_tax (tax AFTER discount)
			// This ensures coupon/discount codes only affect product price, not tax_amount
			$tax_amount      = WooCompatibility::convert_to_BWDK_currency((float) $item->get_subtotal_tax());

            if ($is_single_flow) {
                $pricing = $this->calculateSingleProductFlowAmounts($product instanceof \WC_Product ? $product : null, $item, $qty);

                $primary_amount  = $pricing['primary'];
                $amount          = $pricing['final'];
                $discount_amount = $pricing['discount'];
				// FIX: Use subtotal_tax (tax BEFORE discount) instead of total_tax (tax AFTER discount)
				// This ensures coupon/discount codes only affect product price, not tax_amount
				$tax_amount      = WooCompatibility::convert_to_BWDK_currency((float) $item->get_subtotal_tax());

                $single_main_amount     += $pricing['primary'];
                $single_final_amount    += $pricing['final'];
                $single_discount_amount += $pricing['discount'];
            }

            $options = [];
            if ( $product ) {
                foreach ( $item->get_meta_data() as $meta ) {
                    $raw_key   = $meta->key;
                    $raw_value=$meta->value;

                    if (
                        strpos($raw_key, 'pa_') === 0 ||
                        strpos($raw_key, 'attribute_pa_') === 0 ||
                        strpos($raw_key, 'attribute_') === 0
                    ) {
                        $tax = str_replace('attribute_', '', $raw_key);

                        $label = wc_attribute_label($tax, $product);


                        if ( taxonomy_exists($tax) ) {
                            $term = get_term_by('slug', (string) $raw_value, $tax);
                            $value_readable = $term ? $term->name : (string) $raw_value;
                        } else {
                            $value_readable = (string) $raw_value;
                        }





                        $options[] = [
                            "type_name"=>"other",
                            "name"=>$label." : ".$value_readable ?: $raw_key ." ".$value_readable,
                            "value"=>$label." : ".$value_readable ?: $raw_key ." ".$value_readable,
                            "is_color"=>false,

                        ];
                    }
                }
            }

            $items[] = [
                'name'            => $item->get_name(),
                'image_link'      => ($product && $product->get_image_id()) ? wp_get_attachment_image_url($product->get_image_id()) : "",
                'primary_amount'  => $primary_amount,
                'amount'          => $amount,
                'count'           => $qty,
                'discount_amount' => $discount_amount,
                'tax_amount'      => $tax_amount,
                'pure_data'       => new \stdClass(),
                'options'         => $options,
            ];

            if (!$is_single_flow) {
                $line_context[] = [
                    'index'    => array_key_last($items),
                    'qty'      => $qty,
                    'subtotal' => round((float) $item->get_subtotal(), 2),
                    'total'    => round((float) $item->get_total(), 2),
                ];
            }








        }

        if (!$is_single_flow && !empty($line_context)) {
            $order_discount_total = round((float) $order->get_discount_total(), 2);

            if ($order_discount_total > 0) {
                $allocated_discount = 0.0;

                foreach ($line_context as $ctx) {
                    $allocated_discount += max(0, $ctx['subtotal'] - $ctx['total']);
                }

                $residual_discount = max(0.0, round($order_discount_total - $allocated_discount, 2));

                if ($residual_discount > 0) {
                    $subtotal_sum = array_sum(array_column($line_context, 'subtotal'));
                    $remaining    = $residual_discount;
                    $last_key     = array_key_last($line_context);

                    foreach ($line_context as $key => $ctx) {
                        $idx       = $ctx['index'];
                        $qty       = max(1, (int) $ctx['qty']);
                        $subtotal  = $ctx['subtotal'];
                        $line_total = $ctx['total'];

                        if ($subtotal_sum <= 0) {
                            break;
                        }

                        $share = $subtotal > 0 ? round(($subtotal / $subtotal_sum) * $residual_discount, 2) : 0.0;

                        if ($key === $last_key) {
                            $share = round($remaining, 2);
                        } else {
                            $remaining -= $share;
                        }

                        if ($share <= 0) {
                            continue;
                        }

                        $adjusted_total = max(0.0, round($line_total - $share, 2));
                        $adjusted_unit  = $adjusted_total / $qty;

                        $items[$idx]['amount'] = WooCompatibility::convert_to_BWDK_currency($adjusted_unit);

                        $primary_line_total  = $subtotal;
                        $final_line_total    = $adjusted_total;
                        $line_discount_total = max(0.0, round($primary_line_total - $final_line_total, 2));

                        $items[$idx]['discount_amount'] = WooCompatibility::convert_to_BWDK_currency($line_discount_total);

                        $line_context[$key]['total'] = $adjusted_total;
                    }
                }
            }
        }

        $callbackUrl = home_url( Constant::BWDK_CB_URL );

        // DIAGNOSTIC: Log callback_url being sent to Digify
        $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
        if ($logger) {
            $logger->info(
                sprintf(
                    'BWDK DIAGNOSTIC: CALLBACK_URL SENT TO DIGIFY - order_id=%d callback_url=%s home_url=%s BWDK_CB_URL=%s',
                    $order->get_id(),
                    $callbackUrl,
                    home_url(),
                    Constant::BWDK_CB_URL
                ),
                ['source' => 'bwdk_callback_diagnostic']
            );
        }

        $order->save();



        // FIX: Calculate tax_amount from sum of item subtotal_tax (tax BEFORE discount)
        // This ensures tax_amount is not affected by coupon/discount codes
        $items_tax_sum = 0.0;
        foreach ($order->get_items('line_item') as $item) {
            if ($item instanceof WC_Order_Item_Product) {
                $items_tax_sum += (float) $item->get_subtotal_tax();
            }
        }

        $payload = [
            'merchant_order_id' => $order->get_id(),
            // IMPORTANT: For reused orders, use existing _bwdk_merchant_unique_id (paired with order_uuid)
            // For new orders, use META_BWDK_WOO_ORDER_UUID (will be saved to _bwdk_merchant_unique_id after create)
            'merchant_unique_id' => $order->get_meta('_bwdk_merchant_unique_id') ?: $order->get_meta(Constant::META_BWDK_WOO_ORDER_UUID),
			'main_amount'       =>  WooCompatibility::convert_to_BWDK_currency((float) $order->get_subtotal()),
			'final_amount'      =>  WooCompatibility::convert_to_BWDK_currency((float) $order->get_total()),
			'discount_amount'   =>  WooCompatibility::convert_to_BWDK_currency((float) $order->get_discount_total()),
			// FIX: Use sum of item subtotal_tax (tax BEFORE discount) instead of order->get_total_tax() (tax AFTER discount)
			// This ensures coupon/discount codes only affect product price, not tax_amount
			'tax_amount'        =>  WooCompatibility::convert_to_BWDK_currency($items_tax_sum),
			'shipping_amount'   =>  WooCompatibility::convert_to_BWDK_currency((float) $order->get_shipping_total()),
            'loyalty_amount'    => 0.0,
            'callback_url'      => $callbackUrl,
            'items'             => $items,
        ];

		// For cart/checkout flow, recompute order-level amounts from items only and force shipping to zero.
		if (!$is_single_flow) {
			$items_primary_sum  = 0.0;
			$items_final_sum    = 0.0;
			$items_discount_sum = 0.0;
			$items_tax_sum_recalc = 0.0; // Recalculate tax from items array for consistency

			foreach ($items as $it) {
				$count = isset($it['count']) ? (int) $it['count'] : 1;
				$primary_unit  = isset($it['primary_amount']) ? (float) $it['primary_amount'] : 0.0;
				$final_unit    = isset($it['amount']) ? (float) $it['amount'] : 0.0;
				$discount_line = isset($it['discount_amount']) ? (float) $it['discount_amount'] : 0.0;
				$tax_line      = isset($it['tax_amount']) ? (float) $it['tax_amount'] : 0.0;

				$items_primary_sum  += $primary_unit * $count;
				$items_final_sum    += $final_unit   * $count;
				$items_discount_sum += $discount_line;
				$items_tax_sum_recalc += $tax_line; // Sum tax from items (already in BWDK currency)
			}

			$payload['main_amount']     = round($items_primary_sum, 2);
			$payload['final_amount']    = round($items_final_sum, 2);
			$payload['discount_amount'] = round($items_discount_sum, 2);
			// FIX: Recalculate tax_amount from items array to ensure consistency
			// Items already have tax_amount from subtotal_tax (before discount), so this preserves correct tax
			$payload['tax_amount']      = round($items_tax_sum_recalc, 2);
			$payload['shipping_amount'] = 0.0;
		}

        if ($is_single_flow) {
            $payload['main_amount']     = $single_main_amount ?: $payload['main_amount'];

            // KEEP final_amount from Woo (already tax-inclusive) to avoid dropping VAT in gateway
            // $payload['final_amount'] = ...  // ← remove this line

            $payload['discount_amount'] = $single_discount_amount ?: $payload['discount_amount'];

            $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
            if ($logger) {
                $ctx = ['source' => 'bwdk_single_pricing'];
                $logger->info(
                    sprintf(
                        'Single flow pricing: order_id=%d, primary=%s, final=%s, discount=%s',
                        $order->get_id(),
                        $single_main_amount,
                        $single_final_amount,
                        $single_discount_amount
                    ),
                    $ctx
                );
            }
        }

        // Add return_url and cancel_url
        // IMPORTANT: Use admin-ajax.php as base URL and append origin parameter for deterministic redirect
        $admin_ajax_url = admin_url('admin-ajax.php');
        
        // If origin not provided, try to get from order meta
        if (empty($origin)) {
            $origin = $order->get_meta('_bwdk_origin');
        }
        
        $is_single_flow = (bool) $order->get_meta(Constant::META_BWDK_SINGLE_FLOW);
        if ($is_single_flow) {
            $product_id = (int) $order->get_meta('_bwdk_single_product_id');
            // For single flow, still use admin-ajax but include product ID
            [$return_url, $cancel_url] = $this->build_back_urls($order, $admin_ajax_url, [
                'pid' => $product_id,
            ], $origin);
        } else {
            // For cart/checkout flow, use admin-ajax with origin parameter
            [$return_url, $cancel_url] = $this->build_back_urls($order, $admin_ajax_url, [], $origin);
        }
        
        // Log return_url and cancel_url generation
        $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
        if ($logger) {
            $logger->info(
                sprintf(
                    'BWDK RETURN TRACE: Return/Cancel URLs generated - order_id=%d origin=%s return_url=%s cancel_url=%s',
                    $order->get_id(),
                    $origin ?: 'N/A',
                    $return_url,
                    $cancel_url
                ),
                ['source' => 'bwdk_return_trace']
            );
        }

        $payload['return_url'] = $return_url;
        $payload['cancel_url'] = $cancel_url;

        // --- BEGIN: force tax-inclusive final_amount for gateway ---
        // Items IN PAYLOAD are already in Toman. Sum without re-conversion.
        $sum_items = 0.0;
        foreach ($payload['items'] as $it) {
            $unit = isset($it['amount']) ? (float) $it['amount'] : 0.0; // Toman already
            $cnt  = isset($it['count'])  ? (int) $it['count']   : 1;
            $sum_items += $unit * max(1, $cnt);
        }

        // tax_amount / shipping_amount in payload are ALREADY in Toman
        $tax_total      = isset($payload['tax_amount'])      ? (float) $payload['tax_amount']      : 0.0;
        $shipping_total = isset($payload['shipping_amount']) ? (float) $payload['shipping_amount'] : 0.0;

        // If you have packing and it should be charged at gateway, ensure it is already in Toman before adding here
        $packing_total  = isset($payload['packing_amount']) ? (float) $payload['packing_amount'] : 0.0;
        // If packing is stored as raw Woo currency, convert it once:
        $packing_wc = (float) $order->get_meta('_bwdk_packing_amount', true);
        if ($packing_wc > 0 && $packing_total <= 0) {
            $packing_total = WooCompatibility::convert_to_BWDK_currency(
                round($packing_wc, 2)
            );
        }

        // DO NOT call convert_to_BWDK_currency here again:
        $payload['final_amount'] = round($sum_items + $tax_total + $shipping_total + $packing_total, 2);
        // --- END: force tax-inclusive final_amount for gateway ---

        return apply_filters('bwdk/digify/payload', $payload, $order);
    }

    protected function calculateSingleProductFlowAmounts(?\WC_Product $product, WC_Order_Item_Product $item, int $qty): array
    {
        $qty = max(1, $qty);

        $regular = $this->resolveRegularPrice($product);
        $final   = $this->resolveFinalPrice($product);

		// Fallbacks only when missing; do not overwrite valid discounted prices
		if ($regular <= 0) {
			$regular = $qty > 0 ? (float) $item->get_subtotal() / $qty : (float) $item->get_subtotal();
		}

		if ($final <= 0) {
			$final = $qty > 0 ? (float) $item->get_total() / $qty : (float) $item->get_total();
		}

		if ($regular <= 0 && $final <= 0) {
			$regular = 0.0;
			$final   = 0.0;
		}

        $primary_total  = round($regular * $qty, 2);
        $final_total    = round($final * $qty, 2);
        $discount_total = max(0.0, round($primary_total - $final_total, 2));

		// Temporary debug log (remove before release)
		$logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
		if ($logger) {
			$pid = $product instanceof \WC_Product ? (int) $product->get_id() : (int) $item->get_product_id();
			$ctx = ['source' => 'bwdk_single_price_fix'];
			$logger->info(
				sprintf(
					'Single flow calc: pid=%d regular=%.4f final=%.4f qty=%d primary_total=%.4f final_total=%.4f',
					$pid,
					$regular,
					$final,
					$qty,
					$primary_total,
					$final_total
				),
				$ctx
			);
		}

        return [
            'primary'  => WooCompatibility::convert_to_BWDK_currency($primary_total),
            'final'    => WooCompatibility::convert_to_BWDK_currency($final_total),
            'discount' => WooCompatibility::convert_to_BWDK_currency($discount_total),
        ];
    }

    protected function resolveRegularPrice(?\WC_Product $product): float
    {
        if (!$product instanceof \WC_Product) {
            return 0.0;
        }

        $regular = (float) $product->get_regular_price();
        if ($regular > 0) {
            return $regular;
        }

        if ($product->is_type('variation')) {
            $parent_id = $product->get_parent_id();
            if ($parent_id) {
                $parent = wc_get_product($parent_id);
                if ($parent instanceof \WC_Product) {
                    $regular = (float) $parent->get_regular_price();
                    if ($regular > 0) {
                        return $regular;
                    }
                }
            }
        }

        $meta_regular = (float) $product->get_meta('_regular_price', true);
        if ($meta_regular > 0) {
            return $meta_regular;
        }

        return 0.0;
    }

    protected function resolveFinalPrice(?\WC_Product $product): float
    {
        if (!$product instanceof \WC_Product) {
            return 0.0;
        }

        $display_price = function_exists('wc_get_price_to_display')
            ? (float) wc_get_price_to_display($product)
            : 0.0;

        if ($display_price > 0) {
            return $display_price;
        }

        $active_price = (float) $product->get_price();
        if ($active_price > 0) {
            return $active_price;
        }

        if ($product->is_type('variation')) {
            $parent_id = $product->get_parent_id();
            if ($parent_id) {
                $parent = wc_get_product($parent_id);
                if ($parent instanceof \WC_Product) {
                    $parent_display_price = function_exists('wc_get_price_to_display')
                        ? (float) wc_get_price_to_display($parent)
                        : 0.0;

                    if ($parent_display_price > 0) {
                        return $parent_display_price;
                    }

                    $active_parent_price = (float) $parent->get_price();
                    if ($active_parent_price > 0) {
                        return $active_parent_price;
                    }
                }
            }
        }

        return 0.0;
    }

    /**
     * Sanitize monetary amounts to strict integers for Digify API
     * Handles strings, floats, formatted numbers (e.g. "120,000", "120000.0")
     */
    private function sanitizeAmountToInteger($value): int
    {
        // Handle null or empty string
        if ($value === null || $value === '') {
            return 0;
        }

        // If already int, ensure non-negative
        if (is_int($value)) {
            return max(0, $value);
        }

        // Convert to string, remove non-numeric characters (except decimal point and minus)
        $cleaned = is_string($value) ? $value : (string) $value;
        $cleaned = preg_replace('/[^\d.-]/', '', $cleaned);
        
        // Handle edge cases: empty after cleaning, '-', '.', '-.'
        if ($cleaned === '' || $cleaned === '-' || $cleaned === '.' || $cleaned === '-.') {
            return 0;
        }
        
        // Convert to float, round, then cast to int
        $float_value = (float) $cleaned;
        $rounded = round($float_value);
        
        return (int) max(0, $rounded); // Ensure non-negative
    }

    /**
     * Sanitize reservation_expired_at to integer Unix timestamp
     */
    private function sanitizeReservationExpiredAt($value): int
    {
        // If already int, return as-is
        if (is_int($value)) {
            return $value;
        }

        // If it's a numeric string, cast to int
        if (is_string($value) && is_numeric($value)) {
            return (int) $value;
        }

        // If it's an ISO8601 string, parse it and convert to timestamp
        if (is_string($value)) {
            try {
                $dt = new \DateTime($value, new \DateTimeZone('UTC'));
                return (int) $dt->getTimestamp();
            } catch (\Exception $e) {
                // Parsing failed, fallback to now+30min timestamp
                $fallback = new \DateTime('now', new \DateTimeZone('UTC'));
                $fallback->modify('+30 minutes');
                return (int) $fallback->getTimestamp();
            }
        }

        // Fallback: now+30min timestamp
        $fallback = new \DateTime('now', new \DateTimeZone('UTC'));
        $fallback->modify('+30 minutes');
        return (int) $fallback->getTimestamp();
    }

    /**
     * Sanitize all monetary fields in payload to strict integers
     */
    private function sanitizeAmountsToIntegers(array $payload, WC_Order $order): array
    {
        // Sanitize reservation_expired_at to integer Unix timestamp
        if (isset($payload['reservation_expired_at'])) {
            $payload['reservation_expired_at'] = $this->sanitizeReservationExpiredAt($payload['reservation_expired_at']);
        }

        // Sanitize top-level monetary fields
        $monetary_fields = [
            'main_amount',
            'final_amount',
            'discount_amount',
            'tax_amount',
            'shipping_amount',
            'packing_amount',
            'loyalty_amount',
        ];

        foreach ($monetary_fields as $field) {
            if (isset($payload[$field])) {
                $payload[$field] = $this->sanitizeAmountToInteger($payload[$field]);
            }
        }

        // Sanitize item-level monetary fields and ensure count is int >= 1
        if (isset($payload['items']) && is_array($payload['items'])) {
            foreach ($payload['items'] as &$item) {
                // Sanitize item monetary fields
                $item_monetary_fields = [
                    'primary_amount',
                    'amount',
                    'discount_amount',
                    'tax_amount',
                ];

                foreach ($item_monetary_fields as $field) {
                    if (isset($item[$field])) {
                        $item[$field] = $this->sanitizeAmountToInteger($item[$field]);
                    }
                }

                // Ensure count is int >= 1
                if (isset($item['count'])) {
                    $item['count'] = max(1, (int) $item['count']);
                }
            }
            unset($item); // Break reference
        }

        // Debug log before API call
        $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
        if ($logger) {
            $ctx = ['source' => 'bwdk_amount_sanitization'];
            $logger->info(
                sprintf(
                    'Amount sanitization: order_id=%d currency=%s main=%d final=%d tax=%d ship=%d',
                    $order->get_id(),
                    get_option('woocommerce_currency'),
                    $payload['main_amount'] ?? 0,
                    $payload['final_amount'] ?? 0,
                    $payload['tax_amount'] ?? 0,
                    $payload['shipping_amount'] ?? 0
                ),
                $ctx
            );
        }

        return $payload;
    }

    /**
     * Create a safe payload snapshot for logging (removes heavy fields, truncates strings)
     */
    private function createPayloadSnapshot(array $payload): array
    {
        $snapshot = [
            'main_amount' => $payload['main_amount'] ?? null,
            'final_amount' => $payload['final_amount'] ?? null,
            'tax_amount' => $payload['tax_amount'] ?? null,
            'shipping_amount' => $payload['shipping_amount'] ?? null,
            'items' => [],
        ];

        // Keep at most first 5 items, remove heavy fields
        if (isset($payload['items']) && is_array($payload['items'])) {
            $items_slice = array_slice($payload['items'], 0, 5);
            foreach ($items_slice as $item) {
                $item_snapshot = [
                    'name' => isset($item['name']) ? substr($item['name'], 0, 100) : null,
                    'primary_amount' => $item['primary_amount'] ?? null,
                    'amount' => $item['amount'] ?? null,
                    'count' => $item['count'] ?? null,
                ];
                
                // Truncate image_link if present
                if (isset($item['image_link']) && is_string($item['image_link'])) {
                    $item_snapshot['image_link'] = substr($item['image_link'], 0, 100);
                }
                
                $snapshot['items'][] = $item_snapshot;
            }
        }

        return $snapshot;
    }

    private function log_back_urls(WC_Order $order, array $payload): void
    {
        $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
        if (!$logger) {
            return;
        }

        $ctx = ['source' => 'bwdk_back_urls'];
        $logger->info(
            sprintf(
                'Payload back URLs for order %d: return=%s cancel=%s',
                $order->get_id(),
                $payload['return_url'] ?? 'null',
                $payload['cancel_url'] ?? 'null'
            ),
            $ctx
        );
    }

    private function log_payload_snapshot(array $payload): void
    {
        $logger = function_exists('wc_get_logger') ? wc_get_logger() : null;
        if (!$logger) {
            return;
        }

        $snapshot = $payload;
        if (isset($snapshot['items']) && is_array($snapshot['items'])) {
            foreach ($snapshot['items'] as &$item) {
                if (isset($item['options']) && is_array($item['options'])) {
                    $item['options'] = array_slice($item['options'], 0, 3);
                }
                if (isset($item['image_link']) && is_string($item['image_link'])) {
                    $item['image_link'] = substr($item['image_link'], 0, 60);
                }
            }
            unset($item);
        }

        $logger->info(
            'Create-order payload snapshot: ' . wp_json_encode($snapshot, JSON_UNESCAPED_UNICODE),
            ['source' => 'bwdk_back_urls']
        );
    }
}
