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

/**
 * Stak Multivendor Core REST API (stakmv/v1)
 */
add_action('rest_api_init', 'stakmv_rest_register_core_routes');
add_filter('determine_current_user', 'stakmv_rest_authenticate_bearer_token', 20);

function stakmv_rest_is_request() {
    if (!(defined('REST_REQUEST') && REST_REQUEST)) {
        return false;
    }

    $uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field((string) wp_unslash($_SERVER['REQUEST_URI'])) : '';
    return strpos($uri, '/wp-json/stakmv/v1/') !== false || strpos($uri, 'rest_route=%2Fstakmv%2Fv1%2F') !== false || strpos($uri, 'rest_route=/stakmv/v1/') !== false;
}

function stakmv_rest_get_authorization_header() {
    if (isset($_SERVER['HTTP_AUTHORIZATION']) && is_string($_SERVER['HTTP_AUTHORIZATION'])) {
        return trim(sanitize_text_field((string) wp_unslash($_SERVER['HTTP_AUTHORIZATION'])));
    }

    if (function_exists('getallheaders')) {
        $headers = getallheaders();
        if (is_array($headers)) {
            foreach ($headers as $key => $value) {
                if (strtolower((string) $key) === 'authorization' && is_string($value)) {
                    return trim($value);
                }
            }
        }
    }

    return '';
}

function stakmv_rest_get_bearer_token() {
    $header = stakmv_rest_get_authorization_header();
    if ($header === '') {
        return '';
    }

    if (preg_match('/Bearer\s+(.+)/i', $header, $matches)) {
        return trim((string) $matches[1]);
    }

    return '';
}

function stakmv_rest_issue_bearer_token($user_id) {
    $user_id = (int) $user_id;
    if ($user_id <= 0) {
        return array('token' => '', 'expires_at' => 0, 'expires_in' => 0);
    }

    $ttl = (int) apply_filters('stakmv_api_token_ttl', 30 * DAY_IN_SECONDS, $user_id);
    if ($ttl <= 0) {
        $ttl = 30 * DAY_IN_SECONDS;
    }

    $raw = wp_generate_password(48, false, false);
    $token = $user_id . '.' . $raw;
    $hash = hash('sha256', $raw);
    $exp = time() + $ttl;

    $tokens = get_user_meta($user_id, '_stakmv_api_tokens', true);
    if (!is_array($tokens)) {
        $tokens = array();
    }

    // Prune expired tokens.
    foreach ($tokens as $k => $v) {
        if ((int) $v <= time()) {
            unset($tokens[$k]);
        }
    }

    $tokens[$hash] = $exp;
    update_user_meta($user_id, '_stakmv_api_tokens', $tokens);

    return array(
        'token'      => $token,
        'expires_at' => $exp,
        'expires_in' => max(1, $exp - time()),
    );
}

function stakmv_rest_validate_bearer_token($token) {
    $token = trim((string) $token);
    if ($token === '' || strpos($token, '.') === false) {
        return 0;
    }

    list($uid_raw, $secret) = explode('.', $token, 2);
    $user_id = absint($uid_raw);
    if ($user_id <= 0 || $secret === '') {
        return 0;
    }

    $tokens = get_user_meta($user_id, '_stakmv_api_tokens', true);
    if (!is_array($tokens) || empty($tokens)) {
        return 0;
    }

    $now = time();
    $dirty = false;
    foreach ($tokens as $k => $v) {
        if ((int) $v <= $now) {
            unset($tokens[$k]);
            $dirty = true;
        }
    }
    if ($dirty) {
        update_user_meta($user_id, '_stakmv_api_tokens', $tokens);
    }

    $hash = hash('sha256', $secret);
    if (!isset($tokens[$hash]) || (int) $tokens[$hash] <= $now) {
        return 0;
    }

    return $user_id;
}

function stakmv_rest_revoke_bearer_token($token) {
    $token = trim((string) $token);
    if ($token === '' || strpos($token, '.') === false) {
        return;
    }

    list($uid_raw, $secret) = explode('.', $token, 2);
    $user_id = absint($uid_raw);
    if ($user_id <= 0 || $secret === '') {
        return;
    }

    $tokens = get_user_meta($user_id, '_stakmv_api_tokens', true);
    if (!is_array($tokens) || empty($tokens)) {
        return;
    }

    $hash = hash('sha256', $secret);
    if (isset($tokens[$hash])) {
        unset($tokens[$hash]);
        update_user_meta($user_id, '_stakmv_api_tokens', $tokens);
    }
}

function stakmv_rest_authenticate_bearer_token($user_id) {
    if (!empty($user_id)) {
        return $user_id;
    }
    if (!stakmv_rest_is_request()) {
        return $user_id;
    }

    $token = stakmv_rest_get_bearer_token();
    if ($token === '') {
        return $user_id;
    }

    $token_user = stakmv_rest_validate_bearer_token($token);
    if ($token_user > 0) {
        return $token_user;
    }

    return $user_id;
}

function stakmv_rest_register_core_routes() {
    $ns = 'stakmv/v1';

    // Auth/session
    stakmv_rest_core_route($ns, '/auth/refresh', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_auth_refresh', 'auth');
    stakmv_rest_core_route($ns, '/auth/logout', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_auth_logout', 'auth');
    stakmv_rest_core_route($ns, '/auth/me', WP_REST_Server::READABLE, 'auth', 'stakmv_rest_auth_me', 'auth');

    // Metadata
    stakmv_rest_core_route($ns, '/meta/config', WP_REST_Server::READABLE, 'public', 'stakmv_rest_meta_config', 'meta');
    stakmv_rest_core_route($ns, '/meta/categories', WP_REST_Server::READABLE, 'public', 'stakmv_rest_meta_categories', 'meta');
    stakmv_rest_core_route($ns, '/meta/conditions', WP_REST_Server::READABLE, 'public', 'stakmv_rest_meta_conditions', 'meta');
    stakmv_rest_core_route($ns, '/meta/shipping-options', WP_REST_Server::READABLE, 'public', 'stakmv_rest_meta_shipping_options', 'meta');
    stakmv_rest_core_route($ns, '/meta/countries', WP_REST_Server::READABLE, 'public', 'stakmv_rest_meta_countries', 'meta');

    // Media
    stakmv_rest_core_route($ns, '/media/upload', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_media_upload', 'vendor');
    stakmv_rest_core_route($ns, '/media/(?P<id>\d+)', WP_REST_Server::DELETABLE, 'auth', 'stakmv_rest_media_delete', 'vendor');
    stakmv_rest_core_route($ns, '/media/reorder', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_media_reorder', 'vendor');

    // Vendor listings
    stakmv_rest_core_route($ns, '/vendor/listings', WP_REST_Server::READABLE, 'vendor', 'stakmv_rest_vendor_listings_index', 'vendor');
    stakmv_rest_core_route($ns, '/vendor/listings', WP_REST_Server::CREATABLE, 'vendor', 'stakmv_rest_vendor_listing_create', 'vendor');
    stakmv_rest_core_route($ns, '/vendor/listings/(?P<id>\d+)', WP_REST_Server::READABLE, 'vendor', 'stakmv_rest_vendor_listing_show', 'vendor');
    stakmv_rest_core_route($ns, '/vendor/listings/(?P<id>\d+)', WP_REST_Server::EDITABLE, 'vendor', 'stakmv_rest_vendor_listing_update', 'vendor');
    stakmv_rest_core_route($ns, '/vendor/listings/(?P<id>\d+)', WP_REST_Server::DELETABLE, 'vendor', 'stakmv_rest_vendor_listing_delete', 'vendor');
    stakmv_rest_core_route($ns, '/vendor/listings/(?P<id>\d+)/relist', WP_REST_Server::CREATABLE, 'vendor', 'stakmv_rest_vendor_listing_relist', 'vendor');
    stakmv_rest_core_route($ns, '/vendor/listings/(?P<id>\d+)/duplicate', WP_REST_Server::CREATABLE, 'vendor', 'stakmv_rest_vendor_listing_duplicate', 'vendor');
    stakmv_rest_core_route($ns, '/vendor/listings/(?P<id>\d+)/publish', WP_REST_Server::CREATABLE, 'vendor', 'stakmv_rest_vendor_listing_publish', 'vendor');
    stakmv_rest_core_route($ns, '/vendor/listings/(?P<id>\d+)/unpublish', WP_REST_Server::CREATABLE, 'vendor', 'stakmv_rest_vendor_listing_unpublish', 'vendor');

    // Public listings
    stakmv_rest_core_route($ns, '/listings', WP_REST_Server::READABLE, 'public', 'stakmv_rest_listings_index', 'listings');
    stakmv_rest_core_route($ns, '/listings/(?P<id>\d+)', WP_REST_Server::READABLE, 'public', 'stakmv_rest_listings_show', 'listings');
    stakmv_rest_core_route($ns, '/listings/(?P<id>\d+)/view', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_listing_mark_viewed', 'listings');
    stakmv_rest_core_route($ns, '/listings/(?P<id>\d+)/shipping-preview', WP_REST_Server::READABLE, 'public', 'stakmv_rest_shipping_preview', 'shipping');
    stakmv_rest_core_route($ns, '/listings/(?P<id>\d+)/similar', WP_REST_Server::READABLE, 'public', 'stakmv_rest_listings_similar', 'listings');

    // Auction actions
    stakmv_rest_core_route($ns, '/auctions/(?P<id>\d+)/bid', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_auction_bid', 'auctions');
    stakmv_rest_core_route($ns, '/auctions/(?P<id>\d+)/buy-now', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_auction_buy_now', 'auctions');
    stakmv_rest_core_route($ns, '/auctions/(?P<id>\d+)/history', WP_REST_Server::READABLE, 'public', 'stakmv_rest_auction_history', 'auctions');
    stakmv_rest_core_route($ns, '/cart', WP_REST_Server::READABLE, 'auth', 'stakmv_rest_cart_show', 'auctions');
    stakmv_rest_core_route($ns, '/cart/clear', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_cart_clear', 'auctions');
    stakmv_rest_core_route($ns, '/cart/items', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_cart_items_add', 'auctions');
    stakmv_rest_core_route($ns, '/cart/items/(?P<item_key>[A-Za-z0-9._:-]+)', WP_REST_Server::EDITABLE, 'auth', 'stakmv_rest_cart_items_update', 'auctions');
    stakmv_rest_core_route($ns, '/cart/items/(?P<item_key>[A-Za-z0-9._:-]+)', WP_REST_Server::DELETABLE, 'auth', 'stakmv_rest_cart_items_delete', 'auctions');
    stakmv_rest_core_route($ns, '/cart/coupons', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_cart_coupons_add', 'auctions');
    stakmv_rest_core_route($ns, '/cart/coupons/(?P<code>[A-Za-z0-9._-]+)', WP_REST_Server::DELETABLE, 'auth', 'stakmv_rest_cart_coupons_delete', 'auctions');
    stakmv_rest_core_route($ns, '/cart/customer', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_cart_customer_update', 'auctions');
    stakmv_rest_core_route($ns, '/cart/customer', WP_REST_Server::EDITABLE, 'auth', 'stakmv_rest_cart_customer_update', 'auctions');
    stakmv_rest_core_route($ns, '/checkout/payment-methods', WP_REST_Server::READABLE, 'auth', 'stakmv_rest_checkout_payment_methods', 'auctions');
    stakmv_rest_core_route($ns, '/checkout/create-order', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_checkout_create_order', 'auctions');
    stakmv_rest_core_route($ns, '/checkout/pay', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_checkout_pay', 'auctions');
    stakmv_rest_core_route($ns, '/buying/purchases', WP_REST_Server::READABLE, 'auth', 'stakmv_rest_buying_purchases_index', 'auctions');
    stakmv_rest_core_route($ns, '/buying/purchases/(?P<order_id>\d+)', WP_REST_Server::READABLE, 'auth', 'stakmv_rest_buying_purchase_show', 'auctions');
    stakmv_rest_core_route($ns, '/buying/purchases/(?P<order_id>\d+)/cancel', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_buying_purchase_cancel', 'auctions');
    stakmv_rest_core_route($ns, '/buying/purchases/(?P<order_id>\d+)/tracking', WP_REST_Server::READABLE, 'auth', 'stakmv_rest_buying_purchase_tracking', 'auctions');
    stakmv_rest_core_route($ns, '/buying/purchases/(?P<order_id>\d+)/buy-again', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_buying_purchase_buy_again', 'auctions');
    stakmv_rest_core_route($ns, '/buying/bids', WP_REST_Server::READABLE, 'auth', 'stakmv_rest_buying_bids_index', 'auctions');
    stakmv_rest_core_route($ns, '/buying/recently-viewed', WP_REST_Server::READABLE, 'auth', 'stakmv_rest_buying_recently_viewed_index', 'auctions');

    // Public vendor profile
    stakmv_rest_core_route($ns, '/vendors/(?P<id>\d+)/profile', WP_REST_Server::READABLE, 'public', 'stakmv_rest_vendor_profile_public', 'vendor');
    stakmv_rest_core_route($ns, '/vendors/(?P<id>\d+)/shop', WP_REST_Server::READABLE, 'public', 'stakmv_rest_vendor_shop_public', 'vendor');
    stakmv_rest_core_route($ns, '/vendors/(?P<id>\d+)/sold', WP_REST_Server::READABLE, 'public', 'stakmv_rest_vendor_sold_public', 'vendor');

    // Vendor profile owner edit
    stakmv_rest_core_route($ns, '/vendor/profile', WP_REST_Server::EDITABLE, 'vendor', 'stakmv_rest_vendor_profile_update', 'vendor');
    stakmv_rest_core_route($ns, '/vendor/profile/about', WP_REST_Server::EDITABLE, 'vendor', 'stakmv_rest_vendor_profile_about_update', 'vendor');
    stakmv_rest_core_route($ns, '/vendor/profile/banner', WP_REST_Server::CREATABLE, 'vendor', 'stakmv_rest_vendor_profile_banner_update', 'vendor');

    // Notifications
    stakmv_rest_core_route($ns, '/notifications', WP_REST_Server::READABLE, 'auth', 'stakmv_rest_notifications_index', 'notifications');
    stakmv_rest_core_route($ns, '/notifications/(?P<id>[A-Za-z0-9_-]+)/read', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_notifications_read', 'notifications');
    stakmv_rest_core_route($ns, '/notifications/read-all', WP_REST_Server::CREATABLE, 'auth', 'stakmv_rest_notifications_read_all', 'notifications');

    // Shipping integration helpers
    stakmv_rest_core_route($ns, '/shipping/providers', WP_REST_Server::READABLE, 'public', 'stakmv_rest_shipping_providers', 'shipping');
    stakmv_rest_core_route($ns, '/shipping/rates/quote', WP_REST_Server::CREATABLE, 'public', 'stakmv_rest_shipping_rates_quote', 'shipping');
    stakmv_rest_core_route($ns, '/shipping/listing/(?P<id>\d+)/details', WP_REST_Server::READABLE, 'public', 'stakmv_rest_shipping_listing_details', 'shipping');

    do_action('stakmv_rest_register_routes', $ns);
}

function stakmv_rest_core_route($ns, $route, $methods, $permission_level, $handler, $group = 'general') {
    register_rest_route($ns, $route, array(
        'methods'             => $methods,
        'callback'            => $handler,
        'permission_callback' => function ($request) use ($permission_level, $group) {
            if (!stakmv_rest_api_enabled_for_group($group)) {
                return new WP_Error('rest_forbidden', __('Stak API endpoint group is disabled by admin.', 'stak-multivendor'), array('status' => rest_authorization_required_code()));
            }
            return stakmv_rest_permission($permission_level, $request);
        },
    ));
}

function stakmv_rest_api_enabled_for_group($group) {
    $master = stakmv_get_option('api_enabled', 'yes');
    if ($master !== 'yes') {
        return false;
    }

    $group = sanitize_key((string) $group);
    if ($group === '') {
        return true;
    }

    $option_map = array(
        'auth' => 'api_group_auth',
        'meta' => 'api_group_meta',
        'vendor' => 'api_group_vendor',
        'listings' => 'api_group_listings',
        'auctions' => 'api_group_auctions',
        'shipping' => 'api_group_shipping',
        'notifications' => 'api_group_notifications',
        'pro' => 'api_group_pro',
    );

    if (!isset($option_map[$group])) {
        return true;
    }

    return stakmv_get_option($option_map[$group], 'yes') === 'yes';
}

function stakmv_rest_permission($level, $request = null) {
    switch ($level) {
        case 'public':
            return true;
        case 'auth':
            return is_user_logged_in();
        case 'vendor':
            if (!is_user_logged_in()) return false;
            if (stakmv_rest_is_admin_user() || current_user_can('edit_products')) return true;
            return function_exists('stakmv_is_vendor') ? (bool) stakmv_is_vendor(get_current_user_id()) : false;
        case 'admin':
            return stakmv_rest_is_admin_user();
        default:
            return false;
    }
}

function stakmv_rest_is_admin_user() {
    return current_user_can('manage_woocommerce') || current_user_can('manage_options');
}

function stakmv_rest_can_manage_listings($user_id = 0) {
    $user_id = (int) $user_id;
    if ($user_id <= 0) {
        $user_id = get_current_user_id();
    }
    if ($user_id <= 0) {
        return false;
    }

    if (stakmv_rest_is_admin_user() || current_user_can('edit_products')) {
        return true;
    }

    if (function_exists('stakmv_is_vendor')) {
        return (bool) stakmv_is_vendor($user_id);
    }

    return false;
}

function stakmv_rest_not_implemented($request) {
    return new WP_REST_Response(array(
        'ok' => false,
        'code' => 'not_implemented_yet',
        'message' => 'Endpoint scaffolded and reserved for app integration. Implementation pending.',
    ), 501);
}

function stakmv_rest_auth_refresh($request) {
    $user = wp_get_current_user();
    if (!$user || !$user->ID) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }

    $token_data = stakmv_rest_issue_bearer_token($user->ID);
    $old_token = stakmv_rest_get_bearer_token();
    if ($old_token !== '') {
        stakmv_rest_revoke_bearer_token($old_token);
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'message' => 'Session refreshed.',
        'auth' => array(
            'type' => 'bearer',
            'token' => (string) ($token_data['token'] ?? ''),
            'expires_at' => (int) ($token_data['expires_at'] ?? 0),
            'expires_in' => (int) ($token_data['expires_in'] ?? 0),
        ),
        'user' => array(
            'id' => (int) $user->ID,
            'name' => function_exists('stakmv_get_vendor_display_name') ? stakmv_get_vendor_display_name((int) $user->ID) : (string) $user->display_name,
            'email' => (string) $user->user_email,
            'roles' => array_values(array_map('strval', (array) $user->roles)),
        ),
    ));
}

function stakmv_rest_auth_logout($request) {
    $bearer = stakmv_rest_get_bearer_token();
    if ($bearer !== '') {
        stakmv_rest_revoke_bearer_token($bearer);
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'message' => 'Token revoked.',
    ));
}

function stakmv_rest_auth_me($request) {
    $user = wp_get_current_user();
    if (!$user || !$user->ID) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'user' => array(
            'id' => (int) $user->ID,
            'name' => function_exists('stakmv_get_vendor_display_name') ? stakmv_get_vendor_display_name((int) $user->ID) : (string) $user->display_name,
            'email' => (string) $user->user_email,
            'roles' => array_values(array_map('strval', (array) $user->roles)),
            'is_vendor' => function_exists('stakmv_is_vendor') ? (bool) stakmv_is_vendor($user->ID) : false,
            'is_admin_like' => stakmv_rest_is_admin_user(),
            'can_manage_listings' => stakmv_rest_can_manage_listings($user->ID),
        ),
    ));
}

function stakmv_rest_meta_config($request) {
    return new WP_REST_Response(array(
        'ok' => true,
        'config' => array(
            'site_name' => get_bloginfo('name'),
            'currency' => function_exists('get_woocommerce_currency') ? get_woocommerce_currency() : 'USD',
            'auctions_enabled' => function_exists('stakmv_auctions_enabled') ? (bool) stakmv_auctions_enabled() : (stakmv_get_option('enable_auctions', 'yes') === 'yes'),
            'product_template' => function_exists('stakmv_get_auction_product_template_mode') ? stakmv_get_auction_product_template_mode() : 'template_1',
        ),
    ));
}

function stakmv_rest_meta_categories($request) {
    $terms = get_terms(array(
        'taxonomy' => 'product_cat',
        'hide_empty' => false,
    ));

    $items = array();
    if (!is_wp_error($terms)) {
        foreach ($terms as $term) {
            $items[] = array(
                'id' => (int) $term->term_id,
                'name' => (string) $term->name,
                'slug' => (string) $term->slug,
            );
        }
    }

    return new WP_REST_Response(array('ok' => true, 'items' => $items));
}

function stakmv_rest_meta_conditions($request) {
    $options = function_exists('stakmv_get_condition_options') ? stakmv_get_condition_options() : array();
    $items = array();
    if (is_array($options)) {
        foreach ($options as $key => $row) {
            $items[] = array(
                'key' => sanitize_key((string) $key),
                'label' => sanitize_text_field((string) ($row['label'] ?? '')),
                'detail' => sanitize_text_field((string) ($row['detail'] ?? '')),
            );
        }
    }
    return new WP_REST_Response(array('ok' => true, 'items' => $items));
}

function stakmv_rest_meta_shipping_options($request) {
    return new WP_REST_Response(array(
        'ok' => true,
        'shipping_modes' => array('calculated', 'flat', 'free', 'pickup_only'),
        'shipping_scopes' => array('domestic', 'international', 'selected'),
        'carriers' => array('USPS', 'UPS', 'FedEx', 'DHL'),
        'returns' => array('returns_accepted', 'no_returns'),
    ));
}

function stakmv_rest_meta_countries($request) {
    $countries = array();
    if (function_exists('WC') && WC()->countries) {
        foreach ((array) WC()->countries->get_countries() as $code => $name) {
            $countries[] = array('code' => (string) $code, 'name' => (string) $name);
        }
    }
    return new WP_REST_Response(array('ok' => true, 'items' => $countries));
}

function stakmv_rest_media_upload($request) {
    if (!current_user_can('upload_files')) {
        return new WP_Error('forbidden', 'You cannot upload files.', array('status' => 403));
    }

    $file_params = (array) $request->get_file_params();
    if (empty($file_params['file']) || !is_array($file_params['file'])) {
        return new WP_Error('missing_file', 'No file uploaded. Use field name "file".', array('status' => 400));
    }

    // media_handle_upload() expects the upload in $_FILES.
    $_FILES['file'] = $file_params['file'];

    require_once ABSPATH . 'wp-admin/includes/file.php';
    require_once ABSPATH . 'wp-admin/includes/image.php';
    require_once ABSPATH . 'wp-admin/includes/media.php';

    $attachment_id = media_handle_upload('file', 0);
    if (is_wp_error($attachment_id)) {
        return $attachment_id;
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'id' => (int) $attachment_id,
        'url' => wp_get_attachment_url($attachment_id),
    ));
}

function stakmv_rest_media_delete($request) {
    $id = (int) $request['id'];
    if ($id <= 0) {
        return new WP_Error('invalid_id', 'Invalid media id.', array('status' => 400));
    }

    if (!current_user_can('delete_post', $id)) {
        return new WP_Error('forbidden', 'You cannot delete this media.', array('status' => 403));
    }

    $deleted = wp_delete_attachment($id, true);
    return new WP_REST_Response(array('ok' => (bool) $deleted));
}

/**
 * Keep listing slug aligned to title for REST-created listings.
 * - Force mode is used on create/duplicate to avoid generic "product" slugs.
 * - Non-force mode only repairs empty/default slugs for backward compatibility.
 */
function stakmv_rest_maybe_sync_listing_slug($product_id, $raw_title = '', $force = false) {
    $product_id = (int) $product_id;
    if ($product_id <= 0) {
        return;
    }

    $post = get_post($product_id);
    if (!($post instanceof WP_Post) || $post->post_type !== 'product') {
        return;
    }

    $current_slug = (string) $post->post_name;
    $needs_repair = ($current_slug === '' || preg_match('/^product(?:-\d+)?$/', $current_slug));
    if (!$force && !$needs_repair) {
        return;
    }

    $title_source = trim((string) $raw_title);
    if ($title_source === '') {
        $title_source = (string) $post->post_title;
    }

    $base_slug = sanitize_title($title_source);
    if ($base_slug === '') {
        return;
    }

    $unique_slug = wp_unique_post_slug(
        $base_slug,
        $product_id,
        $post->post_status,
        $post->post_type,
        (int) $post->post_parent
    );

    if ($unique_slug === '' || $unique_slug === $current_slug) {
        return;
    }

    wp_update_post(array(
        'ID'        => $product_id,
        'post_name' => $unique_slug,
    ));
}

function stakmv_rest_vendor_listings_index($request) {
    $vendor_id = get_current_user_id();
    $page = max(1, (int) $request->get_param('page'));
    $per_page = max(1, min(50, (int) $request->get_param('per_page')));

    $query = new WP_Query(array(
        'post_type' => 'product',
        'post_status' => array('publish', 'draft', 'pending', 'private'),
        'author' => $vendor_id,
        'paged' => $page,
        'posts_per_page' => $per_page,
        'orderby' => 'date',
        'order' => 'DESC',
    ));

    $items = array();
    foreach ((array) $query->posts as $post) {
        $product = wc_get_product((int) $post->ID);
        if (!($product instanceof WC_Product)) continue;
        $items[] = stakmv_rest_listing_payload($product);
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'items' => $items,
        'pagination' => array(
            'page' => $page,
            'per_page' => $per_page,
            'total' => (int) $query->found_posts,
            'total_pages' => (int) $query->max_num_pages,
        ),
    ));
}

function stakmv_rest_vendor_listing_create($request) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }
    if (!stakmv_rest_can_manage_listings($user_id)) {
        return new WP_Error('forbidden', 'You are not allowed to create listings.', array('status' => 403));
    }

    $payload = stakmv_rest_get_payload($request);
    if (class_exists('STAKMV_Product_Auction')) {
        $product = new STAKMV_Product_Auction();
    } else {
        $product = new WC_Product_Simple();
    }
    if (array_key_exists('title', $payload)) {
        $product->set_name(sanitize_text_field((string) $payload['title']));
    }
    $product->set_status(stakmv_rest_sanitize_status($payload['status'] ?? 'publish'));
    $product->set_catalog_visibility('visible');
    $product->set_manage_stock(false);
    $product->set_props(array('author' => $user_id));

    $product_id = $product->save();
    if ($product_id <= 0) {
        return new WP_Error('create_failed', 'Could not create listing.', array('status' => 500));
    }

    // Enforce auction product type even if Woo resolved a simple product class.
    wp_set_object_terms($product_id, 'auction', 'product_type');

    $result = stakmv_rest_apply_listing_payload($product_id, $payload, $user_id, false);
    if (is_wp_error($result)) {
        return $result;
    }

    stakmv_rest_maybe_sync_listing_slug($product_id, (string) ($payload['title'] ?? ''), true);

    $final_product = wc_get_product($product_id);
    return new WP_REST_Response(array(
        'ok' => true,
        'item' => stakmv_rest_listing_payload($final_product, true),
        'notice' => $result['notice'] ?? '',
    ));
}

function stakmv_rest_vendor_listing_update($request) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }
    if (!stakmv_rest_can_manage_listings($user_id)) {
        return new WP_Error('forbidden', 'You are not allowed to update listings.', array('status' => 403));
    }

    $id = (int) $request['id'];
    $post = get_post($id);
    if (!$post || $post->post_type !== 'product') {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }
    if ((int) $post->post_author !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot edit this listing.', array('status' => 403));
    }

    $payload = stakmv_rest_get_payload($request);
    $result = stakmv_rest_apply_listing_payload($id, $payload, $user_id, true);
    if (is_wp_error($result)) {
        return $result;
    }

    $product = wc_get_product($id);
    return new WP_REST_Response(array(
        'ok' => true,
        'item' => stakmv_rest_listing_payload($product, true),
        'notice' => $result['notice'] ?? '',
    ));
}

function stakmv_rest_vendor_listing_delete($request) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }
    if (!stakmv_rest_can_manage_listings($user_id)) {
        return new WP_Error('forbidden', 'You are not allowed to delete listings.', array('status' => 403));
    }

    $id = (int) $request['id'];
    $post = get_post($id);
    if (!$post || $post->post_type !== 'product') {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }
    if ((int) $post->post_author !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot delete this listing.', array('status' => 403));
    }

    $bid_count = function_exists('stakmv_get_bid_count') ? (int) stakmv_get_bid_count($id) : (int) get_post_meta($id, '_stakmv_auction_bid_count', true);
    if ($bid_count > 0) {
        return new WP_Error('has_bids', 'This listing has bids and cannot be deleted.', array('status' => 409));
    }

    wp_trash_post($id);
    do_action('stakmv_listing_deleted', $id, $user_id);

    return new WP_REST_Response(array('ok' => true));
}

function stakmv_rest_vendor_listing_relist($request) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }
    if (!stakmv_rest_can_manage_listings($user_id)) {
        return new WP_Error('forbidden', 'You are not allowed to relist listings.', array('status' => 403));
    }

    $id = (int) $request['id'];
    $post = get_post($id);
    if (!$post || $post->post_type !== 'product') {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }
    if ((int) $post->post_author !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot relist this listing.', array('status' => 403));
    }

    $status_raw = (string) get_post_meta($id, '_stakmv_auction_status', true);
    $status = sanitize_key($status_raw);
    if (in_array($status, array('sold', 'pending_payment'), true)) {
        return new WP_Error('relist_denied', 'This auction cannot be relisted right now.', array('status' => 409));
    }

    $end_raw = get_post_meta($id, '_stakmv_auction_end_time', true);
    $end_ts = is_numeric($end_raw) ? (int) $end_raw : ($end_raw ? strtotime((string) $end_raw) : 0);
    $is_expired = ($end_ts > 0 && current_time('timestamp') >= $end_ts);
    $is_marked_ended = in_array($status, array('ended', 'expired'), true);
    if (!$is_expired && !$is_marked_ended) {
        return new WP_Error('relist_denied', 'This auction cannot be relisted right now.', array('status' => 409));
    }

    $days = function_exists('stakmv_get_relist_duration_days') ? stakmv_get_relist_duration_days() : 7;
    $new_end_ts = current_time('timestamp') + ((int) $days * DAY_IN_SECONDS);

    update_post_meta($id, '_stakmv_auction_end_time', $new_end_ts);
    update_post_meta($id, '_stakmv_auction_status', 'active');

    delete_post_meta($id, '_stakmv_auction_current_bid');
    delete_post_meta($id, '_stakmv_auction_bid_count');
    delete_post_meta($id, '_stakmv_auction_has_bids');
    delete_post_meta($id, '_stakmv_auction_highest_bidder');
    delete_post_meta($id, '_stakmv_auction_winner');
    delete_post_meta($id, '_stakmv_auction_failed_bidder');
    delete_post_meta($id, '_stakmv_auction_payment_deadline');
    delete_post_meta($id, '_stakmv_auction_extensions_used');

    $listing_type = sanitize_key((string) get_post_meta($id, '_stakmv_auction_listing_type', true));
    if ($listing_type === 'auction_only' || $listing_type === '') {
        $start_price = wc_format_decimal(get_post_meta($id, '_stakmv_auction_start_price', true));
        if ($start_price !== '') {
            update_post_meta($id, '_regular_price', $start_price);
            update_post_meta($id, '_price', $start_price);
        }
    }

    global $wpdb;
    $bids_table = defined('STAKMV_AUCTION_BIDS_TABLE') ? STAKMV_AUCTION_BIDS_TABLE : ($wpdb->prefix . 'stakmv_auction_bids');
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Cleanup write for relist.
    $wpdb->delete($bids_table, array('auction_id' => $id), array('%d'));

    return new WP_REST_Response(array('ok' => true, 'listing_id' => $id, 'new_end_time' => $new_end_ts));
}

function stakmv_rest_vendor_listing_duplicate($request) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }
    if (!stakmv_rest_can_manage_listings($user_id)) {
        return new WP_Error('forbidden', 'You are not allowed to duplicate listings.', array('status' => 403));
    }

    $id = (int) $request['id'];
    $post = get_post($id);
    if (!$post || $post->post_type !== 'product') {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }
    if ((int) $post->post_author !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot duplicate this listing.', array('status' => 403));
    }

    $payload = stakmv_rest_build_payload_from_listing($id);
    $payload['status'] = sanitize_key((string) $request->get_param('status'));
    if ($payload['status'] === '') {
        $payload['status'] = 'draft';
    }

    if (class_exists('STAKMV_Product_Auction')) {
        $product = new STAKMV_Product_Auction();
    } else {
        $product = new WC_Product_Simple();
    }
    if (array_key_exists('title', $payload)) {
        $product->set_name(sanitize_text_field((string) $payload['title']));
    }
    $product->set_status(stakmv_rest_sanitize_status($payload['status']));
    $product->set_catalog_visibility('visible');
    $product->set_manage_stock(false);
    $product->set_props(array('author' => $user_id));

    $product_id = $product->save();
    if ($product_id <= 0) {
        return new WP_Error('create_failed', 'Could not duplicate listing.', array('status' => 500));
    }

    // Enforce auction product type even if Woo resolved a simple product class.
    wp_set_object_terms($product_id, 'auction', 'product_type');

    $result = stakmv_rest_apply_listing_payload($product_id, $payload, $user_id, false);
    if (is_wp_error($result)) {
        return $result;
    }

    stakmv_rest_maybe_sync_listing_slug($product_id, (string) ($payload['title'] ?? ''), true);

    $final_product = wc_get_product($product_id);
    return new WP_REST_Response(array('ok' => true, 'item' => stakmv_rest_listing_payload($final_product, true)));
}

function stakmv_rest_vendor_listing_publish($request) {
    return stakmv_rest_set_listing_status($request, 'publish');
}

function stakmv_rest_vendor_listing_unpublish($request) {
    return stakmv_rest_set_listing_status($request, 'draft');
}

function stakmv_rest_set_listing_status($request, $status) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }
    if (!stakmv_rest_can_manage_listings($user_id)) {
        return new WP_Error('forbidden', 'You are not allowed to change listing status.', array('status' => 403));
    }

    $id = (int) $request['id'];
    $post = get_post($id);
    if (!$post || $post->post_type !== 'product') {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }
    if ((int) $post->post_author !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot change this listing.', array('status' => 403));
    }

    wp_update_post(array(
        'ID' => $id,
        'post_status' => stakmv_rest_sanitize_status($status),
    ));

    return new WP_REST_Response(array('ok' => true, 'listing_id' => $id, 'status' => stakmv_rest_sanitize_status($status)));
}

function stakmv_rest_build_payload_from_listing($product_id) {
    $product_id = (int) $product_id;
    $product = wc_get_product($product_id);
    if (!($product instanceof WC_Product)) {
        return array();
    }

    $payload = array(
        'title' => (string) $product->get_name(),
        'description' => (string) $product->get_description(),
        'status' => (string) get_post_status($product_id),
        'listing_type' => (string) get_post_meta($product_id, '_stakmv_auction_listing_type', true),
        'start_price' => (string) get_post_meta($product_id, '_stakmv_auction_start_price', true),
        'buy_now_price' => (string) get_post_meta($product_id, '_stakmv_auction_buy_now_price', true),
        'end_date' => (string) get_post_meta($product_id, '_stakmv_auction_end_time', true),
        'shipping_mode' => (string) get_post_meta($product_id, '_stakmv_auction_shipping_mode', true),
        'shipping_scope' => (string) get_post_meta($product_id, '_stakmv_auction_shipping_scope', true),
        'shipping_countries' => (array) get_post_meta($product_id, '_stakmv_auction_shipping_countries', true),
        'shipping_excluded_countries' => (array) get_post_meta($product_id, '_stakmv_auction_shipping_excluded_countries', true),
        'weight' => (string) $product->get_weight(),
        'length' => (string) $product->get_length(),
        'width' => (string) $product->get_width(),
        'height' => (string) $product->get_height(),
        'shipping_carriers' => (array) get_post_meta($product_id, '_stakmv_auction_shipping_carriers', true),
        'quantity' => (int) max(1, (int) ($product->get_stock_quantity() ?? 1)),
        'product_cat' => (int) (wp_get_post_terms($product_id, 'product_cat', array('fields' => 'ids'))[0] ?? 0),
        'condition' => (string) get_post_meta($product_id, '_stakmv_auction_condition', true),
        'location' => (string) get_post_meta($product_id, '_stakmv_auction_location', true),
        'handling_time_days' => (int) get_post_meta($product_id, '_stakmv_auction_handling_time_days', true),
        'returns_policy' => (string) get_post_meta($product_id, '_stakmv_auction_returns_policy', true),
        'returns_days' => (int) get_post_meta($product_id, '_stakmv_auction_returns_days', true),
        'return_shipping_paid_by' => (string) get_post_meta($product_id, '_stakmv_auction_return_shipping_paid_by', true),
        'shipping_cost' => (string) get_post_meta($product_id, '_stakmv_auction_shipping_cost', true),
        'faq' => (array) get_post_meta($product_id, '_stakmv_listing_faqs', true),
        'featured_image_id' => (int) $product->get_image_id(),
        'gallery_image_ids' => array_values(array_map('intval', (array) $product->get_gallery_image_ids())),
    );

    return apply_filters('stakmv_rest_build_listing_payload', $payload, $product_id, $product);
}

function stakmv_rest_get_payload($request) {
    $payload = $request->get_json_params();
    if (!is_array($payload)) {
        $payload = $request->get_body_params();
    }
    return is_array($payload) ? $payload : array();
}

function stakmv_rest_sanitize_status($status) {
    $status = sanitize_key((string) $status);
    if (!in_array($status, array('publish', 'draft', 'pending'), true)) {
        $status = 'publish';
    }
    return $status;
}

function stakmv_rest_get_pagination_params($request, $max_per_page = 50) {
    $max_per_page = max(1, (int) $max_per_page);
    $page = max(1, (int) $request->get_param('page'));
    $raw_per_page = (int) $request->get_param('per_page');
    if ($raw_per_page <= 0) {
        $raw_per_page = 20;
    }
    $per_page = max(1, min($max_per_page, $raw_per_page));

    return array($page, $per_page);
}

function stakmv_rest_normalize_int_list($items) {
    $items = is_array($items) ? $items : array();
    $normalized = array();

    foreach ($items as $item) {
        $id = (int) $item;
        if ($id > 0 && !in_array($id, $normalized, true)) {
            $normalized[] = $id;
        }
    }

    return $normalized;
}

function stakmv_rest_get_order_auction_id($order) {
    if (!($order instanceof WC_Order)) {
        return 0;
    }

    $auction_id = (int) $order->get_meta('_stakmv_auction_id');
    if ($auction_id > 0) {
        return $auction_id;
    }

    foreach ((array) $order->get_items() as $item) {
        if (!($item instanceof WC_Order_Item_Product)) {
            continue;
        }
        $product_id = (int) $item->get_product_id();
        if ($product_id > 0) {
            return $product_id;
        }
    }

    return 0;
}

function stakmv_rest_listing_is_buy_now_eligible($listing_id) {
    $listing_id = (int) $listing_id;
    if ($listing_id <= 0) {
        return false;
    }

    $buy_now_price = (float) get_post_meta($listing_id, '_stakmv_auction_buy_now_price', true);
    if ($buy_now_price <= 0) {
        return false;
    }

    return ((string) get_post_meta($listing_id, '_stakmv_auction_status', true) !== 'sold');
}

function stakmv_rest_get_listing_available_quantity($listing_id) {
    $listing_id = (int) $listing_id;
    if ($listing_id <= 0) {
        return 0;
    }

    $product = wc_get_product($listing_id);
    if (!($product instanceof WC_Product)) {
        return 0;
    }

    if (!$product->managing_stock()) {
        return PHP_INT_MAX;
    }

    $stock_quantity = $product->get_stock_quantity();
    if ($stock_quantity === null) {
        return PHP_INT_MAX;
    }

    return max(0, (int) $stock_quantity);
}

function stakmv_rest_get_cart_meta_key() {
    return 'stakmv_app_cart';
}

function stakmv_rest_default_cart_state() {
    return array(
        'items' => array(),
        'coupons' => array(),
        'customer' => array(),
    );
}

function stakmv_rest_normalize_cart_state($state) {
    $state = is_array($state) ? $state : array();
    $defaults = stakmv_rest_default_cart_state();

    $items = is_array($state['items'] ?? null) ? $state['items'] : array();
    $normalized_items = array();
    foreach ($items as $item_key => $item) {
        $item = is_array($item) ? $item : array();
        $listing_id = (int) ($item['listing_id'] ?? $item['product_id'] ?? 0);
        $quantity = max(1, (int) ($item['quantity'] ?? 1));
        if ($listing_id <= 0) {
            continue;
        }
        $safe_key = sanitize_key((string) $item_key);
        if ($safe_key === '') {
            $safe_key = 'listing_' . $listing_id;
        }
        $normalized_items[$safe_key] = array(
            'listing_id' => $listing_id,
            'quantity' => $quantity,
        );
    }

    $coupons = is_array($state['coupons'] ?? null) ? $state['coupons'] : array();
    $normalized_coupons = array();
    foreach ($coupons as $coupon_code) {
        $code = stakmv_rest_sanitize_coupon_code($coupon_code);
        if ($code !== '' && !in_array($code, $normalized_coupons, true)) {
            $normalized_coupons[] = $code;
        }
    }

    $customer = is_array($state['customer'] ?? null) ? $state['customer'] : array();
    $normalized_customer = stakmv_rest_sanitize_cart_customer($customer);

    return array(
        'items' => $normalized_items,
        'coupons' => $normalized_coupons,
        'customer' => $normalized_customer,
        'updated_at' => (int) ($state['updated_at'] ?? 0),
    ) + $defaults;
}

function stakmv_rest_get_user_cart_state($user_id) {
    $state = get_user_meta((int) $user_id, stakmv_rest_get_cart_meta_key(), true);
    return stakmv_rest_normalize_cart_state($state);
}

function stakmv_rest_save_user_cart_state($user_id, $state) {
    $state = stakmv_rest_normalize_cart_state($state);
    $state['updated_at'] = time();
    update_user_meta((int) $user_id, stakmv_rest_get_cart_meta_key(), $state);
    return $state;
}

function stakmv_rest_sanitize_coupon_code($code) {
    $code = sanitize_text_field((string) $code);
    $code = function_exists('wc_format_coupon_code') ? wc_format_coupon_code($code) : strtolower($code);
    return sanitize_text_field((string) $code);
}

function stakmv_rest_sanitize_cart_address($address) {
    $address = is_array($address) ? $address : array();
    return array(
        'first_name' => sanitize_text_field((string) ($address['first_name'] ?? '')),
        'last_name' => sanitize_text_field((string) ($address['last_name'] ?? '')),
        'company' => sanitize_text_field((string) ($address['company'] ?? '')),
        'address_1' => sanitize_text_field((string) ($address['address_1'] ?? '')),
        'address_2' => sanitize_text_field((string) ($address['address_2'] ?? '')),
        'city' => sanitize_text_field((string) ($address['city'] ?? '')),
        'state' => sanitize_text_field((string) ($address['state'] ?? '')),
        'postcode' => sanitize_text_field((string) ($address['postcode'] ?? '')),
        'country' => strtoupper(sanitize_text_field((string) ($address['country'] ?? ''))),
        'email' => sanitize_email((string) ($address['email'] ?? '')),
        'phone' => sanitize_text_field((string) ($address['phone'] ?? '')),
    );
}

function stakmv_rest_sanitize_cart_customer($customer) {
    $customer = is_array($customer) ? $customer : array();

    $billing = stakmv_rest_sanitize_cart_address((array) ($customer['billing'] ?? array()));
    $shipping = stakmv_rest_sanitize_cart_address((array) ($customer['shipping'] ?? array()));

    if ($shipping['first_name'] === '' && $billing['first_name'] !== '') {
        $shipping['first_name'] = $billing['first_name'];
    }
    if ($shipping['last_name'] === '' && $billing['last_name'] !== '') {
        $shipping['last_name'] = $billing['last_name'];
    }
    if ($shipping['country'] === '' && $billing['country'] !== '') {
        $shipping['country'] = $billing['country'];
    }

    return array(
        'billing' => $billing,
        'shipping' => $shipping,
        'note' => sanitize_textarea_field((string) ($customer['note'] ?? '')),
        'payment_method' => sanitize_key((string) ($customer['payment_method'] ?? '')),
        'payment_method_title' => sanitize_text_field((string) ($customer['payment_method_title'] ?? '')),
    );
}

function stakmv_rest_get_listing_unit_price($product, $listing_id) {
    if (!($product instanceof WC_Product)) {
        return 0.0;
    }

    $listing_id = (int) $listing_id;
    $buy_now_price = (float) get_post_meta($listing_id, '_stakmv_auction_buy_now_price', true);
    if ($buy_now_price > 0) {
        return $buy_now_price;
    }

    return (float) $product->get_price();
}

function stakmv_rest_get_cart_item_shipping_amount($listing_id, $quantity) {
    $listing_id = (int) $listing_id;
    $quantity = max(1, (int) $quantity);
    $shipping_mode = function_exists('stakmv_get_listing_shipping_mode') ? stakmv_get_listing_shipping_mode($listing_id) : 'calculated';

    if ($shipping_mode === 'flat') {
        $flat_cost = (float) get_post_meta($listing_id, '_stakmv_auction_shipping_cost', true);
        return max(0.0, $flat_cost) * $quantity;
    }

    return 0.0;
}

function stakmv_rest_cart_build_snapshot($state) {
    $state = stakmv_rest_normalize_cart_state($state);
    $currency = function_exists('get_woocommerce_currency') ? get_woocommerce_currency() : 'USD';
    $destination_country = strtoupper((string) ($state['customer']['shipping']['country'] ?? $state['customer']['billing']['country'] ?? ''));

    $items_payload = array();
    $normalized_items = array();
    $subtotal = 0.0;
    $shipping_total = 0.0;
    $has_calculated_shipping = false;

    foreach ((array) $state['items'] as $item_key => $item) {
        $listing_id = (int) ($item['listing_id'] ?? 0);
        $quantity = max(1, (int) ($item['quantity'] ?? 1));
        if ($listing_id <= 0) {
            continue;
        }

        $product = wc_get_product($listing_id);
        if (!($product instanceof WC_Product) || get_post_status($listing_id) !== 'publish') {
            continue;
        }

        $unit_price = stakmv_rest_get_listing_unit_price($product, $listing_id);
        if ($unit_price <= 0) {
            continue;
        }

        $shipping_mode = function_exists('stakmv_get_listing_shipping_mode') ? stakmv_get_listing_shipping_mode($listing_id) : 'calculated';
        $line_shipping = stakmv_rest_get_cart_item_shipping_amount($listing_id, $quantity);
        $line_subtotal = $unit_price * $quantity;
        $is_shippable = true;
        $shipping_message = '';

        if ($shipping_mode === 'calculated') {
            $has_calculated_shipping = true;
            $shipping_message = 'Shipping rate is calculated at checkout.';
        }

        if ($destination_country !== '' && function_exists('stakmv_listing_ships_to_country') && !stakmv_listing_ships_to_country($listing_id, $destination_country)) {
            $is_shippable = false;
            $shipping_message = 'This listing does not ship to the selected country.';
        }

        $line_total = $line_subtotal + $line_shipping;
        $subtotal += $line_subtotal;
        $shipping_total += $line_shipping;

        $normalized_items[$item_key] = array(
            'listing_id' => $listing_id,
            'quantity' => $quantity,
        );

        $items_payload[] = array(
            'item_key' => (string) $item_key,
            'listing_id' => $listing_id,
            'quantity' => $quantity,
            'unit_price' => (float) $unit_price,
            'line_subtotal' => (float) $line_subtotal,
            'shipping_mode' => $shipping_mode,
            'shipping_total' => (float) $line_shipping,
            'line_total' => (float) $line_total,
            'is_shippable' => $is_shippable,
            'shipping_message' => $shipping_message,
            'listing' => stakmv_rest_listing_payload($product),
        );
    }

    $state['items'] = $normalized_items;

    $discount_total = 0.0;
    $coupon_count = count((array) $state['coupons']);
    $total = max(0.0, $subtotal + $shipping_total - $discount_total);

    return array(
        'state' => $state,
        'snapshot' => array(
            'ok' => true,
            'currency' => $currency,
            'items' => array_values($items_payload),
            'coupons' => array_values((array) $state['coupons']),
            'customer' => (array) $state['customer'],
            'totals' => array(
                'item_count' => array_sum(array_map(function ($item) {
                    return (int) ($item['quantity'] ?? 0);
                }, $normalized_items)),
                'subtotal' => (float) $subtotal,
                'shipping' => (float) $shipping_total,
                'discount' => (float) $discount_total,
                'tax' => 0.0,
                'total' => (float) $total,
            ),
            'needs_shipping_quote' => $has_calculated_shipping,
        ),
    );
}

function stakmv_rest_cart_show($request) {
    $user_id = get_current_user_id();
    $state = stakmv_rest_get_user_cart_state($user_id);
    $built = stakmv_rest_cart_build_snapshot($state);
    stakmv_rest_save_user_cart_state($user_id, $built['state']);

    return new WP_REST_Response($built['snapshot']);
}

function stakmv_rest_cart_clear($request) {
    $user_id = get_current_user_id();
    $state = stakmv_rest_get_user_cart_state($user_id);
    $state['items'] = array();
    $state['coupons'] = array();
    $state = stakmv_rest_save_user_cart_state($user_id, $state);
    $built = stakmv_rest_cart_build_snapshot($state);

    return new WP_REST_Response($built['snapshot']);
}

function stakmv_rest_cart_items_add($request) {
    $payload = stakmv_rest_get_payload($request);
    $listing_id = (int) ($payload['listing_id'] ?? $payload['product_id'] ?? $payload['id'] ?? 0);
    $quantity = max(1, (int) ($payload['quantity'] ?? 1));

    if ($listing_id <= 0) {
        return new WP_Error('invalid_listing', 'listing_id is required.', array('status' => 400));
    }

    $product = wc_get_product($listing_id);
    if (!($product instanceof WC_Product) || get_post_status($listing_id) !== 'publish') {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }

    $unit_price = stakmv_rest_get_listing_unit_price($product, $listing_id);
    if ($unit_price <= 0) {
        return new WP_Error('not_purchasable', 'Listing is not purchasable.', array('status' => 409));
    }

    $user_id = get_current_user_id();
    $state = stakmv_rest_get_user_cart_state($user_id);

    $item_key = sanitize_key((string) ($payload['item_key'] ?? ''));
    if ($item_key === '') {
        $item_key = 'listing_' . $listing_id;
    }

    $replace = !empty($payload['replace']);
    if (isset($state['items'][$item_key]) && !$replace) {
        $quantity += max(1, (int) ($state['items'][$item_key]['quantity'] ?? 1));
    }

    $available_quantity = stakmv_rest_get_listing_available_quantity($listing_id);
    if ($available_quantity !== PHP_INT_MAX && $quantity > $available_quantity) {
        return new WP_Error(
            'out_of_stock',
            'Requested quantity exceeds available stock.',
            array(
                'status' => 409,
                'available_quantity' => $available_quantity,
            )
        );
    }

    $state['items'][$item_key] = array(
        'listing_id' => $listing_id,
        'quantity' => $quantity,
    );

    $state = stakmv_rest_save_user_cart_state($user_id, $state);
    $built = stakmv_rest_cart_build_snapshot($state);
    stakmv_rest_save_user_cart_state($user_id, $built['state']);

    return new WP_REST_Response($built['snapshot'], 201);
}

function stakmv_rest_cart_items_update($request) {
    $item_key = sanitize_key((string) $request['item_key']);
    if ($item_key === '') {
        return new WP_Error('invalid_item', 'Invalid cart item key.', array('status' => 400));
    }

    $payload = stakmv_rest_get_payload($request);
    $quantity = (int) ($payload['quantity'] ?? 1);

    $user_id = get_current_user_id();
    $state = stakmv_rest_get_user_cart_state($user_id);
    if (!isset($state['items'][$item_key])) {
        return new WP_Error('not_found', 'Cart item not found.', array('status' => 404));
    }

    if ($quantity <= 0) {
        unset($state['items'][$item_key]);
    } else {
        $listing_id = (int) ($state['items'][$item_key]['listing_id'] ?? 0);
        $available_quantity = stakmv_rest_get_listing_available_quantity($listing_id);
        if ($available_quantity !== PHP_INT_MAX && $quantity > $available_quantity) {
            return new WP_Error(
                'out_of_stock',
                'Requested quantity exceeds available stock.',
                array(
                    'status' => 409,
                    'available_quantity' => $available_quantity,
                )
            );
        }
        $state['items'][$item_key]['quantity'] = max(1, $quantity);
    }

    $state = stakmv_rest_save_user_cart_state($user_id, $state);
    $built = stakmv_rest_cart_build_snapshot($state);
    stakmv_rest_save_user_cart_state($user_id, $built['state']);

    return new WP_REST_Response($built['snapshot']);
}

function stakmv_rest_cart_items_delete($request) {
    $item_key = sanitize_key((string) $request['item_key']);
    if ($item_key === '') {
        return new WP_Error('invalid_item', 'Invalid cart item key.', array('status' => 400));
    }

    $user_id = get_current_user_id();
    $state = stakmv_rest_get_user_cart_state($user_id);
    if (isset($state['items'][$item_key])) {
        unset($state['items'][$item_key]);
    }

    $state = stakmv_rest_save_user_cart_state($user_id, $state);
    $built = stakmv_rest_cart_build_snapshot($state);
    stakmv_rest_save_user_cart_state($user_id, $built['state']);

    return new WP_REST_Response($built['snapshot']);
}

function stakmv_rest_cart_coupons_add($request) {
    if (!class_exists('WC_Coupon')) {
        return new WP_Error('missing_dependency', 'WooCommerce coupons are unavailable.', array('status' => 501));
    }

    $payload = stakmv_rest_get_payload($request);
    $code = stakmv_rest_sanitize_coupon_code($payload['code'] ?? '');
    if ($code === '') {
        return new WP_Error('invalid_coupon', 'Coupon code is required.', array('status' => 400));
    }

    $coupon = new WC_Coupon($code);
    if ((int) $coupon->get_id() <= 0) {
        return new WP_Error('not_found', 'Coupon not found.', array('status' => 404));
    }

    $user_id = get_current_user_id();
    $state = stakmv_rest_get_user_cart_state($user_id);
    if (!in_array($code, (array) $state['coupons'], true)) {
        $state['coupons'][] = $code;
    }

    $state = stakmv_rest_save_user_cart_state($user_id, $state);
    $built = stakmv_rest_cart_build_snapshot($state);
    stakmv_rest_save_user_cart_state($user_id, $built['state']);

    return new WP_REST_Response($built['snapshot']);
}

function stakmv_rest_cart_coupons_delete($request) {
    $code = stakmv_rest_sanitize_coupon_code((string) $request['code']);
    if ($code === '') {
        return new WP_Error('invalid_coupon', 'Invalid coupon code.', array('status' => 400));
    }

    $user_id = get_current_user_id();
    $state = stakmv_rest_get_user_cart_state($user_id);
    $state['coupons'] = array_values(array_filter((array) $state['coupons'], function ($existing) use ($code) {
        return stakmv_rest_sanitize_coupon_code($existing) !== $code;
    }));

    $state = stakmv_rest_save_user_cart_state($user_id, $state);
    $built = stakmv_rest_cart_build_snapshot($state);
    stakmv_rest_save_user_cart_state($user_id, $built['state']);

    return new WP_REST_Response($built['snapshot']);
}

function stakmv_rest_cart_customer_update($request) {
    $payload = stakmv_rest_get_payload($request);

    $user_id = get_current_user_id();
    $state = stakmv_rest_get_user_cart_state($user_id);
    $existing_customer = is_array($state['customer']) ? $state['customer'] : array();
    $incoming_customer = isset($payload['customer']) && is_array($payload['customer']) ? $payload['customer'] : $payload;

    $merged_customer = array(
        'billing' => array_merge((array) ($existing_customer['billing'] ?? array()), (array) ($incoming_customer['billing'] ?? array())),
        'shipping' => array_merge((array) ($existing_customer['shipping'] ?? array()), (array) ($incoming_customer['shipping'] ?? array())),
        'note' => array_key_exists('note', $incoming_customer) ? (string) $incoming_customer['note'] : (string) ($existing_customer['note'] ?? ''),
        'payment_method' => array_key_exists('payment_method', $incoming_customer) ? (string) $incoming_customer['payment_method'] : (string) ($existing_customer['payment_method'] ?? ''),
        'payment_method_title' => array_key_exists('payment_method_title', $incoming_customer) ? (string) $incoming_customer['payment_method_title'] : (string) ($existing_customer['payment_method_title'] ?? ''),
    );

    $state['customer'] = stakmv_rest_sanitize_cart_customer($merged_customer);
    $state = stakmv_rest_save_user_cart_state($user_id, $state);
    $built = stakmv_rest_cart_build_snapshot($state);
    stakmv_rest_save_user_cart_state($user_id, $built['state']);

    return new WP_REST_Response($built['snapshot']);
}

function stakmv_rest_checkout_payment_methods($request) {
    if (!function_exists('WC') || !WC() || !method_exists(WC(), 'payment_gateways')) {
        return new WP_Error('missing_dependency', 'WooCommerce payment gateways are unavailable.', array('status' => 501));
    }

    $payment_gateways = WC()->payment_gateways();
    if (!is_object($payment_gateways) || !method_exists($payment_gateways, 'get_available_payment_gateways')) {
        return new WP_Error('missing_dependency', 'WooCommerce payment gateways are unavailable.', array('status' => 501));
    }

    $available = (array) $payment_gateways->get_available_payment_gateways();
    $items = array();
    foreach ($available as $gateway) {
        if (!($gateway instanceof WC_Payment_Gateway)) {
            continue;
        }
        $items[] = array(
            'id' => sanitize_key((string) $gateway->id),
            'title' => wp_kses_post((string) $gateway->get_title()),
            'description' => wp_kses_post((string) $gateway->get_description()),
            'enabled' => $gateway->enabled === 'yes',
            'supports' => array_values(array_map('sanitize_key', (array) $gateway->supports)),
        );
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'items' => $items,
    ));
}

function stakmv_rest_track_recently_viewed_listing($listing_id, $user_id = 0) {
    $listing_id = (int) $listing_id;
    $user_id = (int) $user_id;
    if ($listing_id <= 0) {
        return;
    }

    if ($user_id <= 0) {
        $user_id = get_current_user_id();
    }
    if ($user_id <= 0) {
        return;
    }

    $raw_items = get_user_meta($user_id, 'stakmv_recently_viewed', true);
    $raw_items = is_array($raw_items) ? $raw_items : array();

    $normalized = array();
    foreach ($raw_items as $row) {
        if (is_array($row)) {
            $row_id = (int) ($row['id'] ?? 0);
            if ($row_id > 0 && !isset($normalized[$row_id])) {
                $normalized[$row_id] = array(
                    'id' => $row_id,
                    'viewed_at' => sanitize_text_field((string) ($row['viewed_at'] ?? '')),
                );
            }
            continue;
        }

        $row_id = (int) $row;
        if ($row_id > 0 && !isset($normalized[$row_id])) {
            $normalized[$row_id] = array(
                'id' => $row_id,
                'viewed_at' => '',
            );
        }
    }

    if (isset($normalized[$listing_id])) {
        unset($normalized[$listing_id]);
    }

    $recent = array(
        'id' => $listing_id,
        'viewed_at' => current_time('mysql'),
    );

    $ordered = array_merge(array($recent), array_values($normalized));
    $ordered = array_slice($ordered, 0, 100);
    update_user_meta($user_id, 'stakmv_recently_viewed', $ordered);
}

function stakmv_rest_get_recently_viewed_rows($user_id) {
    $raw_items = get_user_meta((int) $user_id, 'stakmv_recently_viewed', true);
    $raw_items = is_array($raw_items) ? $raw_items : array();

    $rows = array();
    foreach ($raw_items as $row) {
        if (is_array($row)) {
            $id = (int) ($row['id'] ?? 0);
            if ($id <= 0 || isset($rows[$id])) {
                continue;
            }
            $rows[$id] = array(
                'id' => $id,
                'viewed_at' => sanitize_text_field((string) ($row['viewed_at'] ?? '')),
            );
            continue;
        }

        $id = (int) $row;
        if ($id > 0 && !isset($rows[$id])) {
            $rows[$id] = array(
                'id' => $id,
                'viewed_at' => '',
            );
        }
    }

    return array_values($rows);
}

function stakmv_rest_get_user_bid_rows($user_id, $page, $per_page) {
    $user_id = (int) $user_id;
    $page = max(1, (int) $page);
    $per_page = max(1, min(50, (int) $per_page));
    $offset = ($page - 1) * $per_page;

    global $wpdb;
    $bids_table = esc_sql(defined('STAKMV_AUCTION_BIDS_TABLE') ? STAKMV_AUCTION_BIDS_TABLE : ($wpdb->prefix . 'stakmv_auction_bids'));

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Lightweight table existence check for plugin-owned bids table.
    $exists = ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $bids_table)) === $bids_table);
    if (!$exists) {
        return array(
            'rows' => array(),
            'total' => 0,
            'total_pages' => 0,
        );
    }

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Read-only aggregate query on plugin-owned bids table.
    $total = (int) $wpdb->get_var(
        $wpdb->prepare(
            'SELECT COUNT(DISTINCT auction_id) FROM %i WHERE user_id = %d',
            $bids_table,
            $user_id
        )
    );

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Read-only buyer bid summary query.
    $rows = $wpdb->get_results(
        $wpdb->prepare(
            'SELECT b.auction_id,
                    COUNT(*) AS my_bid_count,
                    MAX(b.bid_amount) AS my_highest_bid,
                    MAX(b.created_at) AS latest_bid_at,
                    (
                        SELECT bb.bid_amount
                        FROM %i bb
                        WHERE bb.user_id = %d AND bb.auction_id = b.auction_id
                        ORDER BY bb.id DESC
                        LIMIT 1
                    ) AS latest_bid_amount
             FROM %i b
             WHERE b.user_id = %d
             GROUP BY b.auction_id
             ORDER BY latest_bid_at DESC
             LIMIT %d OFFSET %d',
            $bids_table,
            $user_id,
            $bids_table,
            $user_id,
            $per_page,
            $offset
        ),
        ARRAY_A
    );

    return array(
        'rows' => is_array($rows) ? $rows : array(),
        'total' => $total,
        'total_pages' => ($total > 0 ? (int) ceil($total / $per_page) : 0),
    );
}

function stakmv_rest_apply_listing_payload($product_id, array $payload, $user_id, $is_update) {
    $product_id = (int) $product_id;
    $product = wc_get_product($product_id);
    if (!($product instanceof WC_Product)) {
        return new WP_Error('invalid_listing', 'Invalid listing.', array('status' => 404));
    }

    $auctions_enabled = function_exists('stakmv_auctions_enabled')
        ? stakmv_auctions_enabled()
        : (stakmv_get_option('enable_auctions', 'yes') === 'yes');

    $bid_count = function_exists('stakmv_get_bid_count') ? (int) stakmv_get_bid_count($product_id) : (int) get_post_meta($product_id, '_stakmv_auction_bid_count', true);
    $has_bids_lock = ($bid_count > 0);

    $listing_type = sanitize_text_field((string) ($payload['listing_type'] ?? ''));
    if ($listing_type === '' && $is_update) {
        $listing_type = sanitize_text_field((string) get_post_meta($product_id, '_stakmv_auction_listing_type', true));
    }
    if (!in_array($listing_type, array('auction_only', 'buy_now_only', 'auction_buy_now'), true)) {
        $listing_type = 'auction_only';
    }

    if (!$auctions_enabled) {
        if ($is_update) {
            $existing_listing_type = sanitize_text_field((string) get_post_meta($product_id, '_stakmv_auction_listing_type', true));
            $existing_is_auction = in_array($existing_listing_type, array('auction_only', 'auction_buy_now'), true);
            if (!$existing_is_auction) {
                $listing_type = 'buy_now_only';
            }
        } else {
            $listing_type = 'buy_now_only';
        }
    }

    if ($has_bids_lock) {
        do_action('stakmv_listing_save_locked', $product_id, $payload, $user_id);
        do_action('stakmv_listing_saved', $product_id, $payload, $user_id, $product_id);
        return array('notice' => 'Listing has bids and is locked.');
    }

    if (array_key_exists('title', $payload)) {
        $product->set_name(sanitize_text_field((string) $payload['title']));
    }
    if (array_key_exists('description', $payload)) {
        $product->set_description(wp_kses_post((string) $payload['description']));
    }
    if (array_key_exists('status', $payload)) {
        $product->set_status(stakmv_rest_sanitize_status($payload['status']));
    }
    $product->save();

    $product_id = $product->get_id();

    $quantity_is_provided = array_key_exists('quantity', $payload);
    $quantity_value = $quantity_is_provided ? max(1, absint((string) $payload['quantity'])) : 0;
    if (!$quantity_is_provided && $is_update) {
        $existing_quantity = $product->get_stock_quantity();
        if ($existing_quantity !== null && (int) $existing_quantity > 0) {
            $quantity_is_provided = true;
            $quantity_value = max(1, (int) $existing_quantity);
        }
    }
    if ($quantity_is_provided) {
        $product->set_manage_stock(true);
        $product->set_stock_quantity($quantity_value);
        $product->set_stock_status($quantity_value > 0 ? 'instock' : 'outofstock');
        $product->save();
    }

    $is_buy_now_capable = in_array($listing_type, array('buy_now_only', 'auction_buy_now'), true);
    $is_stakmv_auction_capable = in_array($listing_type, array('auction_only', 'auction_buy_now'), true);

    if (isset($payload['start_price']) && !$has_bids_lock) {
        $start_price = wc_format_decimal($payload['start_price']);
        update_post_meta($product_id, '_stakmv_auction_start_price', $start_price);
        update_post_meta($product_id, '_regular_price', $start_price);
        update_post_meta($product_id, '_price', $start_price);
    }

    $existing_buy_now = $is_update ? get_post_meta($product_id, '_stakmv_auction_buy_now_price', true) : '';
    $buy_now = '';
    if (!$is_buy_now_capable) {
        delete_post_meta($product_id, '_stakmv_auction_buy_now_price');
    } else {
        $buy_now_raw = array_key_exists('buy_now_price', $payload) ? trim((string) $payload['buy_now_price']) : '';
        if ($buy_now_raw !== '') {
            $buy_now = wc_format_decimal($buy_now_raw);
            update_post_meta($product_id, '_stakmv_auction_buy_now_price', $buy_now);
        } elseif ($is_update && $existing_buy_now !== '') {
            $buy_now = wc_format_decimal($existing_buy_now);
            update_post_meta($product_id, '_stakmv_auction_buy_now_price', $buy_now);
        } else {
            delete_post_meta($product_id, '_stakmv_auction_buy_now_price');
        }

        if ($listing_type === 'buy_now_only' && $buy_now !== '') {
            update_post_meta($product_id, '_regular_price', $buy_now);
            update_post_meta($product_id, '_price', $buy_now);
        }
    }

    // Free core does not own offers feature; Pro handles this meta via hooks.
    delete_post_meta($product_id, '_stakmv_auction_accept_offers');

    if ($listing_type === 'auction_buy_now' && !empty($buy_now)) {
        update_post_meta($product_id, '_price', $buy_now);
    }

    if (array_key_exists('end_date', $payload)) {
        $end_date_raw = trim((string) $payload['end_date']);
        if ($end_date_raw === '') {
            delete_post_meta($product_id, '_stakmv_auction_end_time');
        } else {
            $end_ts = is_numeric($end_date_raw) ? (int) $end_date_raw : strtotime(sanitize_text_field($end_date_raw));
            update_post_meta($product_id, '_stakmv_auction_end_time', $end_ts);
        }
    }

    $mode_map = array(
        'auction_only' => 'auction',
        'buy_now_only' => 'buy_now',
        'auction_buy_now' => 'both',
    );
    update_post_meta($product_id, '_stakmv_auction_listing_type', $listing_type);
    update_post_meta($product_id, '_stakmv_auction_sale_mode', $mode_map[$listing_type] ?? 'auction');

    $shipping_mode = sanitize_key((string) ($payload['shipping_mode'] ?? ''));
    if (!in_array($shipping_mode, array('calculated', 'flat', 'free', 'pickup_only'), true)) {
        return new WP_Error('invalid_shipping', 'Please select a shipping type.', array('status' => 400));
    }
    update_post_meta($product_id, '_stakmv_auction_shipping_mode', $shipping_mode);

    $shipping_scope = sanitize_key((string) ($payload['shipping_scope'] ?? 'international'));
    if (!in_array($shipping_scope, array('international', 'domestic', 'selected'), true)) {
        $shipping_scope = 'international';
    }

    $shipping_allowed_countries = array();
    $shipping_excluded_countries = array();
    $country_map = function_exists('WC') && WC()->countries ? WC()->countries->get_countries() : array();
    $valid_country_codes = array_fill_keys(array_keys($country_map), true);

    if (!empty($payload['shipping_countries']) && is_array($payload['shipping_countries'])) {
        foreach ($payload['shipping_countries'] as $country_code) {
            $country_code = strtoupper(sanitize_text_field((string) $country_code));
            if ($country_code !== '' && isset($valid_country_codes[$country_code])) {
                $shipping_allowed_countries[] = $country_code;
            }
        }
        $shipping_allowed_countries = array_values(array_unique($shipping_allowed_countries));
    }

    if (!empty($payload['shipping_excluded_countries']) && is_array($payload['shipping_excluded_countries'])) {
        foreach ($payload['shipping_excluded_countries'] as $country_code) {
            $country_code = strtoupper(sanitize_text_field((string) $country_code));
            if ($country_code !== '' && isset($valid_country_codes[$country_code])) {
                $shipping_excluded_countries[] = $country_code;
            }
        }
        $shipping_excluded_countries = array_values(array_unique($shipping_excluded_countries));
    }

    if ($shipping_mode === 'pickup_only') {
        delete_post_meta($product_id, '_stakmv_auction_shipping_scope');
        delete_post_meta($product_id, '_stakmv_auction_shipping_countries');
        delete_post_meta($product_id, '_stakmv_auction_shipping_excluded_countries');
    } else {
        if ($shipping_scope === 'selected' && empty($shipping_allowed_countries)) {
            return new WP_Error('invalid_shipping', 'Please select at least one allowed country.', array('status' => 400));
        }
        update_post_meta($product_id, '_stakmv_auction_shipping_scope', $shipping_scope);
        update_post_meta($product_id, '_stakmv_auction_shipping_countries', $shipping_allowed_countries);
        update_post_meta($product_id, '_stakmv_auction_shipping_excluded_countries', $shipping_excluded_countries);
    }

    update_post_meta($product_id, '_weight', wc_format_decimal($payload['weight'] ?? ''));
    update_post_meta($product_id, '_length', wc_format_decimal($payload['length'] ?? ''));
    update_post_meta($product_id, '_width', wc_format_decimal($payload['width'] ?? ''));
    update_post_meta($product_id, '_height', wc_format_decimal($payload['height'] ?? ''));

    $carrier_options = array('USPS', 'UPS', 'FedEx', 'DHL');
    $selected_carriers = array();
    if (!empty($payload['shipping_carriers']) && is_array($payload['shipping_carriers'])) {
        foreach (array_map('sanitize_text_field', $payload['shipping_carriers']) as $carrier) {
            if (in_array($carrier, $carrier_options, true)) {
                $selected_carriers[] = $carrier;
            }
        }
        $selected_carriers = array_values(array_unique($selected_carriers));
    }

    if ($shipping_mode === 'calculated') {
        if (empty($selected_carriers)) {
            $selected_carriers = $carrier_options;
        }
        update_post_meta($product_id, '_stakmv_auction_shipping_carriers', $selected_carriers);
        update_post_meta($product_id, '_stakmv_auction_shipping_carrier', $selected_carriers[0]);
    } elseif ($shipping_mode === 'pickup_only') {
        delete_post_meta($product_id, '_stakmv_auction_shipping_carriers');
        update_post_meta($product_id, '_stakmv_auction_shipping_carrier', 'Pickup Only');
    } elseif ($shipping_mode === 'free') {
        delete_post_meta($product_id, '_stakmv_auction_shipping_carriers');
        update_post_meta($product_id, '_stakmv_auction_shipping_carrier', 'Free Shipping');
    } else {
        delete_post_meta($product_id, '_stakmv_auction_shipping_carriers');
        update_post_meta($product_id, '_stakmv_auction_shipping_carrier', '');
    }

    if (!empty($payload['product_cat'])) {
        wp_set_object_terms($product_id, array((int) $payload['product_cat']), 'product_cat');
    }

    if (array_key_exists('condition', $payload)) {
        update_post_meta($product_id, '_stakmv_auction_condition', sanitize_text_field((string) $payload['condition']));
    }
    if (array_key_exists('location', $payload)) {
        update_post_meta($product_id, '_stakmv_auction_location', sanitize_text_field((string) $payload['location']));
    }

    $handling_time_days = isset($payload['handling_time_days']) ? (int) $payload['handling_time_days'] : 0;
    $handling_time_days = max(0, min(30, $handling_time_days));
    update_post_meta($product_id, '_stakmv_auction_handling_time_days', $handling_time_days);

    $returns_policy = sanitize_key((string) ($payload['returns_policy'] ?? 'no_returns'));
    if (in_array($returns_policy, array('returns_14_days', 'returns_30_days'), true)) {
        $returns_policy = 'returns_accepted';
    }
    if (!in_array($returns_policy, array('no_returns', 'returns_accepted'), true)) {
        $returns_policy = 'no_returns';
    }
    update_post_meta($product_id, '_stakmv_auction_returns_policy', $returns_policy);

    $returns_days = isset($payload['returns_days']) ? (int) $payload['returns_days'] : 0;
    $returns_days = max(1, min(60, $returns_days));

    $return_shipping_paid_by = sanitize_key((string) ($payload['return_shipping_paid_by'] ?? 'buyer'));
    if (!in_array($return_shipping_paid_by, array('buyer', 'seller'), true)) {
        $return_shipping_paid_by = 'buyer';
    }

    if ($returns_policy === 'returns_accepted') {
        update_post_meta($product_id, '_stakmv_auction_returns_days', $returns_days);
        update_post_meta($product_id, '_stakmv_auction_return_shipping_paid_by', $return_shipping_paid_by);
    } else {
        delete_post_meta($product_id, '_stakmv_auction_returns_days');
        delete_post_meta($product_id, '_stakmv_auction_return_shipping_paid_by');
    }

    $shipping_cost_raw = array_key_exists('shipping_cost', $payload) ? trim((string) $payload['shipping_cost']) : '';
    if ($shipping_mode === 'flat') {
        if ($shipping_cost_raw === '') {
            return new WP_Error('invalid_shipping', 'Flat shipping cost is required for flat rate.', array('status' => 400));
        }
        $shipping_cost_value = wc_format_decimal($shipping_cost_raw);
        if ($shipping_cost_value === '' || (float) $shipping_cost_value <= 0) {
            return new WP_Error('invalid_shipping', 'Flat shipping cost must be greater than 0.', array('status' => 400));
        }
        update_post_meta($product_id, '_stakmv_auction_shipping_cost', $shipping_cost_value);
    } else {
        delete_post_meta($product_id, '_stakmv_auction_shipping_cost');
    }

    if (!empty($payload['faq']) && is_array($payload['faq'])) {
        $faq_rows = array();
        foreach ($payload['faq'] as $row) {
            if (!is_array($row)) {
                continue;
            }
            $faq_q = sanitize_text_field((string) ($row['q'] ?? ''));
            $faq_a = sanitize_textarea_field((string) ($row['a'] ?? ''));
            if ($faq_q === '' && $faq_a === '') {
                continue;
            }
            $faq_rows[] = array('q' => $faq_q, 'a' => $faq_a);
        }
        if (!empty($faq_rows)) {
            update_post_meta($product_id, '_stakmv_listing_faqs', $faq_rows);
        }
    }

    // Set auction type before save, then enforce it again after save (save() may reset type).
    wp_set_object_terms($product_id, 'auction', 'product_type');

    $featured_image_id = isset($payload['featured_image_id']) ? (int) $payload['featured_image_id'] : 0;
    if ($featured_image_id > 0) {
        $product->set_image_id($featured_image_id);
    }

    $gallery_ids = array();
    if (!empty($payload['gallery_image_ids']) && is_array($payload['gallery_image_ids'])) {
        $gallery_ids = array_values(array_filter(array_map('intval', $payload['gallery_image_ids'])));
    }
    if (!empty($gallery_ids)) {
        $product->set_gallery_image_ids($gallery_ids);
    }
    $product->save();

    // Keep product type stable for app-created listings.
    wp_set_object_terms($product_id, 'auction', 'product_type');
    stakmv_rest_maybe_sync_listing_slug($product_id, (string) ($payload['title'] ?? ''), false);
    wc_delete_product_transients($product_id);

    do_action('stakmv_listing_saved', $product_id, $payload, $user_id, $product_id);
    return array('notice' => $is_update ? 'Listing updated.' : 'Listing created.');
}

function stakmv_rest_vendor_listing_show($request) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }
    if (!stakmv_rest_can_manage_listings($user_id)) {
        return new WP_Error('forbidden', 'You are not allowed to view listings.', array('status' => 403));
    }

    $id = (int) $request['id'];
    $post = get_post($id);
    if (!$post || $post->post_type !== 'product') {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }

    if ((int) $post->post_author !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot view this listing.', array('status' => 403));
    }

    $product = wc_get_product($id);
    if (!($product instanceof WC_Product)) {
        return new WP_Error('invalid_listing', 'Invalid listing.', array('status' => 404));
    }

    return new WP_REST_Response(array('ok' => true, 'item' => stakmv_rest_listing_payload($product, true)));
}

function stakmv_rest_listings_index($request) {
    $page = max(1, (int) $request->get_param('page'));
    $per_page = max(1, min(50, (int) $request->get_param('per_page')));
    $category_id = max(0, (int) $request->get_param('category_id'));

    $args = array(
        'status' => 'publish',
        'type' => 'auction',
        'paginate' => true,
        'page' => $page,
        'limit' => $per_page,
        'orderby' => 'date',
        'order' => 'DESC',
    );

    $search = sanitize_text_field((string) $request->get_param('search'));
    if ($search !== '') {
        $args['s'] = $search;
    }

    if ($category_id > 0) {
        $args['category'] = array($category_id);
    }

    $buy_now_only = sanitize_text_field((string) $request->get_param('buy_now_only'));
    if ($buy_now_only === '1' || strtolower($buy_now_only) === 'true') {
        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Intentional filter for buy-now-only listing mode.
        $args['meta_key'] = '_stakmv_auction_listing_type';
        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Intentional filter for buy-now-only listing mode.
        $args['meta_value'] = 'buy_now_only';
    }

    $results = wc_get_products($args);
    $products = is_array($results) ? $results : (is_object($results) && isset($results->products) ? $results->products : array());
    $total = is_object($results) && isset($results->total) ? (int) $results->total : count($products);
    $max_pages = is_object($results) && isset($results->max_num_pages) ? (int) $results->max_num_pages : 1;

    $items = array();
    foreach ($products as $product) {
        if (!($product instanceof WC_Product)) continue;
        $items[] = stakmv_rest_listing_payload($product);
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'items' => $items,
        'pagination' => array(
            'page' => $page,
            'per_page' => $per_page,
            'total' => $total,
            'total_pages' => $max_pages,
        ),
    ));
}

function stakmv_rest_listings_show($request) {
    $id = (int) $request['id'];
    $product = wc_get_product($id);
    if (!($product instanceof WC_Product) || $product->get_status() !== 'publish') {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }

    if (is_user_logged_in()) {
        stakmv_rest_track_recently_viewed_listing($id, get_current_user_id());
    }

    return new WP_REST_Response(array('ok' => true, 'item' => stakmv_rest_listing_payload($product, true)));
}

function stakmv_rest_listing_mark_viewed($request) {
    $id = (int) $request['id'];
    $product = wc_get_product($id);
    if (!($product instanceof WC_Product) || $product->get_status() !== 'publish') {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }

    stakmv_rest_track_recently_viewed_listing($id, get_current_user_id());

    return new WP_REST_Response(array(
        'ok' => true,
        'listing_id' => $id,
    ));
}

function stakmv_rest_listings_similar($request) {
    $id = (int) $request['id'];
    $related = wc_get_related_products($id, 8);
    $items = array();
    foreach ((array) $related as $rid) {
        $p = wc_get_product((int) $rid);
        if (!($p instanceof WC_Product) || $p->get_status() !== 'publish') continue;
        $items[] = stakmv_rest_listing_payload($p);
    }
    return new WP_REST_Response(array('ok' => true, 'items' => $items));
}

function stakmv_rest_shipping_preview($request) {
    $id = (int) $request['id'];
    return new WP_REST_Response(array(
        'ok' => true,
        'listing_id' => $id,
        'shipping_mode' => function_exists('stakmv_get_listing_shipping_mode') ? stakmv_get_listing_shipping_mode($id) : 'calculated',
        'message' => 'Use /shipping/rates/quote for live quote integration.',
    ));
}

function stakmv_rest_auction_bid($request) {
    $auction_id = (int) $request['id'];
    $user_id = get_current_user_id();
    $amount = (float) $request->get_param('amount');

    if ($auction_id <= 0 || $amount <= 0) {
        return new WP_Error('invalid_payload', 'Invalid auction id or bid amount.', array('status' => 400));
    }

    $product = wc_get_product($auction_id);
    if (!($product instanceof WC_Product) || $product->get_type() !== 'auction') {
        return new WP_Error('invalid_auction', 'Auction not found.', array('status' => 404));
    }

    $author_id = (int) get_post_field('post_author', $auction_id);
    if ($author_id === $user_id) {
        return new WP_Error('owner_bid_forbidden', 'You cannot bid on your own listing.', array('status' => 403));
    }

    $status = (string) get_post_meta($auction_id, '_stakmv_auction_status', true);
    $end_raw = get_post_meta($auction_id, '_stakmv_auction_end_time', true);
    $end_ts = is_numeric($end_raw) ? (int) $end_raw : ($end_raw ? strtotime((string) $end_raw) : 0);
    if ($status === 'sold' || ($end_ts && time() >= $end_ts)) {
        return new WP_Error('auction_ended', 'Auction has ended.', array('status' => 409));
    }

    $current = function_exists('stakmv_get_highest_bid') ? (float) stakmv_get_highest_bid($auction_id) : 0.0;
    $start = (float) get_post_meta($auction_id, '_stakmv_auction_start_price', true);
    $minimum = max($current + 0.01, $start > 0 ? $start : 0.01);

    if ($amount < $minimum) {
        return new WP_Error('bid_too_low', 'Bid is below minimum allowed.', array('status' => 400, 'minimum' => $minimum));
    }

    global $wpdb;
    $table = $wpdb->prefix . 'stakmv_auction_bids';
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Auction bids persistence.
    $inserted = $wpdb->insert(
        $table,
        array(
            'auction_id' => $auction_id,
            'user_id' => $user_id,
            'bid_amount' => $amount,
            'created_at' => current_time('mysql'),
        ),
        array('%d', '%d', '%f', '%s')
    );

    if (!$inserted) {
        return new WP_Error('db_error', 'Could not place bid.', array('status' => 500));
    }

    update_post_meta($auction_id, '_price', $amount);

    return new WP_REST_Response(array(
        'ok' => true,
        'auction_id' => $auction_id,
        'current_bid' => $amount,
        'bid_count' => function_exists('stakmv_get_bid_count') ? (int) stakmv_get_bid_count($auction_id) : 1,
    ));
}

function stakmv_rest_auction_history($request) {
    $auction_id = (int) $request['id'];
    if ($auction_id <= 0) {
        return new WP_Error('invalid_id', 'Invalid listing id.', array('status' => 400));
    }

    global $wpdb;
    $table = $wpdb->prefix . 'stakmv_auction_bids';
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Auction history query is read-only.
    $rows = $wpdb->get_results(
        $wpdb->prepare(
            'SELECT user_id, bid_amount, created_at FROM %i WHERE auction_id = %d ORDER BY id DESC LIMIT 100',
            $table,
            $auction_id
        ),
        ARRAY_A
    );

    $items = array();
    foreach ((array) $rows as $row) {
        $uid = (int) ($row['user_id'] ?? 0);
        $u = $uid ? get_userdata($uid) : null;
        $items[] = array(
            'user_id' => $uid,
            'user_name' => $u ? (string) $u->display_name : 'User',
            'amount' => (float) ($row['bid_amount'] ?? 0),
            'created_at' => (string) ($row['created_at'] ?? ''),
        );
    }

    return new WP_REST_Response(array('ok' => true, 'items' => $items));
}

function stakmv_rest_buying_bids_index($request) {
    $user_id = get_current_user_id();
    list($page, $per_page) = stakmv_rest_get_pagination_params($request, 50);

    $result = stakmv_rest_get_user_bid_rows($user_id, $page, $per_page);
    $rows = (array) ($result['rows'] ?? array());
    $total = (int) ($result['total'] ?? 0);
    $total_pages = (int) ($result['total_pages'] ?? 0);

    $items = array();
    foreach ($rows as $row) {
        $auction_id = (int) ($row['auction_id'] ?? 0);
        if ($auction_id <= 0) {
            continue;
        }

        $product = wc_get_product($auction_id);
        $listing = ($product instanceof WC_Product) ? stakmv_rest_listing_payload($product) : null;

        $items[] = array(
            'auction_id' => $auction_id,
            'my_bid_count' => (int) ($row['my_bid_count'] ?? 0),
            'my_highest_bid' => (float) ($row['my_highest_bid'] ?? 0),
            'my_latest_bid' => (float) ($row['latest_bid_amount'] ?? 0),
            'my_latest_bid_at' => sanitize_text_field((string) ($row['latest_bid_at'] ?? '')),
            'listing' => $listing,
        );
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'items' => $items,
        'pagination' => array(
            'page' => $page,
            'per_page' => $per_page,
            'total' => $total,
            'total_pages' => $total_pages,
        ),
    ));
}

function stakmv_rest_buying_recently_viewed_index($request) {
    $user_id = get_current_user_id();
    list($page, $per_page) = stakmv_rest_get_pagination_params($request, 50);

    $rows = stakmv_rest_get_recently_viewed_rows($user_id);
    $valid_rows = array();
    foreach ($rows as $row) {
        $listing_id = (int) ($row['id'] ?? 0);
        if ($listing_id <= 0) {
            continue;
        }

        $product = wc_get_product($listing_id);
        if (!($product instanceof WC_Product) || get_post_status($listing_id) !== 'publish') {
            continue;
        }

        $valid_rows[] = array(
            'id' => $listing_id,
            'viewed_at' => sanitize_text_field((string) ($row['viewed_at'] ?? '')),
        );
    }

    $total = count($valid_rows);
    $total_pages = ($total > 0 ? (int) ceil($total / $per_page) : 0);
    $offset = ($page - 1) * $per_page;
    $slice = array_slice($valid_rows, $offset, $per_page);

    $items = array();
    foreach ($slice as $row) {
        $listing_id = (int) $row['id'];
        $product = wc_get_product($listing_id);
        if (!($product instanceof WC_Product)) {
            continue;
        }

        $item = stakmv_rest_listing_payload($product);
        $item['viewed_at'] = sanitize_text_field((string) $row['viewed_at']);
        $items[] = $item;
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'items' => $items,
        'pagination' => array(
            'page' => $page,
            'per_page' => $per_page,
            'total' => $total,
            'total_pages' => $total_pages,
        ),
    ));
}

function stakmv_rest_buying_purchases_index($request) {
    if (!function_exists('wc_get_orders')) {
        return new WP_Error('missing_dependency', 'WooCommerce orders are unavailable.', array('status' => 501));
    }

    $user_id = get_current_user_id();
    list($page, $per_page) = stakmv_rest_get_pagination_params($request, 50);

    $status = sanitize_key((string) $request->get_param('status'));
    if (strpos($status, 'wc-') === 0) {
        $status = substr($status, 3);
    }

    $query = array(
        'customer_id' => $user_id,
        'orderby' => 'date',
        'order' => 'DESC',
        'limit' => 100,
        'paged' => 1,
        'paginate' => true,
        'return' => 'objects',
    );

    if ($status !== '') {
        $query['status'] = $status;
    }

    $filtered_orders = array();
    $scan_page = 1;
    $scan_max_pages = 50;
    while ($scan_page <= $scan_max_pages) {
        $query['paged'] = $scan_page;
        $results = wc_get_orders($query);
        $orders = (is_object($results) && isset($results->orders)) ? (array) $results->orders : (array) $results;
        if (empty($orders)) {
            break;
        }
        foreach ($orders as $order) {
            if (!($order instanceof WC_Order)) {
                continue;
            }
            if (stakmv_rest_get_order_auction_id($order) > 0) {
                $filtered_orders[] = $order;
            }
        }
        $max_num_pages = (is_object($results) && isset($results->max_num_pages)) ? (int) $results->max_num_pages : 0;
        if ($max_num_pages > 0 && $scan_page >= $max_num_pages) {
            break;
        }
        $scan_page++;
    }

    $total = count($filtered_orders);
    $total_pages = ($total > 0 ? (int) ceil($total / $per_page) : 0);
    $offset = max(0, ($page - 1) * $per_page);
    $orders = array_slice($filtered_orders, $offset, $per_page);

    $items = array();
    foreach ($orders as $order) {
        if (!($order instanceof WC_Order)) {
            continue;
        }

        $auction_id = stakmv_rest_get_order_auction_id($order);
        $product = $auction_id > 0 ? wc_get_product($auction_id) : null;
        $listing = ($product instanceof WC_Product) ? stakmv_rest_listing_payload($product) : null;

        $created = $order->get_date_created();
        $paid = $order->get_date_paid();

        $line_items = array();
        foreach ((array) $order->get_items() as $order_item) {
            if (!($order_item instanceof WC_Order_Item_Product)) {
                continue;
            }
            $line_items[] = array(
                'product_id' => (int) $order_item->get_product_id(),
                'name' => sanitize_text_field((string) $order_item->get_name()),
                'quantity' => (int) $order_item->get_quantity(),
                'total' => (float) $order_item->get_total(),
            );
        }

        $items[] = array(
            'id' => (int) $order->get_id(),
            'number' => (string) $order->get_order_number(),
            'status' => (string) $order->get_status(),
            'currency' => (string) $order->get_currency(),
            'total' => (float) $order->get_total(),
            'created_at' => $created ? (string) $created->date('c') : '',
            'paid_at' => $paid ? (string) $paid->date('c') : '',
            'auction_id' => $auction_id,
            'can_buy_again' => stakmv_rest_listing_is_buy_now_eligible($auction_id),
            'pay_url' => in_array((string) $order->get_status(), array('pending', 'failed'), true) ? $order->get_checkout_payment_url() : '',
            'listing' => $listing,
            'items' => $line_items,
        );
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'items' => $items,
        'pagination' => array(
            'page' => $page,
            'per_page' => $per_page,
            'total' => $total,
            'total_pages' => $total_pages,
        ),
    ));
}

function stakmv_rest_buying_purchase_buy_again($request) {
    if (!function_exists('wc_get_order')) {
        return new WP_Error('missing_dependency', 'WooCommerce orders are unavailable.', array('status' => 501));
    }

    $order_id = (int) $request['order_id'];
    if ($order_id <= 0) {
        return new WP_Error('invalid_order', 'Invalid order id.', array('status' => 400));
    }

    $order = wc_get_order($order_id);
    if (!($order instanceof WC_Order)) {
        return new WP_Error('not_found', 'Order not found.', array('status' => 404));
    }

    $user_id = get_current_user_id();
    if ((int) $order->get_customer_id() !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot reorder this purchase.', array('status' => 403));
    }

    $auction_id = stakmv_rest_get_order_auction_id($order);
    if ($auction_id <= 0) {
        return new WP_Error('invalid_order', 'Order is not linked to a listing.', array('status' => 409));
    }

    $result = stakmv_rest_create_buy_now_order($auction_id, $user_id);
    if (is_wp_error($result)) {
        return $result;
    }

    $result['source_order_id'] = $order_id;
    return new WP_REST_Response($result);
}

function stakmv_rest_checkout_create_order($request) {
    if (!function_exists('wc_create_order')) {
        return new WP_Error('missing_dependency', 'WooCommerce orders are unavailable.', array('status' => 501));
    }

    $user_id = get_current_user_id();
    $state = stakmv_rest_get_user_cart_state($user_id);
    $built = stakmv_rest_cart_build_snapshot($state);
    $state = $built['state'];
    $snapshot = $built['snapshot'];

    if (empty($snapshot['items'])) {
        return new WP_Error('empty_cart', 'Your cart is empty.', array('status' => 409));
    }

    $not_shippable_ids = array();
    foreach ((array) $snapshot['items'] as $item) {
        if (empty($item['is_shippable'])) {
            $not_shippable_ids[] = (int) ($item['listing_id'] ?? 0);
        }
    }
    $not_shippable_ids = array_values(array_filter(array_map('intval', $not_shippable_ids)));
    if (!empty($not_shippable_ids)) {
        return new WP_Error(
            'shipping_not_available',
            'One or more listings cannot be shipped to the selected destination.',
            array(
                'status' => 409,
                'listing_ids' => $not_shippable_ids,
            )
        );
    }

    $order = wc_create_order(array('customer_id' => $user_id));
    if (is_wp_error($order) || !($order instanceof WC_Order)) {
        return new WP_Error('order_create_failed', 'Could not create order.', array('status' => 500));
    }

    $first_listing_id = 0;
    foreach ((array) $snapshot['items'] as $item) {
        $listing_id = (int) ($item['listing_id'] ?? 0);
        $quantity = max(1, (int) ($item['quantity'] ?? 1));
        $line_subtotal = (float) ($item['line_subtotal'] ?? 0.0);

        $product = wc_get_product($listing_id);
        if (!($product instanceof WC_Product) || $line_subtotal <= 0) {
            continue;
        }

        $available_quantity = stakmv_rest_get_listing_available_quantity($listing_id);
        if ($available_quantity !== PHP_INT_MAX && $quantity > $available_quantity) {
            return new WP_Error(
                'out_of_stock',
                'Requested quantity exceeds available stock for one or more listings.',
                array(
                    'status' => 409,
                    'listing_id' => $listing_id,
                    'available_quantity' => $available_quantity,
                )
            );
        }

        if ($first_listing_id <= 0) {
            $first_listing_id = $listing_id;
        }

        $order->add_product(
            $product,
            $quantity,
            array(
                'subtotal' => $line_subtotal,
                'total' => $line_subtotal,
            )
        );
    }

    $customer = (array) ($state['customer'] ?? array());
    $billing = (array) ($customer['billing'] ?? array());
    $shipping = (array) ($customer['shipping'] ?? array());

    $order->set_address($billing, 'billing');
    $order->set_address($shipping, 'shipping');

    $payment_method = sanitize_key((string) ($customer['payment_method'] ?? ''));
    if ($payment_method !== '' && function_exists('WC') && WC() && method_exists(WC(), 'payment_gateways')) {
        $gateways = WC()->payment_gateways()->get_available_payment_gateways();
        if (isset($gateways[$payment_method]) && $gateways[$payment_method] instanceof WC_Payment_Gateway) {
            $gateway = $gateways[$payment_method];
            $order->set_payment_method($gateway);
            $order->set_payment_method_title((string) $gateway->get_title());
        }
    } elseif (!empty($customer['payment_method_title'])) {
        $order->set_payment_method_title(sanitize_text_field((string) $customer['payment_method_title']));
    }

    $shipping_fee = (float) ($snapshot['totals']['shipping'] ?? 0.0);
    if ($shipping_fee > 0) {
        $shipping_item = new WC_Order_Item_Fee();
        $shipping_item->set_name('Shipping');
        $shipping_item->set_total($shipping_fee);
        $order->add_item($shipping_item);
    }

    $applied_coupons = array();
    $coupon_errors = array();
    foreach ((array) ($state['coupons'] ?? array()) as $coupon_code) {
        $coupon_code = stakmv_rest_sanitize_coupon_code($coupon_code);
        if ($coupon_code === '') {
            continue;
        }

        $apply_result = $order->apply_coupon($coupon_code);
        if (is_wp_error($apply_result)) {
            $coupon_errors[] = sanitize_text_field((string) $apply_result->get_error_message());
            continue;
        }
        $applied_coupons[] = $coupon_code;
    }

    if (!empty($customer['note'])) {
        $order->set_customer_note(sanitize_textarea_field((string) $customer['note']));
    }

    $order->update_meta_data('_stakmv_source', 'mobile_app');
    if ($first_listing_id > 0) {
        $order->update_meta_data('_stakmv_auction_id', $first_listing_id);
    }

    $order->set_status('pending', 'Order created via app checkout API.');
    $order->calculate_totals();
    $order->save();

    $pay_url = $order->get_checkout_payment_url();

    $state['items'] = array();
    $state['coupons'] = array();
    stakmv_rest_save_user_cart_state($user_id, $state);

    return new WP_REST_Response(array(
        'ok' => true,
        'order_id' => (int) $order->get_id(),
        'order_number' => (string) $order->get_order_number(),
        'status' => (string) $order->get_status(),
        'pay_url' => esc_url_raw((string) $pay_url),
        'applied_coupons' => $applied_coupons,
        'coupon_errors' => $coupon_errors,
        'purchase' => stakmv_rest_serialize_purchase_order($order),
    ), 201);
}

function stakmv_rest_checkout_pay($request) {
    if (!function_exists('wc_get_order')) {
        return new WP_Error('missing_dependency', 'WooCommerce orders are unavailable.', array('status' => 501));
    }

    $payload = stakmv_rest_get_payload($request);
    $order_id = (int) ($payload['order_id'] ?? 0);
    if ($order_id <= 0) {
        return new WP_Error('invalid_order', 'order_id is required.', array('status' => 400));
    }

    $order = wc_get_order($order_id);
    if (!($order instanceof WC_Order)) {
        return new WP_Error('not_found', 'Order not found.', array('status' => 404));
    }

    $user_id = get_current_user_id();
    if ((int) $order->get_customer_id() !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot access this order.', array('status' => 403));
    }

    $payment_method = sanitize_key((string) ($payload['payment_method'] ?? ''));
    if ($payment_method !== '' && function_exists('WC') && WC() && method_exists(WC(), 'payment_gateways')) {
        $gateways = WC()->payment_gateways()->get_available_payment_gateways();
        if (isset($gateways[$payment_method]) && $gateways[$payment_method] instanceof WC_Payment_Gateway) {
            $gateway = $gateways[$payment_method];
            $order->set_payment_method($gateway);
            $order->set_payment_method_title((string) $gateway->get_title());
            $order->save();
        }
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'order_id' => (int) $order->get_id(),
        'status' => (string) $order->get_status(),
        'pay_url' => esc_url_raw((string) $order->get_checkout_payment_url()),
    ));
}

function stakmv_rest_serialize_purchase_order($order) {
    if (!($order instanceof WC_Order)) {
        return array();
    }

    $auction_id = stakmv_rest_get_order_auction_id($order);
    $created = $order->get_date_created();
    $paid = $order->get_date_paid();

    $line_items = array();
    $seller_ids = array();
    foreach ((array) $order->get_items() as $order_item) {
        if (!($order_item instanceof WC_Order_Item_Product)) {
            continue;
        }

        $product_id = (int) $order_item->get_product_id();
        $product = $product_id > 0 ? wc_get_product($product_id) : null;
        if ($product instanceof WC_Product) {
            $seller_id = (int) get_post_field('post_author', $product_id);
            if ($seller_id > 0 && !in_array($seller_id, $seller_ids, true)) {
                $seller_ids[] = $seller_id;
            }
        }

        $line_items[] = array(
            'product_id' => $product_id,
            'name' => sanitize_text_field((string) $order_item->get_name()),
            'quantity' => (int) $order_item->get_quantity(),
            'subtotal' => (float) $order_item->get_subtotal(),
            'total' => (float) $order_item->get_total(),
        );
    }

    $sellers = array();
    foreach ($seller_ids as $seller_id) {
        $user = get_userdata($seller_id);
        if (!$user) {
            continue;
        }
        $sellers[] = array(
            'id' => $seller_id,
            'name' => function_exists('stakmv_get_vendor_display_name') ? stakmv_get_vendor_display_name((int) $user->ID) : sanitize_text_field((string) $user->display_name),
            'profile_url' => function_exists('stakmv_get_vendor_profile_url') ? esc_url_raw((string) stakmv_get_vendor_profile_url($seller_id)) : '',
        );
    }

    $listing = null;
    if ($auction_id > 0) {
        $product = wc_get_product($auction_id);
        if ($product instanceof WC_Product) {
            $listing = stakmv_rest_listing_payload($product);
        }
    }

    return array(
        'id' => (int) $order->get_id(),
        'number' => (string) $order->get_order_number(),
        'status' => (string) $order->get_status(),
        'currency' => (string) $order->get_currency(),
        'auction_id' => $auction_id,
        'can_buy_again' => stakmv_rest_listing_is_buy_now_eligible($auction_id),
        'created_at' => $created ? (string) $created->date('c') : '',
        'paid_at' => $paid ? (string) $paid->date('c') : '',
        'totals' => array(
            'subtotal' => (float) $order->get_subtotal(),
            'shipping' => (float) $order->get_shipping_total(),
            'discount' => (float) $order->get_discount_total(),
            'tax' => (float) $order->get_total_tax(),
            'total' => (float) $order->get_total(),
        ),
        'payment' => array(
            'method' => sanitize_key((string) $order->get_payment_method()),
            'method_title' => sanitize_text_field((string) $order->get_payment_method_title()),
            'is_paid' => (bool) $order->is_paid(),
            'pay_url' => in_array((string) $order->get_status(), array('pending', 'failed', 'on-hold'), true) ? esc_url_raw((string) $order->get_checkout_payment_url()) : '',
        ),
        'billing' => array(
            'first_name' => sanitize_text_field((string) $order->get_billing_first_name()),
            'last_name' => sanitize_text_field((string) $order->get_billing_last_name()),
            'company' => sanitize_text_field((string) $order->get_billing_company()),
            'address_1' => sanitize_text_field((string) $order->get_billing_address_1()),
            'address_2' => sanitize_text_field((string) $order->get_billing_address_2()),
            'city' => sanitize_text_field((string) $order->get_billing_city()),
            'state' => sanitize_text_field((string) $order->get_billing_state()),
            'postcode' => sanitize_text_field((string) $order->get_billing_postcode()),
            'country' => sanitize_text_field((string) $order->get_billing_country()),
            'email' => sanitize_email((string) $order->get_billing_email()),
            'phone' => sanitize_text_field((string) $order->get_billing_phone()),
        ),
        'shipping' => array(
            'first_name' => sanitize_text_field((string) $order->get_shipping_first_name()),
            'last_name' => sanitize_text_field((string) $order->get_shipping_last_name()),
            'company' => sanitize_text_field((string) $order->get_shipping_company()),
            'address_1' => sanitize_text_field((string) $order->get_shipping_address_1()),
            'address_2' => sanitize_text_field((string) $order->get_shipping_address_2()),
            'city' => sanitize_text_field((string) $order->get_shipping_city()),
            'state' => sanitize_text_field((string) $order->get_shipping_state()),
            'postcode' => sanitize_text_field((string) $order->get_shipping_postcode()),
            'country' => sanitize_text_field((string) $order->get_shipping_country()),
        ),
        'tracking' => stakmv_rest_get_purchase_tracking_data($order),
        'listing' => $listing,
        'items' => $line_items,
        'sellers' => $sellers,
        'actions' => array(
            'can_cancel' => in_array((string) $order->get_status(), array('pending', 'failed', 'on-hold'), true),
            'can_pay' => in_array((string) $order->get_status(), array('pending', 'failed', 'on-hold'), true),
        ),
    );
}

function stakmv_rest_get_purchase_tracking_data($order) {
    if (!($order instanceof WC_Order)) {
        return array();
    }

    $number_keys = array('_stakmv_tracking_number', '_tracking_number', 'tracking_number');
    $carrier_keys = array('_stakmv_tracking_carrier', '_tracking_carrier', 'tracking_carrier');
    $url_keys = array('_stakmv_tracking_url', '_tracking_url', 'tracking_url');

    $tracking_number = '';
    foreach ($number_keys as $key) {
        $value = sanitize_text_field((string) $order->get_meta($key, true));
        if ($value !== '') {
            $tracking_number = $value;
            break;
        }
    }

    $tracking_carrier = '';
    foreach ($carrier_keys as $key) {
        $value = sanitize_text_field((string) $order->get_meta($key, true));
        if ($value !== '') {
            $tracking_carrier = $value;
            break;
        }
    }

    $tracking_url = '';
    foreach ($url_keys as $key) {
        $value = esc_url_raw((string) $order->get_meta($key, true));
        if ($value !== '') {
            $tracking_url = $value;
            break;
        }
    }

    $fulfillment_status = sanitize_key((string) $order->get_meta('_stakmv_fulfillment_status', true));
    if ($fulfillment_status === '') {
        $fulfillment_status = sanitize_key((string) $order->get_meta('_tracking_status', true));
    }
    if ($fulfillment_status === '') {
        $fulfillment_status = $order->has_status(array('completed')) ? 'delivered' : 'pending';
    }

    return array(
        'number' => $tracking_number,
        'carrier' => $tracking_carrier,
        'url' => $tracking_url,
        'status' => $fulfillment_status,
    );
}

function stakmv_rest_buying_purchase_show($request) {
    if (!function_exists('wc_get_order')) {
        return new WP_Error('missing_dependency', 'WooCommerce orders are unavailable.', array('status' => 501));
    }

    $order_id = (int) $request['order_id'];
    if ($order_id <= 0) {
        return new WP_Error('invalid_order', 'Invalid order id.', array('status' => 400));
    }

    $order = wc_get_order($order_id);
    if (!($order instanceof WC_Order)) {
        return new WP_Error('not_found', 'Order not found.', array('status' => 404));
    }

    $user_id = get_current_user_id();
    if ((int) $order->get_customer_id() !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot view this purchase.', array('status' => 403));
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'item' => stakmv_rest_serialize_purchase_order($order),
    ));
}

function stakmv_rest_buying_purchase_cancel($request) {
    if (!function_exists('wc_get_order')) {
        return new WP_Error('missing_dependency', 'WooCommerce orders are unavailable.', array('status' => 501));
    }

    $order_id = (int) $request['order_id'];
    if ($order_id <= 0) {
        return new WP_Error('invalid_order', 'Invalid order id.', array('status' => 400));
    }

    $order = wc_get_order($order_id);
    if (!($order instanceof WC_Order)) {
        return new WP_Error('not_found', 'Order not found.', array('status' => 404));
    }

    $user_id = get_current_user_id();
    if ((int) $order->get_customer_id() !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot cancel this purchase.', array('status' => 403));
    }

    if (!in_array((string) $order->get_status(), array('pending', 'failed', 'on-hold'), true)) {
        return new WP_Error('cannot_cancel', 'This order cannot be cancelled at its current status.', array('status' => 409));
    }

    $order->update_status('cancelled', 'Order cancelled by customer via API.');

    return new WP_REST_Response(array(
        'ok' => true,
        'item' => stakmv_rest_serialize_purchase_order($order),
    ));
}

function stakmv_rest_buying_purchase_tracking($request) {
    if (!function_exists('wc_get_order')) {
        return new WP_Error('missing_dependency', 'WooCommerce orders are unavailable.', array('status' => 501));
    }

    $order_id = (int) $request['order_id'];
    if ($order_id <= 0) {
        return new WP_Error('invalid_order', 'Invalid order id.', array('status' => 400));
    }

    $order = wc_get_order($order_id);
    if (!($order instanceof WC_Order)) {
        return new WP_Error('not_found', 'Order not found.', array('status' => 404));
    }

    $user_id = get_current_user_id();
    if ((int) $order->get_customer_id() !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot view tracking for this purchase.', array('status' => 403));
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'order_id' => (int) $order->get_id(),
        'tracking' => stakmv_rest_get_purchase_tracking_data($order),
    ));
}

function stakmv_rest_vendor_profile_public($request) {
    $vendor_id = (int) $request['id'];
    if ($vendor_id <= 0 || !get_userdata($vendor_id)) {
        return new WP_Error('not_found', 'Vendor not found.', array('status' => 404));
    }

    if (function_exists('stakmv_is_vendor') && !stakmv_is_vendor($vendor_id)) {
        return new WP_Error('not_vendor', 'User is not a vendor.', array('status' => 404));
    }

    $user = get_userdata($vendor_id);
    $rating_summary = function_exists('stakmv_get_vendor_rating_summary') ? stakmv_get_vendor_rating_summary($vendor_id) : array('average' => 0, 'count' => 0);

    return new WP_REST_Response(array(
        'ok' => true,
        'vendor' => array(
            'id' => $vendor_id,
            'name' => function_exists('stakmv_get_vendor_display_name') ? stakmv_get_vendor_display_name($vendor_id) : (string) $user->display_name,
            'avatar' => get_avatar_url($vendor_id),
            'profile_url' => function_exists('stakmv_get_vendor_profile_url') ? stakmv_get_vendor_profile_url($vendor_id) : '',
            'rating_average' => (float) ($rating_summary['average'] ?? 0),
            'rating_count' => (int) ($rating_summary['count'] ?? 0),
            'active_listings' => (int) count((array) get_posts(array(
                'post_type' => 'product',
                'post_status' => 'publish',
                'author' => $vendor_id,
                'numberposts' => -1,
                'fields' => 'ids',
            ))),
        ),
    ));
}

function stakmv_rest_vendor_shop_public($request) {
    return stakmv_rest_vendor_products_public($request, 'publish');
}

function stakmv_rest_vendor_sold_public($request) {
    return stakmv_rest_vendor_products_public($request, 'sold');
}

function stakmv_rest_vendor_products_public($request, $mode = 'publish') {
    $vendor_id = (int) $request['id'];
    if ($vendor_id <= 0 || !get_userdata($vendor_id)) {
        return new WP_Error('not_found', 'Vendor not found.', array('status' => 404));
    }

    $args = array(
        'post_type' => 'product',
        'post_status' => 'publish',
        'author' => $vendor_id,
        'numberposts' => 50,
        'orderby' => 'date',
        'order' => 'DESC',
        'fields' => 'ids',
    );
    if ($mode === 'sold') {
        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Intentional sold-status filter for vendor profile tab.
        $args['meta_key'] = '_stakmv_auction_status';
        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Intentional sold-status filter for vendor profile tab.
        $args['meta_value'] = 'sold';
    }

    $posts = get_posts($args);

    $items = array();
    foreach ((array) $posts as $pid) {
        $p = wc_get_product((int) $pid);
        if (!($p instanceof WC_Product)) continue;
        $items[] = stakmv_rest_listing_payload($p);
    }

    return new WP_REST_Response(array('ok' => true, 'items' => $items));
}

function stakmv_rest_notifications_index($request) {
    $user_id = get_current_user_id();
    $items = get_user_meta($user_id, 'stakmv_notifications', true);
    $items = is_array($items) ? $items : array();

    return new WP_REST_Response(array('ok' => true, 'items' => array_values($items)));
}

function stakmv_rest_notifications_read($request) {
    $user_id = get_current_user_id();
    $id = sanitize_text_field((string) $request['id']);
    $items = get_user_meta($user_id, 'stakmv_notifications', true);
    $items = is_array($items) ? $items : array();

    foreach ($items as &$item) {
        if ((string) ($item['id'] ?? '') === $id) {
            $item['read'] = true;
            $item['read_at'] = current_time('mysql');
        }
    }

    update_user_meta($user_id, 'stakmv_notifications', $items);
    return new WP_REST_Response(array('ok' => true));
}

function stakmv_rest_notifications_read_all($request) {
    $user_id = get_current_user_id();
    $items = get_user_meta($user_id, 'stakmv_notifications', true);
    $items = is_array($items) ? $items : array();

    foreach ($items as &$item) {
        $item['read'] = true;
        $item['read_at'] = current_time('mysql');
    }

    update_user_meta($user_id, 'stakmv_notifications', $items);
    return new WP_REST_Response(array('ok' => true));
}

function stakmv_rest_media_reorder($request) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }
    if (!stakmv_rest_can_manage_listings($user_id)) {
        return new WP_Error('forbidden', 'You are not allowed to reorder listing media.', array('status' => 403));
    }

    $payload = stakmv_rest_get_payload($request);
    $listing_id = (int) ($payload['listing_id'] ?? 0);
    if ($listing_id <= 0) {
        return new WP_Error('invalid_listing', 'Listing id is required.', array('status' => 400));
    }

    $post = get_post($listing_id);
    if (!$post || $post->post_type !== 'product') {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }
    if ((int) $post->post_author !== $user_id && !stakmv_rest_is_admin_user()) {
        return new WP_Error('forbidden', 'You cannot update this listing.', array('status' => 403));
    }

    $product = wc_get_product($listing_id);
    if (!($product instanceof WC_Product)) {
        return new WP_Error('invalid_listing', 'Invalid listing.', array('status' => 404));
    }

    if (array_key_exists('featured_image_id', $payload)) {
        $featured_id = (int) $payload['featured_image_id'];
        $product->set_image_id($featured_id > 0 ? $featured_id : 0);
    }

    if (!empty($payload['gallery_image_ids']) && is_array($payload['gallery_image_ids'])) {
        $gallery_ids = array_values(array_filter(array_map('intval', $payload['gallery_image_ids'])));
        $product->set_gallery_image_ids($gallery_ids);
    }

    $product->save();

    return new WP_REST_Response(array(
        'ok' => true,
        'listing_id' => $listing_id,
        'featured_image_id' => (int) $product->get_image_id(),
        'gallery_image_ids' => array_values(array_map('intval', (array) $product->get_gallery_image_ids())),
    ));
}

function stakmv_rest_shipping_providers($request) {
    return new WP_REST_Response(array(
        'ok' => true,
        'providers' => array(
            array('key' => 'easypost', 'label' => 'EasyPost'),
            array('key' => 'shippo', 'label' => 'Shippo'),
        ),
        'enabled' => function_exists('stakmv_live_shipping_is_enabled') ? (bool) stakmv_live_shipping_is_enabled() : false,
        'selected' => function_exists('stakmv_live_shipping_get_provider') ? (string) stakmv_live_shipping_get_provider() : 'easypost',
    ));
}

function stakmv_rest_shipping_rates_quote($request) {
    $payload = stakmv_rest_get_payload($request);
    $listing_id = (int) ($payload['listing_id'] ?? $payload['product_id'] ?? 0);
    if ($listing_id <= 0) {
        return new WP_Error('invalid_listing', 'Listing id is required.', array('status' => 400));
    }

    $product = wc_get_product($listing_id);
    if (!($product instanceof WC_Product)) {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }

    $shipping_mode = function_exists('stakmv_get_listing_shipping_mode') ? stakmv_get_listing_shipping_mode($listing_id) : 'calculated';
    if ($shipping_mode === 'free') {
        return new WP_REST_Response(array('ok' => true, 'mode' => 'free', 'rates' => array(array('label' => 'Free Shipping', 'amount' => 0, 'currency' => get_woocommerce_currency()))));
    }
    if ($shipping_mode === 'pickup_only') {
        return new WP_REST_Response(array('ok' => true, 'mode' => 'pickup_only', 'rates' => array(array('label' => 'Pickup Only', 'amount' => 0, 'currency' => get_woocommerce_currency()))));
    }
    if ($shipping_mode === 'flat') {
        $cost = (float) get_post_meta($listing_id, '_stakmv_auction_shipping_cost', true);
        return new WP_REST_Response(array('ok' => true, 'mode' => 'flat', 'rates' => array(array('label' => 'Shipping', 'amount' => $cost, 'currency' => get_woocommerce_currency()))));
    }

    if (!function_exists('stakmv_live_shipping_is_enabled') || !stakmv_live_shipping_is_enabled()) {
        return new WP_Error('live_shipping_disabled', 'Live shipping is disabled.', array('status' => 409));
    }

    $destination = isset($payload['destination']) && is_array($payload['destination']) ? $payload['destination'] : array();
    $package = array(
        'contents' => array(
            array(
                'data' => $product,
                'product_id' => $listing_id,
                'quantity' => max(1, (int) ($payload['quantity'] ?? 1)),
            ),
        ),
        'destination' => array(
            'address_1' => sanitize_text_field((string) ($destination['address_1'] ?? '')),
            'address_2' => sanitize_text_field((string) ($destination['address_2'] ?? '')),
            'city' => sanitize_text_field((string) ($destination['city'] ?? '')),
            'state' => sanitize_text_field((string) ($destination['state'] ?? '')),
            'postcode' => sanitize_text_field((string) ($destination['postcode'] ?? '')),
            'country' => sanitize_text_field((string) ($destination['country'] ?? '')),
        ),
    );

    if (!function_exists('stakmv_live_shipping_build_package_profile')) {
        return new WP_Error('missing_dependency', 'Live shipping provider not available.', array('status' => 500));
    }

    $profile = stakmv_live_shipping_build_package_profile($package);
    if (empty($profile) || empty($profile['eligible'])) {
        return new WP_Error('not_eligible', 'Listing is not eligible for live shipping.', array('status' => 409));
    }

    $provider = stakmv_live_shipping_get_provider();
    $allowed = stakmv_live_shipping_get_allowed_carriers_for_package($profile);
    $rates = stakmv_live_shipping_fetch_rates($provider, $package, $allowed);
    if (is_wp_error($rates)) {
        return $rates;
    }

    return new WP_REST_Response(array(
        'ok' => true,
        'mode' => 'calculated',
        'provider' => $provider,
        'rates' => $rates,
    ));
}

function stakmv_rest_create_buy_now_order($auction_id, $user_id) {
    $auction_id = (int) $auction_id;
    $user_id = (int) $user_id;

    if ($auction_id <= 0) {
        return new WP_Error('invalid_id', 'Invalid listing id.', array('status' => 400));
    }

    $product = wc_get_product($auction_id);
    if (!($product instanceof WC_Product) || $product->get_type() !== 'auction') {
        return new WP_Error('invalid_auction', 'Auction not found.', array('status' => 404));
    }

    if ((string) get_post_meta($auction_id, '_stakmv_auction_status', true) === 'sold') {
        return new WP_Error('already_sold', 'This item has already been sold.', array('status' => 409));
    }

    $buy_now_price = (float) get_post_meta($auction_id, '_stakmv_auction_buy_now_price', true);
    if ($buy_now_price <= 0) {
        return new WP_Error('no_buy_now', 'Buy Now is not available for this listing.', array('status' => 409));
    }

    $available_quantity = stakmv_rest_get_listing_available_quantity($auction_id);
    if ($available_quantity !== PHP_INT_MAX && $available_quantity < 1) {
        return new WP_Error('out_of_stock', 'This listing is out of stock.', array('status' => 409));
    }

    $order = wc_create_order(array('customer_id' => $user_id));
    if (is_wp_error($order) || !($order instanceof WC_Order)) {
        return new WP_Error('order_create_failed', 'Could not create order.', array('status' => 500));
    }

    $order->add_product($product, 1, array('subtotal' => $buy_now_price, 'total' => $buy_now_price));

    $shipping_mode = function_exists('stakmv_get_listing_shipping_mode') ? stakmv_get_listing_shipping_mode($auction_id) : 'calculated';
    if ($shipping_mode === 'flat') {
        $shipping_cost = (float) get_post_meta($auction_id, '_stakmv_auction_shipping_cost', true);
        if ($shipping_cost > 0) {
            $order->add_fee(array('name' => 'Shipping', 'amount' => $shipping_cost));
        }
    }

    $order->update_meta_data('_stakmv_auction_id', $auction_id);
    $order->update_meta_data('_buy_now_used', true);
    $order->set_status('pending', 'Buy Now order created via API');
    $order->calculate_totals();
    $order->save();

    $pay_url = add_query_arg(
        array('key' => $order->get_order_key(), 'pay_for_order' => 'true'),
        wc_get_endpoint_url('order-pay', $order->get_id(), wc_get_checkout_url())
    );

    return array(
        'ok' => true,
        'order_id' => (int) $order->get_id(),
        'auction_id' => $auction_id,
        'pay_url' => $pay_url,
    );
}

function stakmv_rest_auction_buy_now($request) {
    $result = stakmv_rest_create_buy_now_order((int) $request['id'], get_current_user_id());
    if (is_wp_error($result)) {
        return $result;
    }

    return new WP_REST_Response($result);
}

function stakmv_rest_vendor_profile_update($request) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }
    if (!current_user_can('edit_user', $user_id)) {
        return new WP_Error('forbidden', 'You cannot update this profile.', array('status' => 403));
    }

    $payload = stakmv_rest_get_payload($request);

    // Store editable public profile fields in plugin-owned user meta.
    $profile_fields = array(
        'display_name'          => array('user_meta' => 'stakmv_display_name', 'sanitize' => 'sanitize_text_field'),
        'website'               => array('user_meta' => 'stakmv_website', 'sanitize' => 'esc_url_raw'),
        'bio'                   => array('user_meta' => 'stakmv_bio', 'sanitize' => 'sanitize_textarea_field'),
        'location'              => array('user_meta' => 'stakmv_location', 'sanitize' => 'sanitize_text_field'),
        'instagram'             => array('user_meta' => 'stakmv_instagram', 'sanitize' => 'sanitize_text_field'),
        'twitter'               => array('user_meta' => 'stakmv_twitter', 'sanitize' => 'sanitize_text_field'),
        'ship_origin_address_1' => array('user_meta' => 'stakmv_ship_origin_address_1', 'sanitize' => 'sanitize_text_field'),
        'ship_origin_address_2' => array('user_meta' => 'stakmv_ship_origin_address_2', 'sanitize' => 'sanitize_text_field'),
        'ship_origin_city'      => array('user_meta' => 'stakmv_ship_origin_city', 'sanitize' => 'sanitize_text_field'),
        'ship_origin_state'     => array('user_meta' => 'stakmv_ship_origin_state', 'sanitize' => 'sanitize_text_field'),
        'ship_origin_postcode'  => array('user_meta' => 'stakmv_ship_origin_postcode', 'sanitize' => 'sanitize_text_field'),
        'ship_origin_country'   => array('user_meta' => 'stakmv_ship_origin_country', 'sanitize' => 'sanitize_text_field'),
    );
    foreach ($profile_fields as $payload_key => $field) {
        if (!array_key_exists($payload_key, $payload)) {
            continue;
        }
        $sanitize = (string) $field['sanitize'];
        update_user_meta($user_id, (string) $field['user_meta'], $sanitize((string) $payload[$payload_key]));
    }

    return new WP_REST_Response(array('ok' => true));
}

function stakmv_rest_vendor_profile_about_update($request) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }

    $payload = stakmv_rest_get_payload($request);
    $about = (string) ($payload['about'] ?? $payload['content'] ?? '');
    update_user_meta($user_id, 'stakmv_public_about', wp_kses_post($about));

    return new WP_REST_Response(array('ok' => true));
}

function stakmv_rest_vendor_profile_banner_update($request) {
    $user_id = get_current_user_id();
    if ($user_id <= 0) {
        return new WP_Error('not_authenticated', 'Not authenticated.', array('status' => 401));
    }

    $payload = stakmv_rest_get_payload($request);
    $urls = array();

    if (!empty($payload['slide_ids']) && is_array($payload['slide_ids'])) {
        foreach ($payload['slide_ids'] as $id) {
            $url = wp_get_attachment_url((int) $id);
            if ($url) {
                $urls[] = esc_url_raw($url);
            }
            if (count($urls) >= 5) {
                break;
            }
        }
    } elseif (!empty($payload['slide_urls']) && is_array($payload['slide_urls'])) {
        foreach ($payload['slide_urls'] as $url) {
            $clean = esc_url_raw((string) $url);
            if ($clean !== '') {
                $urls[] = $clean;
            }
            if (count($urls) >= 5) {
                break;
            }
        }
    }

    update_user_meta($user_id, 'stakmv_banner_slide_urls', $urls);
    return new WP_REST_Response(array('ok' => true, 'slides' => $urls));
}

function stakmv_rest_shipping_listing_details($request) {
    $id = (int) $request['id'];
    $product = wc_get_product($id);
    if (!($product instanceof WC_Product)) {
        return new WP_Error('not_found', 'Listing not found.', array('status' => 404));
    }

    $shipping_mode = function_exists('stakmv_get_listing_shipping_mode') ? stakmv_get_listing_shipping_mode($id) : 'calculated';

    return new WP_REST_Response(array(
        'ok' => true,
        'listing_id' => $id,
        'mode' => $shipping_mode,
        'cost' => (float) get_post_meta($id, '_stakmv_auction_shipping_cost', true),
        'location' => sanitize_text_field((string) get_post_meta($id, '_stakmv_auction_location', true)),
        'handling_days' => (int) get_post_meta($id, '_stakmv_auction_handling_time_days', true),
        'returns_policy' => sanitize_key((string) get_post_meta($id, '_stakmv_auction_returns_policy', true)),
    ));
}

function stakmv_rest_listing_payload($product, $with_meta = false) {
    $id = (int) $product->get_id();
    $author_id = (int) get_post_field('post_author', $id);
    $thumb_id = $product->get_image_id();
    $resolved_type = (string) $product->get_type();
    if ($resolved_type !== 'auction' && has_term('auction', 'product_type', $id)) {
        $resolved_type = 'auction';
    }

    $featured_url = $thumb_id ? wp_get_attachment_image_url($thumb_id, 'woocommerce_thumbnail') : wc_placeholder_img_src('woocommerce_thumbnail');
    $gallery_ids = array_values(array_map('intval', (array) $product->get_gallery_image_ids()));
    $gallery_urls = array();
    foreach ($gallery_ids as $gallery_id) {
        $gallery_url = wp_get_attachment_image_url($gallery_id, 'woocommerce_thumbnail');
        if ($gallery_url) {
            $gallery_urls[] = $gallery_url;
        }
    }
    $image_urls = array();
    if ($featured_url) {
        $image_urls[] = $featured_url;
    }
    foreach ($gallery_urls as $gallery_url) {
        if (!in_array($gallery_url, $image_urls, true)) {
            $image_urls[] = $gallery_url;
        }
    }

    $payload = array(
        'id' => $id,
        'title' => (string) $product->get_name(),
        'status' => (string) get_post_status($id),
        'permalink' => get_permalink($id),
        'thumbnail' => $featured_url,
        'type' => $resolved_type,
        'listing_type' => (string) get_post_meta($id, '_stakmv_auction_listing_type', true),
        'sale_mode' => (string) get_post_meta($id, '_stakmv_auction_sale_mode', true),
        'start_price' => (float) get_post_meta($id, '_stakmv_auction_start_price', true),
        'buy_now_price' => (float) get_post_meta($id, '_stakmv_auction_buy_now_price', true),
        'quantity' => (int) max(1, (int) ($product->get_stock_quantity() ?? 1)),
        'stock_status' => (string) $product->get_stock_status(),
        'current_bid' => function_exists('stakmv_get_highest_bid') ? (float) stakmv_get_highest_bid($id) : 0.0,
        'bid_count' => function_exists('stakmv_get_bid_count') ? (int) stakmv_get_bid_count($id) : 0,
        'auction_status' => (string) get_post_meta($id, '_stakmv_auction_status', true),
        'auction_end_time' => (string) get_post_meta($id, '_stakmv_auction_end_time', true),
        'author' => array(
            'id' => $author_id,
            'name' => function_exists('stakmv_get_vendor_display_name') ? stakmv_get_vendor_display_name($author_id) : get_the_author_meta('display_name', $author_id),
            'profile_url' => function_exists('stakmv_get_vendor_profile_url') ? stakmv_get_vendor_profile_url($author_id) : '',
        ),
        'featured_image_id' => (int) $thumb_id,
        'gallery_image_ids' => $gallery_ids,
    );

    if ($with_meta) {
        $condition_raw = trim((string) get_post_meta($id, '_stakmv_auction_condition', true));
        $condition_label = '';
        $condition_detail = '';
        if ($condition_raw !== '' && function_exists('stakmv_get_condition_options')) {
            $condition_options = stakmv_get_condition_options();
            if (isset($condition_options[$condition_raw]) && is_array($condition_options[$condition_raw])) {
                $condition_label = sanitize_text_field((string) ($condition_options[$condition_raw]['label'] ?? ''));
                $condition_detail = sanitize_text_field((string) ($condition_options[$condition_raw]['detail'] ?? ''));
            }
        }
        if ($condition_label === '' && $condition_raw !== '') {
            $condition_label = sanitize_text_field(ucwords(str_replace(array('_', '-'), ' ', $condition_raw)));
        }
        if ($condition_detail === '' && $condition_raw !== '' && preg_match('/\s*[—-]\s*/u', $condition_raw)) {
            $parts = preg_split('/\s*[—-]\s*/u', $condition_raw, 2);
            if (isset($parts[1])) {
                $condition_detail = sanitize_text_field((string) $parts[1]);
            }
        }

        $shipping_mode = function_exists('stakmv_get_listing_shipping_mode') ? stakmv_get_listing_shipping_mode($id) : 'calculated';
        $shipping_scope = function_exists('stakmv_get_listing_shipping_scope') ? stakmv_get_listing_shipping_scope($id) : 'international';
        $shipping_countries = array_values(array_map('strval', (array) get_post_meta($id, '_stakmv_auction_shipping_countries', true)));
        $shipping_excluded_countries = array_values(array_map('strval', (array) get_post_meta($id, '_stakmv_auction_shipping_excluded_countries', true)));
        $shipping_carriers = get_post_meta($id, '_stakmv_auction_shipping_carriers', true);
        if (!is_array($shipping_carriers)) {
            $shipping_carriers = array();
        }
        $shipping_carriers = array_values(array_filter(array_map('sanitize_text_field', $shipping_carriers)));
        if (empty($shipping_carriers)) {
            $single_carrier = sanitize_text_field((string) get_post_meta($id, '_stakmv_auction_shipping_carrier', true));
            if ($single_carrier !== '') {
                $shipping_carriers[] = $single_carrier;
            }
        }

        $returns_policy = sanitize_key((string) get_post_meta($id, '_stakmv_auction_returns_policy', true));
        if (in_array($returns_policy, array('returns_14_days', 'returns_30_days'), true)) {
            $returns_policy = 'returns_accepted';
        }
        if (!in_array($returns_policy, array('no_returns', 'returns_accepted'), true)) {
            $returns_policy = 'no_returns';
        }
        $returns_days = (int) get_post_meta($id, '_stakmv_auction_returns_days', true);
        if ($returns_days < 1) {
            $returns_days = 30;
        }
        if ($returns_days > 60) {
            $returns_days = 60;
        }
        $return_shipping_paid_by = sanitize_key((string) get_post_meta($id, '_stakmv_auction_return_shipping_paid_by', true));
        if (!in_array($return_shipping_paid_by, array('buyer', 'seller'), true)) {
            $return_shipping_paid_by = 'buyer';
        }

        $rating_summary = function_exists('stakmv_get_vendor_rating_summary') ? stakmv_get_vendor_rating_summary($author_id) : array('average' => 0, 'count' => 0);
        $rating_count = (int) ($rating_summary['count'] ?? 0);
        $positive_feedback = 0.0;
        if ($rating_count > 0) {
            $stars = isset($rating_summary['stars']) && is_array($rating_summary['stars']) ? $rating_summary['stars'] : array();
            $positive_count = (int) ($stars[4] ?? 0) + (int) ($stars[5] ?? 0);
            if ($positive_count > 0) {
                $positive_feedback = ($positive_count / $rating_count) * 100;
            }
        }

        $seller_profile_url = function_exists('stakmv_get_vendor_profile_url') ? stakmv_get_vendor_profile_url($author_id) : '';
        $seller_items_url = $seller_profile_url ? add_query_arg('section', 'shop', $seller_profile_url) : '';

        $payload['description'] = (string) $product->get_description();
        $payload['short_description'] = (string) $product->get_short_description();
        $payload['manage_stock'] = (bool) $product->managing_stock();
        $payload['available_quantity'] = stakmv_rest_get_listing_available_quantity($id);
        $payload['weight'] = (string) $product->get_weight();
        $payload['length'] = (string) $product->get_length();
        $payload['width'] = (string) $product->get_width();
        $payload['height'] = (string) $product->get_height();
        $payload['dimensions'] = array(
            'length' => (string) $product->get_length(),
            'width' => (string) $product->get_width(),
            'height' => (string) $product->get_height(),
        );
        $payload['condition'] = $condition_raw;
        $payload['condition_label'] = $condition_label;
        $payload['condition_detail'] = $condition_detail;
        $payload['auction_end_time'] = (string) get_post_meta($id, '_stakmv_auction_end_time', true);
        $payload['end_date'] = (string) get_post_meta($id, '_stakmv_auction_end_time', true);
        $payload['shipping_mode'] = $shipping_mode;
        $payload['shipping_cost'] = (float) get_post_meta($id, '_stakmv_auction_shipping_cost', true);
        $payload['location'] = sanitize_text_field((string) get_post_meta($id, '_stakmv_auction_location', true));
        $payload['handling_time_days'] = (int) get_post_meta($id, '_stakmv_auction_handling_time_days', true);
        $payload['shipping_scope'] = $shipping_scope;
        $payload['shipping_countries'] = $shipping_countries;
        $payload['shipping_excluded_countries'] = $shipping_excluded_countries;
        $payload['shipping_carriers'] = $shipping_carriers;
        $payload['returns_policy'] = $returns_policy;
        $payload['returns_days'] = $returns_days;
        $payload['return_shipping_paid_by'] = $return_shipping_paid_by;
        $payload['gallery_image_urls'] = $gallery_urls;
        $payload['image_urls'] = $image_urls;
        $payload['author']['avatar'] = get_avatar_url($author_id);
        $payload['author']['rating_average'] = (float) ($rating_summary['average'] ?? 0);
        $payload['author']['rating_count'] = $rating_count;
        $payload['author']['rating_positive_percent'] = $positive_feedback;
        $payload['author']['other_items_url'] = $seller_items_url;
        $payload['shipping'] = array(
            'mode' => $shipping_mode,
            'cost' => (float) get_post_meta($id, '_stakmv_auction_shipping_cost', true),
            'scope' => $shipping_scope,
            'countries' => $shipping_countries,
            'excluded_countries' => $shipping_excluded_countries,
            'location' => sanitize_text_field((string) get_post_meta($id, '_stakmv_auction_location', true)),
            'handling_days' => (int) get_post_meta($id, '_stakmv_auction_handling_time_days', true),
            'carriers' => $shipping_carriers,
        );
        $payload['returns'] = array(
            'policy' => $returns_policy,
            'days' => $returns_days,
            'paid_by' => $return_shipping_paid_by,
        );

        $faq_rows = array();
        $saved_faqs = get_post_meta($id, '_stakmv_listing_faqs', true);
        if (is_array($saved_faqs)) {
            foreach ($saved_faqs as $faq_row) {
                if (!is_array($faq_row)) {
                    continue;
                }
                $faq_q = sanitize_text_field((string) ($faq_row['q'] ?? ''));
                $faq_a = sanitize_textarea_field((string) ($faq_row['a'] ?? ''));
                if ($faq_q === '' && $faq_a === '') {
                    continue;
                }
                $faq_rows[] = array(
                    'q' => $faq_q,
                    'a' => $faq_a,
                );
            }
        }
        $payload['faq'] = $faq_rows;

        $review_items = array();
        $review_comments = get_comments(array(
            'post_id' => $id,
            'status' => 'approve',
            'type' => 'review',
            'number' => 5,
            'orderby' => 'comment_date_gmt',
            'order' => 'DESC',
        ));
        foreach ((array) $review_comments as $comment) {
            if (!($comment instanceof WP_Comment)) {
                continue;
            }
            $review_items[] = array(
                'author' => sanitize_text_field((string) $comment->comment_author),
                'content' => sanitize_textarea_field((string) $comment->comment_content),
                'date' => mysql2date('c', (string) $comment->comment_date_gmt, false),
                'rating' => (int) get_comment_meta($comment->comment_ID, 'rating', true),
            );
        }
        $payload['reviews'] = array(
            'count' => (int) $product->get_review_count(),
            'average' => (float) $product->get_average_rating(),
            'items' => $review_items,
        );
    }

    return apply_filters('stakmv_rest_listing_payload', $payload, $product, (bool) $with_meta);
}
