<?php

/**
 * Order-related functionality for Wuclean plugin.
 *
 * @package Wuclean
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
/**
 * Clear cached orphaned order data.
 */
function pt_wuclean_clear_orphaned_order_data() {
    delete_transient( 'pt_wuclean_orphaned_order_data' );
    delete_option( 'pt_wuclean_orphaned_order_data' );
}

/**
 * Localized strings for Orders admin JavaScript (scan/redirect messages, confirmations).
 *
 * @return array<string, string> Keys match what wp_localize_script expects for the orders screen.
 */
function pt_wuclean_get_orders_script_strings() {
    /* translators: %d: number of newly flagged orders */
    $scan_completed_flagged = __( 'Scan completed! %d new order(s) flagged. Redirecting to Flagged Orders tab...', 'wuclean-database-cleaner-for-woocommerce' );
    /* translators: %d: number of previously flagged orders */
    $scan_completed_already_flagged = __( 'Scan completed! No new flags. %d order(s) already flagged.', 'wuclean-database-cleaner-for-woocommerce' );
    /* translators: %d: count of orphaned items found */
    $orphaned_scan_found = __( 'Search completed! Found %d orphaned item(s). Redirecting to Orphaned Order Data tab...', 'wuclean-database-cleaner-for-woocommerce' );
    return array(
        'scan_completed_flagged'            => $scan_completed_flagged,
        'scan_completed_already_flagged'    => $scan_completed_already_flagged,
        'scan_completed_none'               => __( 'Scan completed! No orders match the current flagging criteria.', 'wuclean-database-cleaner-for-woocommerce' ),
        'scan_completed_no_new_redirecting' => __( 'Scan completed! No new orders match the current flagging criteria. Redirecting to show existing flagged orders...', 'wuclean-database-cleaner-for-woocommerce' ),
        'orphaned_scan_found'               => $orphaned_scan_found,
        'orphaned_scan_none'                => __( 'Search completed! No orphaned order metadata found.', 'wuclean-database-cleaner-for-woocommerce' ),
        'confirm_unflag_all'                => __( 'This will unflag all currently flagged orders. They may be flagged again when the cleanup routine runs. Continue?', 'wuclean-database-cleaner-for-woocommerce' ),
    );
}

/**
 * Renders the Orders admin page (Settings, Flagged Orders, Orphaned Order Data, Order Logs).
 *
 * @return void
 */
function pt_wuclean_render_orders_page() {
    // Defense-in-depth: the admin menu is capability-gated, but verify again before reading request input.
    if ( !current_user_can( pt_wuclean_get_required_capability() ) ) {
        wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wuclean-database-cleaner-for-woocommerce' ) );
    }
    // Tab query arg: settings is the default; other tabs require a valid tab nonce (see nav links).
    $current_tab = 'settings';
    $valid_tabs = array(
        'settings',
        'flagged',
        'protected',
        'orphaned_data',
        'logs'
    );
    if ( isset( $_GET['tab'] ) ) {
        $tab = sanitize_text_field( wp_unslash( $_GET['tab'] ) );
        if ( !in_array( $tab, $valid_tabs, true ) ) {
            $current_tab = 'settings';
        } elseif ( 'settings' === $tab ) {
            $current_tab = 'settings';
        } elseif ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'wuclean-orders-tab' ) ) {
            $current_tab = $tab;
        }
    }
    // Check HPOS status - flagged orders tab requires HPOS.
    $hpos_enabled = pt_wuclean_is_hpos_enabled();
    // Redirect to settings if trying to access HPOS-dependent tabs without HPOS.
    if ( ('flagged' === $current_tab || 'protected' === $current_tab || 'orphaned_data' === $current_tab) && !$hpos_enabled ) {
        $current_tab = 'settings';
    }
    echo '<div class="wrap">';
    echo '<h1>🛒 ' . esc_html__( 'Orders', 'wuclean-database-cleaner-for-woocommerce' ) . '</h1>';
    // Tab navigation (conditionally show HPOS-dependent tabs).
    echo '<nav class="nav-tab-wrapper">';
    $settings_url = add_query_arg( array(
        'page'     => 'wuclean-orders',
        'tab'      => 'settings',
        '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
    ), admin_url( 'admin.php' ) );
    echo '<a href="' . esc_url( $settings_url ) . '" class="nav-tab' . (( 'settings' === $current_tab ? ' nav-tab-active' : '' )) . '">' . esc_html__( 'Settings', 'wuclean-database-cleaner-for-woocommerce' ) . '</a>';
    // Only show Flagged Orders tab if HPOS is enabled.
    if ( $hpos_enabled ) {
        $flagged_url = add_query_arg( array(
            'page'     => 'wuclean-orders',
            'tab'      => 'flagged',
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        ), admin_url( 'admin.php' ) );
        echo '<a href="' . esc_url( $flagged_url ) . '" class="nav-tab' . (( 'flagged' === $current_tab ? ' nav-tab-active' : '' )) . '">' . esc_html__( 'Flagged Orders', 'wuclean-database-cleaner-for-woocommerce' ) . '</a>';
        $protected_url = add_query_arg( array(
            'page'     => 'wuclean-orders',
            'tab'      => 'protected',
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        ), admin_url( 'admin.php' ) );
        echo '<a href="' . esc_url( $protected_url ) . '" class="nav-tab' . (( 'protected' === $current_tab ? ' nav-tab-active' : '' )) . '">' . esc_html__( 'Protected Orders', 'wuclean-database-cleaner-for-woocommerce' ) . '</a>';
    }
    // Only show Orphaned Order Data tab if HPOS is enabled.
    if ( $hpos_enabled ) {
        $orphaned_url = add_query_arg( array(
            'page'     => 'wuclean-orders',
            'tab'      => 'orphaned_data',
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        ), admin_url( 'admin.php' ) );
        echo '<a href="' . esc_url( $orphaned_url ) . '" class="nav-tab' . (( 'orphaned_data' === $current_tab ? ' nav-tab-active' : '' )) . '">' . esc_html__( 'Orphaned Order Data', 'wuclean-database-cleaner-for-woocommerce' ) . '</a>';
    }
    $logs_url = add_query_arg( array(
        'page'     => 'wuclean-orders',
        'tab'      => 'logs',
        '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
    ), admin_url( 'admin.php' ) );
    echo '<a href="' . esc_url( $logs_url ) . '" class="nav-tab' . (( 'logs' === $current_tab ? ' nav-tab-active' : '' )) . '">' . esc_html__( 'Order Logs', 'wuclean-database-cleaner-for-woocommerce' ) . '</a>';
    echo '</nav>';
    // Standard inner wrapper for all tabs (max width + side padding).
    echo '<div class="pt-wuclean-page-inner" style="max-width: 1600px; box-sizing: border-box;">';
    if ( 'settings' === $current_tab ) {
        pt_wuclean_render_orders_settings_tab();
    } elseif ( 'flagged' === $current_tab && $hpos_enabled ) {
        pt_wuclean_render_flagged_orders_tab();
    } elseif ( 'protected' === $current_tab && $hpos_enabled ) {
        pt_wuclean_render_flagged_orders_tab();
    } elseif ( 'orphaned_data' === $current_tab && $hpos_enabled ) {
        pt_wuclean_render_orphaned_order_data_tab();
    } elseif ( 'logs' === $current_tab ) {
        pt_wuclean_render_order_logs_tab();
    }
    echo '</div>';
    // Close inner wrapper.
    echo '</div>';
}

/**
 * Render orders settings tab
 */
function pt_wuclean_render_orders_settings_tab() {
    if ( !current_user_can( pt_wuclean_get_required_capability() ) ) {
        wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wuclean-database-cleaner-for-woocommerce' ) );
    }
    // Check HPOS status - required for order cleanup.
    $hpos_enabled = pt_wuclean_is_hpos_enabled();
    // Handle form submission (only if HPOS is enabled).
    if ( $hpos_enabled && isset( $_POST['pt_wuclean_order_settings_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pt_wuclean_order_settings_nonce'] ) ), 'pt_wuclean_save_order_settings' ) ) {
        $settings = get_option( 'pt_wuclean_settings', array() );
        // Flag settings - save enable/disable state AND days value separately.
        $settings['enable_trash_orphaned_orders'] = ( !empty( $_POST['enable_trash_orphaned_orders'] ) ? 1 : 0 );
        $settings['trash_orphaned_orders_days'] = max( 1, intval( $_POST['trash_orphaned_orders'] ?? 365 ) );
        $settings['enable_trash_guest_orders'] = ( !empty( $_POST['enable_trash_guest_orders'] ) ? 1 : 0 );
        $settings['trash_guest_orders_days'] = max( 1, intval( $_POST['trash_guest_orders'] ?? 365 ) );
        $settings['enable_trash_all_orders'] = ( !empty( $_POST['enable_trash_all_orders'] ) ? 1 : 0 );
        $settings['trash_all_orders_days'] = max( 1, intval( $_POST['trash_all_orders_days'] ?? 730 ) );
        // Default values are set below when loading settings for display.
        update_option( 'pt_wuclean_settings', $settings, false );
        echo '<div class="notice notice-success"><p>' . esc_html__( 'Order cleanup settings saved successfully!', 'wuclean-database-cleaner-for-woocommerce' ) . '</p></div>';
    }
    $settings = get_option( 'pt_wuclean_settings', array() );
    // If HPOS not enabled, clear any existing scheduled events.
    if ( !$hpos_enabled ) {
        // Clear any existing scheduled events.
        wp_clear_scheduled_hook( 'pt_wuclean_flag_orders_event' );
    }
    // Load enable/disable flags separately from days values.
    $enable_trash_orphaned_orders = $settings['enable_trash_orphaned_orders'] ?? 0;
    $trash_orphaned_orders_days = $settings['trash_orphaned_orders_days'] ?? 365;
    $enable_trash_guest_orders = $settings['enable_trash_guest_orders'] ?? 0;
    $trash_guest_orders_days = $settings['trash_guest_orders_days'] ?? 365;
    $enable_trash_all_orders = $settings['enable_trash_all_orders'] ?? 0;
    $trash_all_orders_days = $settings['trash_all_orders_days'] ?? 730;
    // Order log limit.
    $order_log_limit = $settings['order_log_limit'] ?? 100;
    echo '<div style="margin-top:2em; max-width: 1600px;">';
    echo '<h2>⚙️ ' . esc_html__( 'Order Cleanup Settings', 'wuclean-database-cleaner-for-woocommerce' ) . '</h2>';
    // Info box.
    echo '<div style="max-width: 1600px;background: #e7f3ff; border: 1px solid #2196f3; border-radius: 4px; padding: 15px; margin: 20px 0;">';
    if ( pt_wuclean_fs()->is_not_paying() ) {
        // Free version: Show basic feature description.
        echo '<p style="margin: 0 0 10px 0;"><strong>ℹ️ ' . esc_html__( 'About Order Cleanup:', 'wuclean-database-cleaner-for-woocommerce' ) . '</strong> ' . esc_html__( 'Flag orphaned orders, guest orders and any orders based on their age. You can then manually review, trash and delete these orders. You can protect specific users (via user profile) from having their orders flagged. Automate this and also cleanup coupons and order notes with Wuclean Pro👑', 'wuclean-database-cleaner-for-woocommerce' ) . '</p>';
    }
    echo '</div>';
    echo '<form method="post" class="wuclean">';
    wp_nonce_field( 'pt_wuclean_save_order_settings', 'pt_wuclean_order_settings_nonce' );
    // Settings card.
    $disabled_attr = ( !$hpos_enabled ? ' disabled' : '' );
    $disabled_style = ( !$hpos_enabled ? ' opacity: 0.5; pointer-events: none;' : '' );
    echo '<div style="background: #fff; border: 1px solid #ccd0d4; border-radius: 4px; padding: 20px; margin: 20px 0; max-width: 1600px;' . esc_attr( $disabled_style ) . '">';
    echo '<h3 style="margin-top: 0;">📋 ' . esc_html__( 'Shared Scan Settings (Manual + Scheduled)', 'wuclean-database-cleaner-for-woocommerce' ) . '</h3>';
    echo '<table class="form-table" style="table-layout: fixed;"><tbody>';
    // Flag orphaned orders.
    echo '<tr><th scope="row" style="width: 300px;">' . esc_html__( 'Flag orphaned orders', 'wuclean-database-cleaner-for-woocommerce' ) . ' <span class="pt-wuclean-tooltip">?<span class="pt-wuclean-tooltip-text">' . esc_html__( 'Flag orders missing user and billing information, and orders linked to non-existent users.', 'wuclean-database-cleaner-for-woocommerce' ) . '</span></span></th><td>';
    echo '<input type="checkbox" name="enable_trash_orphaned_orders" value="1"' . checked( $enable_trash_orphaned_orders, 1, false ) . esc_attr( $disabled_attr ) . '> ';
    echo '<input type="number" name="trash_orphaned_orders" min="1" max="3650" value="' . esc_attr( $trash_orphaned_orders_days ) . '" style="width: 70px;"' . esc_attr( $disabled_attr ) . '> ' . esc_html__( 'days old', 'wuclean-database-cleaner-for-woocommerce' );
    echo '</td></tr>';
    // Flag guest orders.
    echo '<tr><th scope="row" style="width: 300px;">' . esc_html__( 'Flag guest orders', 'wuclean-database-cleaner-for-woocommerce' ) . ' <span class="pt-wuclean-tooltip">?<span class="pt-wuclean-tooltip-text">' . esc_html__( 'Flag guest orders after the specified number of days.', 'wuclean-database-cleaner-for-woocommerce' ) . '</span></span></th><td>';
    echo '<input type="checkbox" name="enable_trash_guest_orders" value="1"' . checked( $enable_trash_guest_orders, 1, false ) . esc_attr( $disabled_attr ) . '> ';
    echo '<input type="number" name="trash_guest_orders" min="1" max="3650" value="' . esc_attr( $trash_guest_orders_days ) . '" style="width: 70px;"' . esc_attr( $disabled_attr ) . '> ' . esc_html__( 'days old', 'wuclean-database-cleaner-for-woocommerce' );
    echo '</td></tr>';
    // Flag all orders.
    echo '<tr><th scope="row" style="width: 300px;">' . esc_html__( 'Flag all orders', 'wuclean-database-cleaner-for-woocommerce' ) . ' <span class="pt-wuclean-tooltip">?<span class="pt-wuclean-tooltip-text">' . esc_html__( 'Flag all orders after the specified number of days.', 'wuclean-database-cleaner-for-woocommerce' ) . '</span></span></th><td>';
    echo '<input type="checkbox" name="enable_trash_all_orders" value="1"' . checked( $enable_trash_all_orders, 1, false ) . esc_attr( $disabled_attr ) . '> ';
    echo '<input type="number" name="trash_all_orders_days" min="1" max="3650" value="' . esc_attr( $trash_all_orders_days ) . '" style="width: 70px;"' . esc_attr( $disabled_attr ) . '> ' . esc_html__( 'days old', 'wuclean-database-cleaner-for-woocommerce' );
    echo '</td></tr>';
    echo '</tbody></table>';
    echo '<p style="margin-bottom: 0;">';
    if ( $hpos_enabled ) {
        echo '<input type="submit" class="button button-primary" value="' . esc_attr__( 'Save Settings', 'wuclean-database-cleaner-for-woocommerce' ) . '">';
    } else {
        echo '<input type="submit" class="button button-primary" value="' . esc_attr__( 'Save Settings', 'wuclean-database-cleaner-for-woocommerce' ) . '" disabled title="' . esc_attr__( 'HPOS required - see warning above', 'wuclean-database-cleaner-for-woocommerce' ) . '">';
    }
    echo '</p>';
    echo '</div>';
    // Close settings card.
    echo '</form>';
    // Manual Scans section (single card with multiple scan buttons).
    echo '<div style="background: #fff; border: 1px solid #ccd0d4; border-radius: 4px; padding: 20px; margin: 20px 0; max-width: 1600px;' . esc_attr( $disabled_style ) . '">';
    echo '<h3 style="margin-top: 0;">🔍 ' . esc_html__( 'Manual Scans', 'wuclean-database-cleaner-for-woocommerce' ) . '</h3>';
    // Order Flagging Scan.
    echo '<div style="margin-bottom:1.5em;">';
    echo '<h4 style="margin-bottom:0.5em;">📦 ' . esc_html__( 'Order Flagging Scan', 'wuclean-database-cleaner-for-woocommerce' ) . '</h4>';
    echo '<p style="margin: 0.5em 0 1em 0; color: #666;">' . esc_html__( 'The three flag settings saved above will be used for this scan.', 'wuclean-database-cleaner-for-woocommerce' ) . '</p>';
    if ( $hpos_enabled ) {
        echo '<button type="button" id="pt-start-order-scan" class="button button-primary" style="background: #2196f3; border-color: #2196f3;">🔍 ' . esc_html__( 'Flag Orders for Review', 'wuclean-database-cleaner-for-woocommerce' ) . '</button>';
    } else {
        echo '<button type="button" class="button button-primary" style="background: #ccc; border-color: #ccc; cursor: not-allowed;" disabled title="' . esc_attr__( 'HPOS required - see warning above', 'wuclean-database-cleaner-for-woocommerce' ) . '">🔍 ' . esc_html__( 'Flag Orders for Review', 'wuclean-database-cleaner-for-woocommerce' ) . ' (' . esc_html__( 'Disabled', 'wuclean-database-cleaner-for-woocommerce' ) . ')</button>';
    }
    echo '<button type="button" id="pt-stop-order-scan" class="button" style="display: none; margin-left: 10px; background: #dc3232; border-color: #dc3232; color: #fff;">⏹️ ' . esc_html__( 'Stop Scan', 'wuclean-database-cleaner-for-woocommerce' ) . '</button>';
    echo '<div id="pt-order-scan-progress" style="display: none; margin-top: 15px;">';
    echo '<div style="background: #f0f0f0; border: 1px solid #ddd; border-radius: 4px; padding: 15px;">';
    echo '<div style="margin-bottom: 8px;"><strong>' . esc_html__( 'Scan Progress:', 'wuclean-database-cleaner-for-woocommerce' ) . '</strong></div>';
    echo '<div style="background: #fff; border: 1px solid #ddd; border-radius: 2px; height: 25px; position: relative;">';
    echo '<div id="pt-order-progress-bar" style="background: #2196f3; height: 100%; width: 0%; transition: width 0.3s ease; border-radius: 2px;"></div>';
    echo '</div>';
    echo '<div id="pt-order-progress-text" style="margin-top: 8px; font-size: 13px; color: #666;">' . esc_html__( 'Initializing...', 'wuclean-database-cleaner-for-woocommerce' ) . '</div>';
    echo '</div>';
    echo '</div>';
    echo '</div>';
    // Orphaned Order Meta Scan.
    echo '<div style="margin-bottom:0;">';
    echo '<h4 style="margin-bottom:0.5em;">🗑️ ' . esc_html__( 'Orphaned Order Meta Scan', 'wuclean-database-cleaner-for-woocommerce' ) . '</h4>';
    if ( $hpos_enabled ) {
        echo '<button type="button" id="pt-start-orphaned-meta-scan" class="button button-primary" style="background: #2196f3; border-color: #2196f3;">🔍 ' . esc_html__( 'Search for Orphaned Order Meta', 'wuclean-database-cleaner-for-woocommerce' ) . '</button>';
    } else {
        echo '<button type="button" class="button button-primary" style="background: #ccc; border-color: #ccc; cursor: not-allowed;" disabled title="' . esc_attr__( 'HPOS required - see warning above', 'wuclean-database-cleaner-for-woocommerce' ) . '">🔍 ' . esc_html__( 'Search for Orphaned Order Meta', 'wuclean-database-cleaner-for-woocommerce' ) . ' (' . esc_html__( 'Disabled', 'wuclean-database-cleaner-for-woocommerce' ) . ')</button>';
    }
    echo '<button type="button" id="pt-stop-orphaned-meta-scan" class="button" style="display: none; margin-left: 10px; background: #dc3232; border-color: #dc3232; color: #fff;">⏹️ ' . esc_html__( 'Stop Scan', 'wuclean-database-cleaner-for-woocommerce' ) . '</button>';
    echo '<div id="pt-orphaned-meta-scan-progress" style="display: none; margin-top: 15px;">';
    echo '<div style="background: #f0f0f0; border: 1px solid #ddd; border-radius: 4px; padding: 15px;">';
    echo '<div style="margin-bottom: 8px;"><strong>' . esc_html__( 'Scan Progress:', 'wuclean-database-cleaner-for-woocommerce' ) . '</strong></div>';
    echo '<div style="background: #fff; border: 1px solid #ddd; border-radius: 2px; height: 25px; position: relative;">';
    echo '<div id="pt-orphaned-meta-progress-bar" style="background: #2196f3; height: 100%; width: 0%; transition: width 0.3s ease; border-radius: 2px;"></div>';
    echo '</div>';
    echo '<div id="pt-orphaned-meta-progress-text" style="margin-top: 8px; font-size: 13px; color: #666;">' . esc_html__( 'Initializing...', 'wuclean-database-cleaner-for-woocommerce' ) . '</div>';
    echo '</div>';
    echo '</div>';
    echo '</div>';
    echo '</div>';
    // Close manual scans card.
    // Free version: progressive disclosure for automation benefits (Settings tab only).
    $pro_benefits = array(
        __( 'Schedule automatic order cleanup every few minutes, daily, weekly or monthly and receive email notifications summarising key actions', 'wuclean-database-cleaner-for-woocommerce' ),
        __( 'Automatically detect and delete orphaned order meta data', 'wuclean-database-cleaner-for-woocommerce' ),
        __( 'Automatically trash flagged orders after 24 hours and automatically delete trashed orders 24 hours later', 'wuclean-database-cleaner-for-woocommerce' ),
        __( 'Automatically trash expired coupons after 24 hours and automatically delete trashed coupons', 'wuclean-database-cleaner-for-woocommerce' ),
        __( 'Automatically delete unnecessary order notes sooner without deleting orders', 'wuclean-database-cleaner-for-woocommerce' ),
        __( 'Control cleanup batch size and frequency to keep you orders database clean with minimal server impact', 'wuclean-database-cleaner-for-woocommerce' )
    );
    pt_wuclean_render_pro_automation_callout( $pro_benefits, 'orders-settings' );
    // Enqueue JavaScript files for settings tab (Order scan functionality).
    wp_enqueue_script(
        'pt-wuclean-common',
        plugin_dir_url( __FILE__ ) . 'assets/js/common.js',
        array('jquery'),
        '1.0.0',
        true
    );
    wp_enqueue_script(
        'pt-wuclean-orders',
        plugin_dir_url( __FILE__ ) . 'assets/js/orders.js',
        array('jquery', 'pt-wuclean-common'),
        '1.0.0',
        true
    );
    wp_localize_script( 'pt-wuclean-common', 'pt_wuclean_common_vars', pt_wuclean_get_common_script_strings() );
    // Localize script with nonce and other variables (settings tab).
    wp_localize_script( 'pt-wuclean-orders', 'pt_wuclean_orders_vars', array_merge( array(
        'order_search_nonce'         => wp_create_nonce( 'pt_wuclean_order_search' ),
        'orphaned_meta_search_nonce' => wp_create_nonce( 'pt_wuclean_orphaned_meta_search' ),
        'flagged_orders_url'         => add_query_arg( array(
            'page'     => 'wuclean-orders',
            'tab'      => 'flagged',
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        ), admin_url( 'admin.php' ) ),
        'orphaned_data_url'          => add_query_arg( array(
            'page'     => 'wuclean-orders',
            'tab'      => 'orphaned_data',
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        ), admin_url( 'admin.php' ) ),
        'ajaxurl'                    => admin_url( 'admin-ajax.php' ),
    ), pt_wuclean_get_orders_script_strings() ) );
    echo '</div>';
}

/**
 * Renders the Flagged Orders tab.
 *
 * @return void
 */
function pt_wuclean_render_flagged_orders_tab() {
    $settings = get_option( 'pt_wuclean_settings', array() );
    // Validate tab nonce before processing any request parameters for this view.
    $orders_tab_nonce_valid = isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'wuclean-orders-tab' );
    // Check if we're showing protected orders for a specific user.
    if ( $orders_tab_nonce_valid ) {
        $show_protected = isset( $_GET['show_protected'] ) && '1' === sanitize_text_field( wp_unslash( $_GET['show_protected'] ) );
        $current_tab = ( isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : '' );
        if ( 'protected' === $current_tab ) {
            $show_protected = true;
        }
        $filter_user_id = ( isset( $_GET['user_id'] ) ? intval( $_GET['user_id'] ) : 0 );
    } else {
        $show_protected = false;
        $filter_user_id = 0;
    }
    // Show header based on what we're displaying.
    if ( $show_protected && $filter_user_id ) {
        $user = get_user_by( 'id', $filter_user_id );
        $user_name = ( $user ? $user->user_login : __( 'Unknown User', 'wuclean-database-cleaner-for-woocommerce' ) );
        // translators: %s: username.
        echo '<h2>' . sprintf( esc_html__( 'Protected Orders for User: %s', 'wuclean-database-cleaner-for-woocommerce' ), esc_html( $user_name ) ) . '</h2>';
    } elseif ( $show_protected ) {
        echo '<h2>' . esc_html__( 'Protected Orders', 'wuclean-database-cleaner-for-woocommerce' ) . '</h2>';
    }
    // Handle form submission for unflagging all orders (only for flagged orders, not protected orders).
    if ( isset( $_POST['unflag_all_orders'] ) && check_admin_referer( 'pt_wuclean_flagged_orders', 'pt_wuclean_flagged_orders_nonce' ) && !($show_protected && $filter_user_id) ) {
        // Use direct SQL DELETE for efficiency - this is a universal reset.
        global $wpdb;
        $orders_meta_table = pt_wuclean_safe_table_name( 'wc_orders_meta' );
        if ( '' === $orders_meta_table ) {
            return;
        }
        // Delete all flagging metadata from HPOS tables (wc_orders_meta).
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Bulk unflag: intentional delete by meta_key; admin POST with nonce.
        $deleted_flagged = $wpdb->delete( $orders_meta_table, array(
            'meta_key' => '_pt_wuclean_flagged_at',
        ) );
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Bulk unflag: intentional delete by meta_key; admin POST with nonce.
        $deleted_reason = $wpdb->delete( $orders_meta_table, array(
            'meta_key' => '_pt_wuclean_flag_reason',
        ) );
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Bulk unflag: intentional delete by meta_key; admin POST with nonce.
        $deleted_marked = $wpdb->delete( $orders_meta_table, array(
            'meta_key' => '_pt_wuclean_marked_for_trash',
        ) );
        // CRITICAL: Also delete from legacy wp_postmeta table to prevent HPOS backfill from restoring old values.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Bulk unflag: intentional delete by meta_key; admin POST with nonce.
        $wpdb->delete( $wpdb->postmeta, array(
            'meta_key' => '_pt_wuclean_flagged_at',
        ) );
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Bulk unflag: intentional delete by meta_key; admin POST with nonce.
        $wpdb->delete( $wpdb->postmeta, array(
            'meta_key' => '_pt_wuclean_flag_reason',
        ) );
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Bulk unflag: intentional delete by meta_key; admin POST with nonce.
        $wpdb->delete( $wpdb->postmeta, array(
            'meta_key' => '_pt_wuclean_marked_for_trash',
        ) );
        // Count of orders unflagged (use flagged_at count as primary metric).
        $unflagged_count = $deleted_flagged;
        // Add a single summary log entry for performance (instead of logging each order individually).
        if ( $unflagged_count > 0 ) {
            $settings = get_option( 'pt_wuclean_settings', array() );
            $logs = get_option( 'pt_wuclean_order_logs', array() );
            $log_limit = $settings['order_log_limit'] ?? 100;
            if ( $log_limit > 0 ) {
                /* translators: %d: number of orders. */
                $unflagged_state_msg = sprintf( __( 'Unflagged %d orders (bulk reset)', 'wuclean-database-cleaner-for-woocommerce' ), $unflagged_count );
                $logs[] = array(
                    'order_id'  => 0,
                    'date'      => '-',
                    'number'    => 'Bulk Action',
                    'value'     => '-',
                    'name'      => '-',
                    'state'     => $unflagged_state_msg,
                    'timestamp' => current_time( 'mysql' ),
                );
                // Prune logs to limit.
                if ( count( $logs ) > $log_limit ) {
                    $logs = array_slice( $logs, -$log_limit );
                }
                update_option( 'pt_wuclean_order_logs', $logs, false );
            }
            // Redirect to refresh the page and show updated state.
            if ( !headers_sent() ) {
                $redirect_url = add_query_arg( array(
                    'page'            => 'wuclean-orders',
                    'tab'             => 'flagged',
                    'unflagged_count' => $unflagged_count,
                    '_wpnonce'        => wp_create_nonce( 'wuclean-orders-tab' ),
                ), admin_url( 'admin.php' ) );
                wp_safe_redirect( $redirect_url );
                exit;
            } else {
                // Fallback: show message and add JavaScript refresh.
                wp_enqueue_script( 'jquery' );
                $reload_script = 'setTimeout(function() { window.location.reload(); }, 1000);';
                wp_add_inline_script( 'jquery', $reload_script );
                echo '<div class="notice notice-info"><p>' . esc_html__( 'Page will refresh in 1 second to show updated data.', 'wuclean-database-cleaner-for-woocommerce' ) . '</p></div>';
            }
        } else {
            echo '<div class="notice notice-warning"><p>' . sprintf( esc_html__( 'No flagged orders found to unflag.', 'wuclean-database-cleaner-for-woocommerce' ) ) . '</p></div>';
        }
    }
    // Show success message if orders were unflagged.
    if ( $orders_tab_nonce_valid && isset( $_GET['unflagged_count'] ) && is_numeric( $_GET['unflagged_count'] ) ) {
        $unflagged_count = intval( $_GET['unflagged_count'] );
        if ( $unflagged_count > 0 ) {
            // translators: %d: number of metadata entries.
            echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( sprintf( esc_html__( '%d metadata entries have been removed. All flagged orders have been reset and may be flagged again when the cleanup routine runs.', 'wuclean-database-cleaner-for-woocommerce' ), $unflagged_count ) ) . '</p></div>';
        }
    }
    // Search and pagination setup..
    $search_query = '';
    if ( $orders_tab_nonce_valid && isset( $_GET['flagged_order_search'] ) ) {
        $search_query = sanitize_text_field( wp_unslash( $_GET['flagged_order_search'] ) );
    }
    $displayed_orders = array();
    if ( $show_protected && $filter_user_id ) {
        // Show protected orders for specific user.
        $protected_orders = pt_wuclean_get_user_protected_orders( $filter_user_id );
        foreach ( $protected_orders as $protected_data ) {
            $order_id = $protected_data['order_id'];
            $order = wc_get_order( $order_id );
            if ( !$order ) {
                continue;
            }
            $order_date = ( $order->get_date_created() ? $order->get_date_created()->date_i18n( get_option( 'date_format' ) ) : '' );
            $order_number = $order->get_order_number();
            $order_status = wc_get_order_status_name( $order->get_status() );
            $order_value = $order->get_currency() . ' ' . number_format( (float) $order->get_total(), 2 );
            $user_id = $order->get_user_id();
            $user_name = $order->get_formatted_billing_full_name();
            $user_edit_link = ( $user_id > 0 ? get_edit_user_link( $user_id ) : false );
            $user_link = ( $user_id > 0 && $user_edit_link && get_userdata( $user_id ) ? '<a href="' . esc_url( $user_edit_link ) . '">' . esc_html( $user_name ) . '</a>' : esc_html( $user_name ) );
            $order_link = '<a href="' . esc_url( $order->get_edit_order_url() ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $order_number ) . '</a>';
            // Determine protection reason with link to user profile if user-level protected.
            $protection_reasons = array();
            if ( $protected_data['individually_protected'] ) {
                $protection_reasons[] = esc_html__( 'Individually Protected', 'wuclean-database-cleaner-for-woocommerce' );
            }
            if ( $protected_data['user_level_protected'] ) {
                /* translators: %s: username. */
                $user_level_label = __( 'User-Level (%s)', 'wuclean-database-cleaner-for-woocommerce' );
                $user_profile_link = '<a href="' . esc_url( get_edit_user_link( $protected_data['user_id'] ) ) . '" title="' . esc_attr__( 'Edit user to change protection settings', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html( sprintf( $user_level_label, $protected_data['user_login'] ) ) . '</a>';
                $protection_reasons[] = $user_profile_link;
            }
            $flag_reason = implode( ' + ', $protection_reasons );
            // Only individually protected orders can be unchecked (user-level requires changing user settings).
            $should_check = $protected_data['individually_protected'];
            $displayed_orders[] = array(
                'order_id'     => $order_id,
                'order_date'   => $order_date,
                'order_link'   => $order_link,
                'order_status' => $order_status,
                'order_value'  => $order_value,
                'user_link'    => $user_link,
                'user_name'    => $user_name,
                'should_check' => $should_check,
                'flag_reason'  => $flag_reason,
            );
        }
    } elseif ( $show_protected ) {
        // Show all protected orders across all users.
        $all_protected_orders = pt_wuclean_get_all_protected_orders();
        foreach ( $all_protected_orders as $protected_data ) {
            $order_id = $protected_data['order_id'];
            $order = wc_get_order( $order_id );
            if ( !$order ) {
                continue;
            }
            $order_date = ( $order->get_date_created() ? $order->get_date_created()->date_i18n( get_option( 'date_format' ) ) : '' );
            $order_number = $order->get_order_number();
            $order_status = wc_get_order_status_name( $order->get_status() );
            $order_value = $order->get_currency() . ' ' . number_format( (float) $order->get_total(), 2 );
            $user_id = $order->get_user_id();
            $user_name = $order->get_formatted_billing_full_name();
            $user_edit_link = ( $user_id > 0 ? get_edit_user_link( $user_id ) : false );
            $user_link = ( $user_id > 0 && $user_edit_link && get_userdata( $user_id ) ? '<a href="' . esc_url( $user_edit_link ) . '">' . esc_html( $user_name ) . '</a>' : esc_html( $user_name ) );
            $order_link = '<a href="' . esc_url( $order->get_edit_order_url() ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $order_number ) . '</a>';
            // Determine protection reason with link to user profile if user-level protected.
            $protection_reasons = array();
            if ( $protected_data['individually_protected'] ) {
                $protection_reasons[] = esc_html__( 'Individually Protected', 'wuclean-database-cleaner-for-woocommerce' );
            }
            if ( $protected_data['user_level_protected'] ) {
                /* translators: %s: username. */
                $user_level_label = __( 'User-Level (%s)', 'wuclean-database-cleaner-for-woocommerce' );
                $user_profile_link = '<a href="' . esc_url( get_edit_user_link( $protected_data['user_id'] ) ) . '" title="' . esc_attr__( 'Edit user to change protection settings', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html( sprintf( $user_level_label, $protected_data['user_login'] ) ) . '</a>';
                $protection_reasons[] = $user_profile_link;
            }
            $flag_reason = implode( ' + ', $protection_reasons );
            // Only individually protected orders can be unchecked (user-level requires changing user settings).
            $should_check = $protected_data['individually_protected'];
            $displayed_orders[] = array(
                'order_id'     => $order_id,
                'order_date'   => $order_date,
                'order_link'   => $order_link,
                'order_status' => $order_status,
                'order_value'  => $order_value,
                'user_link'    => $user_link,
                'user_name'    => $user_name,
                'should_check' => $should_check,
                'flag_reason'  => $flag_reason,
            );
        }
    } else {
        // Show flagged orders (normal behavior).
        $all_flagged_order_ids = pt_wuclean_get_flagged_orders( 1000, time() );
        if ( !empty( $all_flagged_order_ids ) ) {
            foreach ( $all_flagged_order_ids as $order_id ) {
                $order = wc_get_order( $order_id );
                if ( !$order ) {
                    continue;
                }
                // Skip protected orders from display (SQL should have filtered them, but this is a safety check).
                if ( pt_wuclean_is_order_protected( $order_id ) ) {
                    continue;
                }
                $order_date = ( $order->get_date_created() ? $order->get_date_created()->date_i18n( get_option( 'date_format' ) ) : '' );
                $order_number = $order->get_order_number();
                $order_status = wc_get_order_status_name( $order->get_status() );
                $order_value = $order->get_currency() . ' ' . number_format( (float) $order->get_total(), 2 );
                $user_id = $order->get_user_id();
                $user_name = $order->get_formatted_billing_full_name();
                $user_edit_link = ( $user_id > 0 ? get_edit_user_link( $user_id ) : false );
                $user_link = ( $user_id > 0 && $user_edit_link && get_userdata( $user_id ) ? '<a href="' . esc_url( $user_edit_link ) . '">' . esc_html( $user_name ) . '</a>' : esc_html( $user_name ) );
                $order_link = '<a href="' . esc_url( $order->get_edit_order_url() ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $order_number ) . '</a>';
                $should_check = false;
                // Orders are unchecked by default so users can select which ones to protect.
                $flag_reason = $order->get_meta( '_pt_wuclean_flag_reason', true );
                $displayed_orders[] = array(
                    'order_id'     => $order_id,
                    'order_date'   => $order_date,
                    'order_link'   => $order_link,
                    'order_status' => $order_status,
                    'order_value'  => $order_value,
                    'user_link'    => $user_link,
                    'user_name'    => $user_name,
                    'should_check' => $should_check,
                    'flag_reason'  => $flag_reason,
                );
            }
        }
    }
    // Search filter.
    if ( '' !== $search_query ) {
        $displayed_orders = array_filter( $displayed_orders, function ( $order ) use($search_query) {
            foreach ( array(
                'order_date',
                'order_link',
                'order_status',
                'user_name',
                'flag_reason'
            ) as $field ) {
                if ( isset( $order[$field] ) && stripos( wp_strip_all_tags( (string) $order[$field] ), $search_query ) !== false ) {
                    return true;
                }
            }
            return false;
        } );
    }
    $total_orders = count( $displayed_orders );
    $per_page = 25;
    // Standard pagination size for consistency.
    // Honor page only when tab nonce is valid (same rule as flagged_order_search).
    $page = ( $orders_tab_nonce_valid && isset( $_GET['flagged_order_page'] ) ? max( 1, intval( $_GET['flagged_order_page'] ) ) : 1 );
    $total_pages = max( 1, ceil( $total_orders / $per_page ) );
    $paged_orders = array_slice( array_values( $displayed_orders ), ($page - 1) * $per_page, $per_page );
    echo '<div style="max-width: 1600px; margin-top: 2em;">';
    echo '<h2>🚩 ' . esc_html__( 'Flagged Orders', 'wuclean-database-cleaner-for-woocommerce' ) . '</h2>';
    // Explanatory info box.
    if ( $show_protected && $filter_user_id ) {
        echo '<div style="background: #e7f5e8; border: 1px solid #46b450; border-radius: 4px; padding: 15px; margin: 20px 0;">';
        echo '<p style="margin: 0;"><strong>🛡️ ' . esc_html__( 'User Level Protected Orders:', 'wuclean-database-cleaner-for-woocommerce' ) . '</strong> ' . esc_html__( '"User-Level" protection applies when "Protect all orders" has been enabled in the user profile. Change this setting in the users profile to unprotect the users orders.', 'wuclean-database-cleaner-for-woocommerce' ) . '</p>';
        echo '</div>';
    } elseif ( $show_protected ) {
        echo '<div style="background: #e7f5e8; border: 1px solid #46b450; border-radius: 4px; padding: 15px; margin: 20px 0;">';
        echo '<p style="margin: 0;"><strong>🛡️ ' . esc_html__( 'User Level Protected Orders:', 'wuclean-database-cleaner-for-woocommerce' ) . '</strong> ' . esc_html__( '"User-Level" protection applies when "Protect all orders" has been enabled in the user profile. Change this setting in the users profile to unprotect the users orders.', 'wuclean-database-cleaner-for-woocommerce' ) . '</p>';
        echo '</div>';
    } else {
        echo '<div style="background: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; padding: 15px; margin: 20px 0;">';
        if ( pt_wuclean_fs()->is_not_paying() ) {
            // Free version: Show basic feature description.
            echo '<p style="margin: 0;"><strong>ℹ️ ' . esc_html__( 'About Flagged Orders:', 'wuclean-database-cleaner-for-woocommerce' ) . '</strong> ' . esc_html__( 'Orders listed here have been flagged for trashing. Click on the order number to delete selected orders via WooCommerce. Use "Unflag All Orders" to reset the list. Automate these processes with Wuclean Pro👑', 'wuclean-database-cleaner-for-woocommerce' ) . '</p>';
        }
        echo '</div>';
    }
    // Search form. - standardized pattern.
    echo '<form method="get" action="' . esc_url( admin_url( 'admin.php' ) ) . '" style="margin-bottom: 20px;">';
    echo '<input type="hidden" name="page" value="wuclean-orders">';
    echo '<input type="hidden" name="tab" value="' . esc_attr( ( $show_protected ? 'protected' : 'flagged' ) ) . '">';
    echo '<input type="hidden" name="_wpnonce" value="' . esc_attr( wp_create_nonce( 'wuclean-orders-tab' ) ) . '">';
    if ( $show_protected && $filter_user_id ) {
        echo '<input type="hidden" name="show_protected" value="1">';
        echo '<input type="hidden" name="user_id" value="' . esc_attr( $filter_user_id ) . '">';
    } elseif ( $show_protected ) {
        echo '<input type="hidden" name="show_protected" value="1">';
    }
    $search_placeholder = esc_attr__( 'Search by Order Date, Number, Status, Customer or Reason', 'wuclean-database-cleaner-for-woocommerce' );
    echo '<input type="text" name="flagged_order_search" value="' . esc_attr( $search_query ) . '" placeholder="' . esc_attr( $search_placeholder ) . '" style="width: 400px; padding: 5px; height: 30px; box-sizing: border-box;">';
    echo '<input type="submit" value="' . esc_attr__( 'Search', 'wuclean-database-cleaner-for-woocommerce' ) . '" class="button">';
    if ( !empty( $search_query ) ) {
        $clear_args = array(
            'page'     => 'wuclean-orders',
            'tab'      => ( $show_protected ? 'protected' : 'flagged' ),
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        );
        if ( $show_protected && $filter_user_id ) {
            $clear_args['show_protected'] = '1';
            $clear_args['user_id'] = $filter_user_id;
        } elseif ( $show_protected ) {
            $clear_args['show_protected'] = '1';
        }
        $clear_url = add_query_arg( $clear_args, admin_url( 'admin.php' ) );
        echo '<a href="' . esc_url( $clear_url ) . '" class="button">' . esc_html__( 'Clear Search', 'wuclean-database-cleaner-for-woocommerce' ) . '</a>';
    }
    echo '</form>';
    // Pagination info..
    if ( $total_orders > 0 ) {
        echo '<div style="margin-bottom: 10px;">';
        if ( $show_protected && $filter_user_id ) {
            if ( !empty( $search_query ) ) {
                $filtered_message = sprintf(
                    /* translators: %1$d: start number, %2$d: end number, %3$d: total count. */
                    esc_html__( 'Showing %1$d-%2$d of %3$d protected orders (filtered by search)', 'wuclean-database-cleaner-for-woocommerce' ),
                    ($page - 1) * $per_page + 1,
                    min( $page * $per_page, $total_orders ),
                    $total_orders
                );
                echo '<span>' . esc_html( $filtered_message ) . '</span>';
            } else {
                $orders_message = sprintf(
                    /* translators: %1$d: start number, %2$d: end number, %3$d: total count. */
                    esc_html__( 'Showing %1$d-%2$d of %3$d protected orders', 'wuclean-database-cleaner-for-woocommerce' ),
                    ($page - 1) * $per_page + 1,
                    min( $page * $per_page, $total_orders ),
                    $total_orders
                );
                echo '<span>' . esc_html( $orders_message ) . '</span>';
            }
        } elseif ( $show_protected ) {
            if ( !empty( $search_query ) ) {
                $filtered_message = sprintf(
                    /* translators: %1$d: start number, %2$d: end number, %3$d: total count. */
                    esc_html__( 'Showing %1$d-%2$d of %3$d protected orders (filtered by search)', 'wuclean-database-cleaner-for-woocommerce' ),
                    ($page - 1) * $per_page + 1,
                    min( $page * $per_page, $total_orders ),
                    $total_orders
                );
                echo '<span>' . esc_html( $filtered_message ) . '</span>';
            } else {
                $orders_message = sprintf(
                    /* translators: %1$d: start number, %2$d: end number, %3$d: total count. */
                    esc_html__( 'Showing %1$d-%2$d of %3$d protected orders', 'wuclean-database-cleaner-for-woocommerce' ),
                    ($page - 1) * $per_page + 1,
                    min( $page * $per_page, $total_orders ),
                    $total_orders
                );
                echo '<span>' . esc_html( $orders_message ) . '</span>';
            }
        } elseif ( !empty( $search_query ) ) {
            $filtered_message = sprintf(
                /* translators: %1$d: start number, %2$d: end number, %3$d: total count. */
                esc_html__( 'Showing %1$d-%2$d of %3$d orders (filtered by search)', 'wuclean-database-cleaner-for-woocommerce' ),
                ($page - 1) * $per_page + 1,
                min( $page * $per_page, $total_orders ),
                $total_orders
            );
            echo '<span>' . esc_html( $filtered_message ) . '</span>';
        } else {
            $orders_message = sprintf(
                /* translators: %1$d: start number, %2$d: end number, %3$d: total count. */
                esc_html__( 'Showing %1$d-%2$d of %3$d orders', 'wuclean-database-cleaner-for-woocommerce' ),
                ($page - 1) * $per_page + 1,
                min( $page * $per_page, $total_orders ),
                $total_orders
            );
            echo '<span>' . esc_html( $orders_message ) . '</span>';
        }
        echo '</div>';
    } else {
        // Show message when no orders exist.
        echo '<div style="margin-bottom: 10px;">';
        if ( $show_protected && $filter_user_id ) {
            // translators: Message when no protected orders exist for the selected user.
            echo '<span>' . esc_html__( 'No protected orders found for this user.', 'wuclean-database-cleaner-for-woocommerce' ) . '</span>';
        } elseif ( $show_protected ) {
            // translators: Message when no protected orders exist across all users.
            echo '<span>' . esc_html__( 'No protected orders found across all users.', 'wuclean-database-cleaner-for-woocommerce' ) . '</span>';
        } else {
            // translators: Message when no flagged orders exist.
            echo '<span>' . esc_html__( 'No flagged orders found.', 'wuclean-database-cleaner-for-woocommerce' ) . '</span>';
        }
        echo '</div>';
    }
    echo '<form method="post" action="">';
    wp_nonce_field( 'pt_wuclean_flagged_orders', 'pt_wuclean_flagged_orders_nonce' );
    // Bulk action buttons - standardized pattern (always show interface).
    if ( $show_protected && $filter_user_id ) {
        // Show buttons for protected orders for specific user.
        echo '<div style="margin-bottom: 20px;">';
        echo '<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">';
        echo '</div>';
        echo '</div>';
    } elseif ( $show_protected ) {
        // Show buttons for all protected orders.
        echo '<div style="margin-bottom: 20px;">';
        echo '<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">';
        echo '</div>';
        echo '</div>';
    } else {
        // Show normal buttons for flagged orders.
        echo '<div style="margin-bottom: 20px;">';
        echo '<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">';
        echo '<button type="submit" name="unflag_all_orders" value="1" class="button button-secondary" style="background: #f7f7f7; border-color: #ccc; color: #555;">' . esc_html__( 'Unflag All Orders', 'wuclean-database-cleaner-for-woocommerce' ) . '</button>';
        echo '<span id="flagged-selected-count" style="margin-left: 10px; color: #666;"></span>';
        echo '</div>';
        echo '</div>';
    }
    // Main table - updated styling.
    echo '<table class="wp-list-table widefat striped" style="margin-top: 20px; width: 100%; table-layout: auto;">';
    echo '<thead>';
    echo '<tr>';
    echo '<th style="text-align: left; min-width: 110px;" title="' . esc_attr__( 'The date the order was created', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Order Date', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="text-align: left; min-width: 110px;" title="' . esc_attr__( 'Order number (click to edit order)', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Order Number', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="text-align: left; min-width: 110px;" title="' . esc_attr__( 'Order status (e.g., Processing, Completed)', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Order Status', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="text-align: left; min-width: 110px;" title="' . esc_attr__( 'Order value including currency', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Order Value', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="text-align: left; min-width: 180px;" title="' . esc_attr__( 'User\'s billing name (click to edit user)', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Customer Name', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    if ( $show_protected && $filter_user_id ) {
        echo '<th style="text-align: left; min-width: 220px;" title="' . esc_attr__( 'How this order is protected - individually or user-level (click username to edit)', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Protection Type', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    } elseif ( $show_protected ) {
        echo '<th style="text-align: left; min-width: 220px;" title="' . esc_attr__( 'How this order is protected - individually or user-level (click username to edit)', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Protection Type', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    } else {
        echo '<th style="text-align: left; min-width: 150px;" title="' . esc_attr__( 'Reason why this order was flagged for cleanup', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Reason', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    }
    echo '</tr>';
    echo '</thead>';
    echo '<tbody>';
    if ( !empty( $paged_orders ) ) {
        $row_index = 0;
        foreach ( $paged_orders as $order_data ) {
            $bg_color = ( 0 === $row_index % 2 ? '#ffffff' : '#f9f9f9' );
            echo '<tr style="background-color: ' . esc_attr( $bg_color ) . '; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor=\'#e8f4f8\'" onmouseout="this.style.backgroundColor=\'' . esc_attr( $bg_color ) . '\'">';
            ++$row_index;
            echo '<td style="text-align: left;">' . esc_html( $order_data['order_date'] ) . '</td>';
            // $order_data['order_link'] is generated internally using esc_url()/esc_html() (including target/rel attributes).
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output is pre-escaped.
            echo '<td style="text-align: left;">' . $order_data['order_link'] . '</td>';
            echo '<td style="text-align: left;">' . esc_html( $order_data['order_status'] ) . '</td>';
            echo '<td style="text-align: left;">' . esc_html( $order_data['order_value'] ) . '</td>';
            echo '<td style="text-align: left;">' . wp_kses_post( $order_data['user_link'] ) . '</td>';
            echo '<td style="text-align: left;">' . wp_kses_post( $order_data['flag_reason'] ?? '' ) . '</td>';
            echo '</tr>';
        }
    } elseif ( $show_protected && $filter_user_id ) {
        // Show appropriate empty message based on context.
        $colspan = 6;
        echo '<tr><td colspan="' . esc_attr( $colspan ) . '" style="text-align: center; padding: 20px;">' . esc_html__( 'This user has no protected orders (individually or user-level).', 'wuclean-database-cleaner-for-woocommerce' ) . '</td></tr>';
    } elseif ( $show_protected ) {
        $colspan = 6;
        echo '<tr><td colspan="' . esc_attr( $colspan ) . '" style="text-align: center; padding: 20px;">' . esc_html__( 'No protected orders found (individually or user-level).', 'wuclean-database-cleaner-for-woocommerce' ) . '</td></tr>';
    } else {
        $colspan = 6;
        echo '<tr><td colspan="' . esc_attr( $colspan ) . '" style="text-align: center; padding: 20px;">' . esc_html__( 'No orders are currently flagged for trashing. Any new orders flagged by the cleanup routine will be listed here in future.', 'wuclean-database-cleaner-for-woocommerce' ) . '</td></tr>';
    }
    echo '</tbody>';
    echo '</table>';
    // Pagination controls - updated styling.
    // Pagination - using centralized function..
    $preserve_params = array(
        'page'     => 'wuclean-orders',
        'tab'      => ( $show_protected ? 'protected' : 'flagged' ),
        '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
    );
    if ( !empty( $search_query ) ) {
        $preserve_params['flagged_order_search'] = $search_query;
    }
    if ( $show_protected && $filter_user_id ) {
        $preserve_params['show_protected'] = '1';
        $preserve_params['user_id'] = $filter_user_id;
    } elseif ( $show_protected ) {
        $preserve_params['show_protected'] = '1';
    }
    pt_wuclean_render_pagination( array(
        'current_page'    => $page,
        'total_pages'     => $total_pages,
        'base_url'        => admin_url( 'admin.php' ),
        'page_param'      => 'flagged_order_page',
        'preserve_params' => $preserve_params,
    ) );
    echo '</form>';
    // Enqueue JavaScript files for flagged orders tab.
    wp_enqueue_script(
        'pt-wuclean-common',
        plugin_dir_url( __FILE__ ) . 'assets/js/common.js',
        array('jquery'),
        '1.0.0',
        true
    );
    wp_enqueue_script(
        'pt-wuclean-orders',
        plugin_dir_url( __FILE__ ) . 'assets/js/orders.js',
        array('jquery', 'pt-wuclean-common'),
        '1.0.0',
        true
    );
    wp_localize_script( 'pt-wuclean-common', 'pt_wuclean_common_vars', pt_wuclean_get_common_script_strings() );
    // Localize script with nonce and other variables (flagged orders tab).
    wp_localize_script( 'pt-wuclean-orders', 'pt_wuclean_orders_vars', array_merge( array(
        'order_search_nonce'         => wp_create_nonce( 'pt_wuclean_order_search' ),
        'orphaned_meta_search_nonce' => wp_create_nonce( 'pt_wuclean_orphaned_meta_search' ),
        'flagged_orders_url'         => add_query_arg( array(
            'page'     => 'wuclean-orders',
            'tab'      => 'flagged',
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        ), admin_url( 'admin.php' ) ),
        'orphaned_data_url'          => add_query_arg( array(
            'page'     => 'wuclean-orders',
            'tab'      => 'orphaned_data',
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        ), admin_url( 'admin.php' ) ),
        'ajaxurl'                    => admin_url( 'admin-ajax.php' ),
    ), pt_wuclean_get_orders_script_strings() ) );
    echo '</div>';
}

/**
 * Render the Orphaned Order Data tab (HPOS): lists stored detection results for orphaned order meta / item meta.
 */
function pt_wuclean_render_orphaned_order_data_tab() {
    // Get orphaned data from transient or option.
    $orphaned_data = get_transient( 'pt_wuclean_orphaned_order_data' );
    if ( false === $orphaned_data ) {
        $orphaned_data = get_option( 'pt_wuclean_orphaned_order_data', array() );
    }
    // Validate tab nonce before trusting any GET-based filters for this view.
    $orders_orphaned_tab_nonce_valid = isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'wuclean-orders-tab' );
    // Handle search.
    $search_term = '';
    if ( $orders_orphaned_tab_nonce_valid && isset( $_GET['search'] ) ) {
        $search_term = sanitize_text_field( wp_unslash( $_GET['search'] ) );
    }
    $search_term_str = trim( $search_term );
    // Filter data by search.
    $filtered_data = array();
    if ( !empty( $search_term_str ) ) {
        foreach ( $orphaned_data as $item ) {
            if ( !is_array( $item ) ) {
                continue;
            }
            // Search across all fields.
            $searchable_text = implode( ' ', array(
                $item['id'] ?? '',
                $item['order_id'] ?? '',
                $item['order_item_id'] ?? '',
                $item['meta_key'] ?? '',
                $item['meta_value'] ?? '',
                $item['description'] ?? '',
                $item['type'] ?? ''
            ) );
            if ( stripos( $searchable_text, $search_term_str ) !== false ) {
                $filtered_data[] = $item;
            }
        }
    } else {
        $filtered_data = $orphaned_data;
    }
    // Pagination settings.
    $items_per_page = 25;
    $current_page = 1;
    if ( $orders_orphaned_tab_nonce_valid && isset( $_GET['paged'] ) ) {
        $current_page = max( 1, intval( $_GET['paged'] ) );
    }
    $total_items = count( $filtered_data );
    $total_pages = ceil( $total_items / $items_per_page );
    $offset = ($current_page - 1) * $items_per_page;
    // Paginate the filtered data.
    $paginated_data = array_slice( $filtered_data, $offset, $items_per_page );
    echo '<div style="max-width: 1600px; margin-top: 2em;">';
    echo '<h2>🗑️ ' . esc_html__( 'Orphaned Order Data', 'wuclean-database-cleaner-for-woocommerce' ) . '</h2>';
    if ( pt_wuclean_fs()->is_not_paying() ) {
        echo '<div style="background: #ffe5e5; border: 1px solid #dc3232; border-radius: 4px; padding: 15px; margin: 20px 0;">';
        echo '<p style="margin: 0;"><strong>⚠️ ' . esc_html__( 'Warning:', 'wuclean-database-cleaner-for-woocommerce' ) . '</strong> ' . esc_html__( 'This tab shows orphaned order metadata in HPOS tables (wc_orders_meta and woocommerce_order_itemmeta) that no longer have parent records. These items are detected using intelligent filtering to target WooCommerce-specific data only. Make backups and review carefully before deletion using your database manager or consider upgrading to Wuclean Pro👑', 'wuclean-database-cleaner-for-woocommerce' ) . '</p>';
        echo '</div>';
    }
    // Empty state.
    if ( empty( $orphaned_data ) ) {
        echo '<div style="background: #fff; border: 1px solid #ccd0d4; border-radius: 4px; padding: 20px; margin: 20px 0; text-align: center;">';
        echo '<p style="margin: 0; color: #646970;">' . esc_html__( 'No orphaned order data found.', 'wuclean-database-cleaner-for-woocommerce' ) . '</p>';
        echo '</div>';
        echo '</div>';
        return;
    }
    // Search form.
    echo '<form method="get" action="' . esc_url( admin_url( 'admin.php' ) ) . '" style="margin-bottom: 20px;">';
    echo '<input type="hidden" name="page" value="wuclean-orders">';
    echo '<input type="hidden" name="tab" value="orphaned_data">';
    echo '<input type="hidden" name="_wpnonce" value="' . esc_attr( wp_create_nonce( 'wuclean-orders-tab' ) ) . '">';
    $search_placeholder = esc_attr__( 'Search by Order ID, Meta ID, Meta Key, or Type', 'wuclean-database-cleaner-for-woocommerce' );
    echo '<input type="text" name="search" value="' . esc_attr( $search_term ) . '" placeholder="' . esc_attr( $search_placeholder ) . '" style="width: 400px; padding: 5px; height: 30px; box-sizing: border-box;">';
    echo '<input type="submit" value="' . esc_attr__( 'Search', 'wuclean-database-cleaner-for-woocommerce' ) . '" class="button">';
    if ( !empty( $search_term ) ) {
        echo '<a href="' . esc_url( add_query_arg( array(
            'page'     => 'wuclean-orders',
            'tab'      => 'orphaned_data',
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        ), admin_url( 'admin.php' ) ) ) . '" class="button">' . esc_html__( 'Clear Search', 'wuclean-database-cleaner-for-woocommerce' ) . '</a>';
    }
    echo '</form>';
    // Start the deletion form.
    echo '<form method="post" action="">';
    wp_nonce_field( 'pt_wuclean_orphaned_order_data', 'pt_wuclean_orphaned_order_data_nonce' );
    // Pagination info.
    if ( $total_items > 0 ) {
        echo '<div style="margin-bottom: 10px;">';
        if ( !empty( $search_term ) ) {
            $filtered_message = sprintf(
                /* translators: %1$d: start number, %2$d: end number, %3$d: total count. */
                esc_html__( 'Showing %1$d-%2$d of %3$d items (filtered by search)', 'wuclean-database-cleaner-for-woocommerce' ),
                $offset + 1,
                min( $offset + $items_per_page, $total_items ),
                $total_items
            );
            echo '<span>' . esc_html( $filtered_message ) . '</span>';
        } else {
            $items_message = sprintf(
                /* translators: %1$d: start number, %2$d: end number, %3$d: total count. */
                esc_html__( 'Showing %1$d-%2$d of %3$d items', 'wuclean-database-cleaner-for-woocommerce' ),
                $offset + 1,
                min( $offset + $items_per_page, $total_items ),
                $total_items
            );
            echo '<span>' . esc_html( $items_message ) . '</span>';
        }
        echo '</div>';
    }
    // Bulk action buttons.
    if ( !empty( $paginated_data ) ) {
        echo '<div style="margin-bottom: 20px;">';
        echo '<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">';
        echo '</div>';
        echo '</div>';
    }
    // Data table with alternating row colors.
    echo '<table class="wp-list-table widefat fixed striped" style="margin-top: 20px;">';
    echo '<thead>';
    echo '<tr>';
    echo '<th style="width: 80px;">' . esc_html__( 'Type', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="width: 80px;">' . esc_html__( 'ID', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="width: 80px;">' . esc_html__( 'Parent ID', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="width: 250px;">' . esc_html__( 'Meta Key', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="width: 250px;">' . esc_html__( 'Meta Value', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th>' . esc_html__( 'Description', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '</tr>';
    echo '</thead>';
    echo '<tbody>';
    $row_number = 0;
    foreach ( $paginated_data as $item ) {
        ++$row_number;
        $row_class = ( 0 === $row_number % 2 ? 'even-row' : 'odd-row' );
        echo '<tr class="' . esc_attr( $row_class ) . '" style="transition: background-color 0.2s;">';
        // Checkbox - combine type and ID for deletion.
        $checkbox_value = $item['type'] . '|' . $item['id'];
        // Type.
        $type_label = ( 'order_meta' === $item['type'] ? __( 'Order Meta', 'wuclean-database-cleaner-for-woocommerce' ) : __( 'Item Meta', 'wuclean-database-cleaner-for-woocommerce' ) );
        echo '<td>' . esc_html( $type_label ) . '</td>';
        // ID.
        echo '<td>' . esc_html( $item['id'] ?? '' ) . '</td>';
        // Parent ID.
        $parent_id = ( 'order_meta' === $item['type'] ? $item['order_id'] ?? '' : $item['order_item_id'] ?? '' );
        echo '<td>' . esc_html( $parent_id ) . '</td>';
        // Meta Key.
        echo '<td><code>' . esc_html( $item['meta_key'] ?? '' ) . '</code></td>';
        // Meta Value (truncated).
        $meta_value = $item['meta_value'] ?? '';
        $truncated_value = ( strlen( $meta_value ) > 50 ? substr( $meta_value, 0, 50 ) . '...' : $meta_value );
        echo '<td>' . esc_html( $truncated_value ) . '</td>';
        // Description.
        echo '<td>' . esc_html( $item['description'] ?? '' ) . '</td>';
        echo '</tr>';
    }
    echo '</tbody>';
    echo '</table>';
    // Pagination - using centralized function.
    $preserve_params = array(
        'page'     => 'wuclean-orders',
        'tab'      => 'orphaned_data',
        '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
    );
    if ( !empty( $search_term ) ) {
        $preserve_params['search'] = $search_term;
    }
    pt_wuclean_render_pagination( array(
        'current_page'    => $current_page,
        'total_pages'     => $total_pages,
        'base_url'        => admin_url( 'admin.php' ),
        'page_param'      => 'paged',
        'preserve_params' => $preserve_params,
    ) );
    echo '</form>';
    // Enqueue JavaScript files for orphaned data tab.
    wp_enqueue_script(
        'pt-wuclean-common',
        plugin_dir_url( __FILE__ ) . 'assets/js/common.js',
        array('jquery'),
        '1.0.0',
        true
    );
    // Add CSS for alternating row colors and hover effect.
    wp_enqueue_style(
        'pt-wuclean-admin',
        plugin_dir_url( __FILE__ ) . '../assets/css/admin.css',
        array(),
        '1.0.0'
    );
    $inline_css = '.wp-list-table .odd-row { background-color: #f9f9f9; }
        .wp-list-table .even-row { background-color: #ffffff; }
        .wp-list-table tbody tr:hover { background-color: #e8f4f8 !important; }';
    wp_add_inline_style( 'pt-wuclean-admin', $inline_css );
    echo '</div>';
}

/**
 * Render order logs tab
 */
function pt_wuclean_render_order_logs_tab() {
    // Handle clear logs form submission.
    if ( isset( $_POST['clear_order_logs'] ) && isset( $_POST['pt_wuclean_clear_logs_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pt_wuclean_clear_logs_nonce'] ) ), 'pt_wuclean_clear_order_logs' ) ) {
        $logs_count = count( get_option( 'pt_wuclean_order_logs', array() ) );
        update_option( 'pt_wuclean_order_logs', array(), false );
        // translators: %d: number of logs.
        echo '<div class="updated notice"><p>' . esc_html( sprintf( esc_html__( '%d order logs have been cleared.', 'wuclean-database-cleaner-for-woocommerce' ), $logs_count ) ) . '</p></div>';
    }
    // Pagination and search setup.
    $all_logs = get_option( 'pt_wuclean_order_logs', array() );
    $all_logs = array_reverse( $all_logs );
    // Newest first.
    $search_query = ( isset( $_GET['order_log_search'] ) ? sanitize_text_field( wp_unslash( $_GET['order_log_search'] ) ) : '' );
    echo '<div style="max-width: 1600px; margin-top: 2em;">';
    echo '<h2>📝 ' . esc_html__( 'Order Logs', 'wuclean-database-cleaner-for-woocommerce' ) . '</h2>';
    // Info box.
    echo '<div style="background: #e7f3ff; border: 1px solid #2196f3; border-radius: 4px; padding: 15px; margin: 20px 0;">';
    echo '<p style="margin: 0;"><strong>ℹ️ ' . esc_html__( 'About Order Logs:', 'wuclean-database-cleaner-for-woocommerce' ) . '</strong> ' . esc_html__( 'This log tracks all order cleanup actions.', 'wuclean-database-cleaner-for-woocommerce' ) . '</p>';
    echo '</div>';
    // Early return if no logs exist at all.
    if ( empty( $all_logs ) ) {
        echo '<div style="background: #fff; border: 1px solid #ccd0d4; border-radius: 4px; padding: 20px; margin: 20px 0; text-align: center;">';
        echo '<p style="margin: 0; color: #646970;">' . esc_html__( 'No order logs found.', 'wuclean-database-cleaner-for-woocommerce' ) . '</p>';
        echo '<p style="margin: 10px 0 0 0; color: #646970; font-size: 13px;">' . esc_html__( 'Any new order actions will be logged here.', 'wuclean-database-cleaner-for-woocommerce' ) . '</p>';
        echo '</div>';
        echo '</div>';
        return;
    }
    // Apply search filter if needed.
    $logs = $all_logs;
    if ( '' !== $search_query ) {
        $logs = array_filter( $logs, function ( $log ) use($search_query) {
            foreach ( array('date', 'number', 'name') as $field ) {
                if ( isset( $log[$field] ) && stripos( (string) $log[$field], $search_query ) !== false ) {
                    return true;
                }
            }
            return false;
        } );
    }
    $total_logs = count( $logs );
    $per_page = 25;
    // Standard pagination size for consistency.
    $page = ( isset( $_GET['order_log_page'] ) ? max( 1, intval( $_GET['order_log_page'] ) ) : 1 );
    $total_pages = max( 1, ceil( $total_logs / $per_page ) );
    $paginated_logs = array_slice( $logs, ($page - 1) * $per_page, $per_page );
    // Search form. - standardized pattern.
    echo '<form method="get" action="' . esc_url( admin_url( 'admin.php' ) ) . '" style="margin-bottom: 20px;">';
    echo '<input type="hidden" name="page" value="wuclean-orders">';
    echo '<input type="hidden" name="tab" value="logs">';
    echo '<input type="hidden" name="_wpnonce" value="' . esc_attr( wp_create_nonce( 'wuclean-orders-tab' ) ) . '">';
    $search_placeholder = esc_attr__( 'Search by Order Date, Order Number, or Customer Name', 'wuclean-database-cleaner-for-woocommerce' );
    echo '<input type="text" name="order_log_search" value="' . esc_attr( $search_query ) . '" placeholder="' . esc_attr( $search_placeholder ) . '" style="width: 400px; padding: 5px; height: 30px; box-sizing: border-box;">';
    echo '<input type="submit" value="' . esc_attr__( 'Search', 'wuclean-database-cleaner-for-woocommerce' ) . '" class="button">';
    if ( !empty( $search_query ) ) {
        echo '<a href="' . esc_url( add_query_arg( array(
            'page'     => 'wuclean-orders',
            'tab'      => 'logs',
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        ), admin_url( 'admin.php' ) ) ) . '" class="button">' . esc_html__( 'Clear Search', 'wuclean-database-cleaner-for-woocommerce' ) . '</a>';
    }
    echo '</form>';
    // Pagination info..
    if ( $total_logs > 0 ) {
        echo '<div style="margin-bottom: 10px;">';
        if ( !empty( $search_query ) ) {
            $filtered_message = sprintf(
                /* translators: %1$d: start number, %2$d: end number, %3$d: total count. */
                esc_html__( 'Showing %1$d-%2$d of %3$d logs (filtered by search)', 'wuclean-database-cleaner-for-woocommerce' ),
                ($page - 1) * $per_page + 1,
                min( $page * $per_page, $total_logs ),
                $total_logs
            );
            echo '<span>' . esc_html( $filtered_message ) . '</span>';
        } else {
            $logs_message = sprintf(
                /* translators: %1$d: start number, %2$d: end number, %3$d: total count. */
                esc_html__( 'Showing %1$d-%2$d of %3$d logs', 'wuclean-database-cleaner-for-woocommerce' ),
                ($page - 1) * $per_page + 1,
                min( $page * $per_page, $total_logs ),
                $total_logs
            );
            echo '<span>' . esc_html( $logs_message ) . '</span>';
        }
        echo '</div>';
    }
    // Clear logs button.
    if ( $total_logs > 0 ) {
        echo '<div style="margin-bottom: 20px;">';
        echo '<form method="post" style="display: inline;">';
        wp_nonce_field( 'pt_wuclean_clear_order_logs', 'pt_wuclean_clear_logs_nonce' );
        echo '<button type="submit" name="clear_order_logs" value="1" class="button button-secondary" data-confirm-message="' . esc_attr( __( 'Are you sure you want to clear all order logs? This action cannot be undone.', 'wuclean-database-cleaner-for-woocommerce' ) ) . '">' . esc_html__( 'Clear All Order Logs', 'wuclean-database-cleaner-for-woocommerce' ) . '</button>';
        echo '</form>';
        echo '</div>';
    }
    // Main table - updated styling.
    echo '<table class="wp-list-table widefat fixed striped" style="margin-top: 20px;">';
    echo '<thead>';
    echo '<tr>';
    echo '<th style="text-align: left; width: 120px;" title="' . esc_attr__( 'The date the order was created', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Order Date', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="text-align: right; width: 80px;" title="' . esc_attr__( 'Order number', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Order #', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="text-align: right; width: 80px;" title="' . esc_attr__( 'Order value', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Value', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="text-align: left;" title="' . esc_attr__( 'User\'s billing name', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Customer Name', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="text-align: left; width: 120px;" title="' . esc_attr__( 'Trashed, Deleted, or Protected', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Action', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '<th style="text-align: left; width: 170px;" title="' . esc_attr__( 'Date and time the state was changed', 'wuclean-database-cleaner-for-woocommerce' ) . '">' . esc_html__( 'Timestamp', 'wuclean-database-cleaner-for-woocommerce' ) . '</th>';
    echo '</tr>';
    echo '</thead>';
    echo '<tbody>';
    if ( !empty( $paginated_logs ) ) {
        $row_index = 0;
        foreach ( $paginated_logs as $log ) {
            $bg_color = ( 0 === $row_index % 2 ? '#ffffff' : '#f9f9f9' );
            echo '<tr style="background-color: ' . esc_attr( $bg_color ) . '; transition: background-color 0.2s;" onmouseover="this.style.backgroundColor=\'#e8f4f8\'" onmouseout="this.style.backgroundColor=\'' . esc_attr( $bg_color ) . '\'">';
            ++$row_index;
            echo '<td style="text-align: left;">' . esc_html( $log['date'] ) . '</td>';
            echo '<td style="text-align: right;">' . esc_html( $log['number'] ) . '</td>';
            echo '<td style="text-align: right;">' . esc_html( $log['value'] ) . '</td>';
            echo '<td style="text-align: left;">' . esc_html( $log['name'] ) . '</td>';
            echo '<td style="text-align: left;">' . esc_html( $log['state'] ) . '</td>';
            echo '<td style="text-align: left;">' . (( !empty( $log['timestamp'] ) ? esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $log['timestamp'] ) ) ) : '' )) . '</td>';
            echo '</tr>';
        }
    } elseif ( !empty( $search_query ) ) {
        echo '<tr><td colspan="6" style="text-align: center; padding: 20px;">';
        echo esc_html__( 'No logs match your search criteria.', 'wuclean-database-cleaner-for-woocommerce' );
        $clear_url = add_query_arg( array(
            'page'     => 'wuclean-orders',
            'tab'      => 'logs',
            '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
        ), admin_url( 'admin.php' ) );
        echo '<br><a href="' . esc_url( $clear_url ) . '" class="button" style="margin-top: 10px;">' . esc_html__( 'Clear Search', 'wuclean-database-cleaner-for-woocommerce' ) . '</a>';
        echo '</td></tr>';
    } else {
        echo '<tr><td colspan="6" style="text-align: center; padding: 20px;">' . esc_html__( 'No order logs found.', 'wuclean-database-cleaner-for-woocommerce' ) . '</td></tr>';
    }
    echo '</tbody>';
    echo '</table>';
    // Pagination controls - updated styling.
    // Pagination - using centralized function..
    $preserve_params = array(
        'page'     => 'wuclean-orders',
        'tab'      => 'logs',
        '_wpnonce' => wp_create_nonce( 'wuclean-orders-tab' ),
    );
    if ( !empty( $search_query ) ) {
        $preserve_params['order_log_search'] = $search_query;
    }
    pt_wuclean_render_pagination( array(
        'current_page'    => $page,
        'total_pages'     => $total_pages,
        'base_url'        => admin_url( 'admin.php' ),
        'page_param'      => 'order_log_page',
        'preserve_params' => $preserve_params,
    ) );
    // Enqueue common JavaScript so the pagination "Go" button works on this tab.
    wp_enqueue_script(
        'pt-wuclean-common',
        plugin_dir_url( __FILE__ ) . 'assets/js/common.js',
        array('jquery'),
        '1.0.0',
        true
    );
    wp_localize_script( 'pt-wuclean-common', 'pt_wuclean_common_vars', pt_wuclean_get_common_script_strings() );
    echo '</div>';
}

/**
 * Helper functions for orders
 */
if ( !function_exists( 'pt_wuclean_safe_table_name' ) ) {
    /**
     * Return a sanitized, whitelisted table name for direct queries.
     *
     * @param string $table_key Logical table identifier.
     * @return string Sanitized table name or empty string if unavailable.
     */
    function pt_wuclean_safe_table_name(  $table_key  ) {
        global $wpdb;
        $map = array(
            'wc_orders'                  => ( isset( $wpdb->wc_orders ) ? $wpdb->wc_orders : $wpdb->prefix . 'wc_orders' ),
            'wc_orders_meta'             => ( isset( $wpdb->wc_orders_meta ) ? $wpdb->wc_orders_meta : $wpdb->prefix . 'wc_orders_meta' ),
            'wc_order_stats'             => ( isset( $wpdb->wc_order_stats ) ? $wpdb->wc_order_stats : $wpdb->prefix . 'wc_order_stats' ),
            'woocommerce_order_items'    => $wpdb->prefix . 'woocommerce_order_items',
            'woocommerce_order_itemmeta' => $wpdb->prefix . 'woocommerce_order_itemmeta',
            'users'                      => $wpdb->users,
            'usermeta'                   => $wpdb->usermeta,
            'posts'                      => $wpdb->posts,
            'postmeta'                   => $wpdb->postmeta,
        );
        if ( !isset( $map[$table_key] ) ) {
            return '';
        }
        return preg_replace( '/[^A-Za-z0-9_]/', '', $map[$table_key] );
    }

}
/**
 * Get flagged orders (HPOS-only) - Used by manual and scheduled scans
 *
 * Uses direct SQL for performance - avoids loading all orders into memory.
 * On stores with 100,000+ orders, this is 1000x+ faster than looping through all orders.
 *
 * @param int $batch_size Number of orders to retrieve.
 * @param int $cutoff     Timestamp cutoff for flagged orders.
 * @return array Array of order IDs.
 */
function pt_wuclean_get_flagged_orders(  $batch_size, $cutoff  ) {
    global $wpdb;
    // HPOS order + order meta tables use core WooCommerce naming with the site prefix.
    $orders_meta_table = $wpdb->prefix . 'wc_orders_meta';
    $orders_table = $wpdb->prefix . 'wc_orders';
    // Query only orders that have the flagged meta key AND are NOT already in trash.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table names sanitized via pt_wuclean_safe_table_name().
    // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder -- Table names are sanitized via pt_wuclean_safe_table_name().
    // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table names from pt_wuclean_safe_table_name(), cannot be placeholders.
    // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table names are sanitized via pt_wuclean_safe_table_name(); query is prepared via $wpdb->prepare().
    $flagged_order_ids = $wpdb->get_col( $wpdb->prepare(
        "SELECT om.order_id \n         FROM {$orders_meta_table} om\n         INNER JOIN {$orders_table} o ON om.order_id = o.id\n         WHERE om.meta_key = %s\n         AND CAST(om.meta_value AS UNSIGNED) <= %d\n         AND o.status != %s\n         ORDER BY CAST(om.meta_value AS UNSIGNED) ASC\n         LIMIT %d",
        '_pt_wuclean_flagged_at',
        $cutoff,
        'trash',
        $batch_size
    ) );
    // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    return ( is_array( $flagged_order_ids ) ? array_map( 'intval', $flagged_order_ids ) : array() );
}

/**
 * Count flagged orders (HPOS-only) - Used by manual and scheduled scans
 *
 * Uses direct SQL for performance - avoids loading all orders into memory.
 * On stores with 100,000+ orders, this is 1000x+ faster than looping through all orders.
 * Excludes protected orders to match the Flagged Orders tab display.
 *
 * @return int Number of flagged orders (excluding protected)
 */
function pt_wuclean_count_flagged_orders() {
    global $wpdb;
    // HPOS order + meta table names and user meta table.
    $orders_table = $wpdb->prefix . 'wc_orders';
    $orders_meta_table = $wpdb->prefix . 'wc_orders_meta';
    $usermeta_table = $wpdb->usermeta;
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table names sanitized via pt_wuclean_safe_table_name().
    // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table names from pt_wuclean_safe_table_name(), cannot be placeholders.
    // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table names are sanitized via pt_wuclean_safe_table_name(); query is prepared via $wpdb->prepare().
    $count = $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(DISTINCT om_flagged.order_id) \n         FROM {$orders_meta_table} om_flagged\n         INNER JOIN {$orders_table} o ON om_flagged.order_id = o.id\n         LEFT JOIN {$orders_meta_table} om_protected ON om_flagged.order_id = om_protected.order_id AND om_protected.meta_key = %s AND om_protected.meta_value = %s\n         LEFT JOIN {$usermeta_table} um_protected ON o.customer_id = um_protected.user_id AND um_protected.meta_key = %s AND um_protected.meta_value = %s AND o.customer_id > 0\n         WHERE om_flagged.meta_key = %s\n         AND o.status != %s\n         AND om_protected.order_id IS NULL\n         AND um_protected.user_id IS NULL",
        '_pt_wuclean_protected',
        '1',
        '_pt_wuclean_protect_orders',
        '1',
        '_pt_wuclean_flagged_at',
        'trash'
    ) );
    // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    return ( $count ? intval( $count ) : 0 );
}

/**
 * Checks whether an order is protected from trashing (per-order or user-level).
 *
 * @param int $order_id Order ID.
 * @return bool True if protected.
 */
function pt_wuclean_is_order_protected(  $order_id  ) {
    $order = wc_get_order( $order_id );
    if ( !$order ) {
        return false;
    }
    // Check if order itself is protected.
    $order_protected = '1' === $order->get_meta( '_pt_wuclean_protected', true );
    // Check if user has all orders protected.
    $user_id = $order->get_user_id();
    $user_protect_orders = ( $user_id ? '1' === get_user_meta( $user_id, '_pt_wuclean_protect_orders', true ) : false );
    return $order_protected || $user_protect_orders;
}

/**
 * Logs an order action (e.g. trashed, protection removed) to the order logs option.
 *
 * @param int    $order_id Order ID.
 * @param string $action   Action label (e.g. 'Trashed', 'Protection Removed').
 * @return void
 */
function pt_wuclean_log_order_action(  $order_id, $action  ) {
    $logs = get_option( 'pt_wuclean_order_logs', array() );
    $settings = get_option( 'pt_wuclean_settings', array() );
    $log_limit = $settings['order_log_limit'] ?? 100;
    // Get order data before it gets trashed/deleted.
    $order = wc_get_order( $order_id );
    $now = current_time( 'mysql' );
    $log_entry = array(
        'order_id'  => $order_id,
        'date'      => '',
        'number'    => '',
        'value'     => '',
        'name'      => '',
        'state'     => $action,
        'timestamp' => $now,
    );
    if ( $order ) {
        $log_entry['date'] = ( $order->get_date_created() ? $order->get_date_created()->date( 'Y-m-d H:i:s' ) : '' );
        $log_entry['number'] = $order->get_order_number();
        $log_entry['value'] = $order->get_total();
        $log_entry['name'] = $order->get_formatted_billing_full_name();
    }
    $logs[] = $log_entry;
    // Prune logs to limit.
    if ( count( $logs ) > $log_limit ) {
        $logs = array_slice( $logs, -$log_limit );
    }
    update_option( 'pt_wuclean_order_logs', $logs, false );
}

/**
 * Find orphaned orders using SQL (problematic/incomplete orders) - HPOS compatible - Used by manual and scheduled scans
 *
 * Orphaned orders are problematic/incomplete orders that should be flagged:
 * - customer_id IS NULL (corrupted data, not valid guest orders)
 * - customer_id > 0 but user doesn't exist (deleted user)
 * - Missing billing_email (incomplete order data, regardless of customer_id)
 *
 * @param int $days_old Minimum age in days.
 * @param int $limit    Maximum number of orders to return.
 * @return array Array of order IDs.
 */
function pt_wuclean_find_orphaned_orders_sql(  $days_old, $limit  ) {
    global $wpdb;
    // Calculate cutoff date.
    $cutoff_date = gmdate( 'Y-m-d H:i:s', strtotime( "-{$days_old} days" ) );
    // HPOS: Query wc_orders and wc_orders_meta tables.
    $orders_table = pt_wuclean_safe_table_name( 'wc_orders' );
    $orders_meta_table = pt_wuclean_safe_table_name( 'wc_orders_meta' );
    $usermeta_table = pt_wuclean_safe_table_name( 'usermeta' );
    $users_table = $wpdb->users;
    // WordPress users table.
    if ( '' === $orders_table || '' === $orders_meta_table || '' === $usermeta_table ) {
        return array();
    }
    // Find orders that are orphaned (problematic/incomplete):
    // 1. customer_id IS NULL (corrupted, not valid guest orders).
    // 2. customer_id > 0 but user doesn't exist (deleted user)
    // 3. Missing billing_email (incomplete order data)
    // Filter out protected orders at SQL level to prevent infinite loops.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table names sanitized via pt_wuclean_safe_table_name().
    // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder -- Table names are sanitized via pt_wuclean_safe_table_name()..
    // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table names from pt_wuclean_safe_table_name(), cannot be placeholders.
    // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table names are sanitized via pt_wuclean_safe_table_name(); query is prepared via $wpdb->prepare().
    $order_ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT o.id\n         FROM {$orders_table} o\n         LEFT JOIN {$orders_meta_table} om_flagged ON o.id = om_flagged.order_id AND om_flagged.meta_key = '_pt_wuclean_flagged_at'\n         LEFT JOIN {$orders_meta_table} om_protected ON o.id = om_protected.order_id AND om_protected.meta_key = '_pt_wuclean_protected' AND om_protected.meta_value = '1'\n         LEFT JOIN {$users_table} u ON o.customer_id = u.ID AND o.customer_id > 0\n         LEFT JOIN {$usermeta_table} um_protected ON o.customer_id = um_protected.user_id AND um_protected.meta_key = '_pt_wuclean_protect_orders' AND um_protected.meta_value = '1' AND o.customer_id > 0\n         WHERE o.type = 'shop_order'\n         AND o.status != 'trash'\n         AND o.date_created_gmt < %s\n         AND om_flagged.meta_value IS NULL\n         AND om_protected.order_id IS NULL\n         AND um_protected.user_id IS NULL\n         AND (\n             -- Condition 1: customer_id IS NULL (corrupted data)\n             o.customer_id IS NULL\n             -- Condition 2: customer_id > 0 but user doesn't exist (deleted user)\n             OR (o.customer_id > 0 AND u.ID IS NULL)\n             -- Condition 3: Missing billing_email for orders without valid user (customer_id = 0 or NULL)\n             -- Note: Registered user orders (customer_id > 0 with valid user) are not flagged for missing billing_email\n             -- as they may be valid even if billing_email is missing (user email can be retrieved from user account)\n             -- In HPOS, billing_email is a column in wc_orders table, not in wc_orders_meta\n             OR ((o.customer_id = 0 OR o.customer_id IS NULL) AND (o.billing_email IS NULL OR TRIM(o.billing_email) = ''))\n         )\n         ORDER BY o.date_created_gmt ASC\n         LIMIT %d", $cutoff_date, $limit ) );
    // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    return $order_ids;
}

/**
 * Find guest orders using SQL (valid guest orders only) - HPOS compatible - Used by manual and scheduled scans
 *
 * Guest orders are legitimate guest checkout orders: customer_id = 0 AND has billing_email
 *
 * @param int $days_old Minimum age in days.
 * @param int $limit    Maximum number of orders to return.
 * @return array Array of order IDs.
 */
function pt_wuclean_find_guest_orders_sql(  $days_old, $limit  ) {
    global $wpdb;
    // Calculate cutoff date.
    $cutoff_date = gmdate( 'Y-m-d H:i:s', strtotime( "-{$days_old} days" ) );
    // HPOS: Query wc_orders and wc_orders_meta tables.
    $orders_table = pt_wuclean_safe_table_name( 'wc_orders' );
    $orders_meta_table = pt_wuclean_safe_table_name( 'wc_orders_meta' );
    if ( '' === $orders_table || '' === $orders_meta_table ) {
        return array();
    }
    // Find valid guest orders: customer_id = 0 (not NULL) AND has billing_email.
    // Filter out protected orders at SQL level to prevent infinite loops.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table names sanitized via pt_wuclean_safe_table_name().
    // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder -- Table names are sanitized via pt_wuclean_safe_table_name()..
    // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table names from pt_wuclean_safe_table_name(), cannot be placeholders.
    // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table names are sanitized via pt_wuclean_safe_table_name(); query is prepared via $wpdb->prepare().
    $order_ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT o.id\n         FROM {$orders_table} o\n         LEFT JOIN {$orders_meta_table} om_flagged ON o.id = om_flagged.order_id AND om_flagged.meta_key = '_pt_wuclean_flagged_at'\n         LEFT JOIN {$orders_meta_table} om_protected ON o.id = om_protected.order_id AND om_protected.meta_key = '_pt_wuclean_protected' AND om_protected.meta_value = '1'\n         WHERE o.type = 'shop_order'\n         AND o.status != 'trash'\n         AND o.date_created_gmt < %s\n         AND om_flagged.meta_value IS NULL\n         AND om_protected.order_id IS NULL\n         AND o.customer_id = 0\n         AND o.billing_email IS NOT NULL\n         AND TRIM(o.billing_email) != ''\n         ORDER BY o.date_created_gmt ASC\n         LIMIT %d", $cutoff_date, $limit ) );
    // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    return $order_ids;
}

/**
 * Find old orders using SQL (any order past threshold) - HPOS compatible - Used by manual and scheduled scans
 *
 * @param int $days_old Minimum age in days.
 * @param int $limit    Maximum number of orders to return.
 * @return array Array of order IDs.
 */
function pt_wuclean_find_old_orders_sql(  $days_old, $limit  ) {
    global $wpdb;
    // Calculate cutoff date.
    $cutoff_date = gmdate( 'Y-m-d H:i:s', strtotime( "-{$days_old} days" ) );
    // HPOS: Query wc_orders and wc_orders_meta tables.
    $orders_table = pt_wuclean_safe_table_name( 'wc_orders' );
    $orders_meta_table = pt_wuclean_safe_table_name( 'wc_orders_meta' );
    $usermeta_table = pt_wuclean_safe_table_name( 'usermeta' );
    if ( '' === $orders_table || '' === $orders_meta_table || '' === $usermeta_table ) {
        return array();
    }
    // Find all orders older than threshold.
    // Filter out protected orders at SQL level to prevent infinite loops.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table names sanitized via pt_wuclean_safe_table_name().
    // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder -- Table names are sanitized via pt_wuclean_safe_table_name()..
    // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table names from pt_wuclean_safe_table_name(), cannot be placeholders.
    // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table names are sanitized via pt_wuclean_safe_table_name(); query is prepared via $wpdb->prepare().
    $order_ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT o.id\n         FROM {$orders_table} o\n         LEFT JOIN {$orders_meta_table} om_flagged ON o.id = om_flagged.order_id AND om_flagged.meta_key = '_pt_wuclean_flagged_at'\n         LEFT JOIN {$orders_meta_table} om_protected ON o.id = om_protected.order_id AND om_protected.meta_key = '_pt_wuclean_protected'\n         LEFT JOIN {$usermeta_table} um_protected ON o.customer_id = um_protected.user_id AND um_protected.meta_key = '_pt_wuclean_protect_orders' AND o.customer_id > 0\n         WHERE o.type = 'shop_order'\n         AND o.status != 'trash'\n         AND o.date_created_gmt < %s\n         AND om_flagged.meta_value IS NULL\n         AND (om_protected.meta_value IS NULL OR om_protected.meta_value != '1')\n         AND (um_protected.meta_value IS NULL OR um_protected.meta_value != '1')\n         ORDER BY o.date_created_gmt ASC\n         LIMIT %d", $cutoff_date, $limit ) );
    // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    return $order_ids;
}

/**
 * Find orders to flag using SQL (unified function for both scheduled and manual scans)
 * Uses optimized SQL queries for consistent, fast results across all scan types
 *
 * @param int $batch_size Number of orders to find.
 * @return array Array with 'orders' (associative array of order_id => flag_reason) and 'total_found'.
 */
function pt_wuclean_find_orders_to_flag_sql(  $batch_size  ) {
    // Force fresh settings from database (bypass ALL caching to prevent stale data).
    global $wpdb;
    wp_cache_delete( 'pt_wuclean_settings', 'options' );
    // Get settings directly from database, bypassing all cache layers.
    $settings_raw = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", 'pt_wuclean_settings' ) );
    $settings = ( $settings_raw ? maybe_unserialize( $settings_raw ) : array() );
    // Load enable flags and days values separately.
    $enable_trash_orphaned_orders = intval( $settings['enable_trash_orphaned_orders'] ?? 0 );
    $trash_orphaned_orders_days = intval( $settings['trash_orphaned_orders_days'] ?? 365 );
    $enable_trash_guest_orders = intval( $settings['enable_trash_guest_orders'] ?? 0 );
    $trash_guest_orders_days = intval( $settings['trash_guest_orders_days'] ?? 365 );
    $enable_trash_all_orders = intval( $settings['enable_trash_all_orders'] ?? 0 );
    $trash_all_orders_days = intval( $settings['trash_all_orders_days'] ?? 730 );
    // If no cleanup criteria are enabled, return empty array.
    if ( !$enable_trash_guest_orders && !$enable_trash_all_orders && !$enable_trash_orphaned_orders ) {
        return array(
            'orders'      => array(),
            'total_found' => 0,
        );
    }
    $orders_to_flag = array();
    // Priority 1: Find orphaned orders using SQL (highest priority).
    if ( $enable_trash_orphaned_orders && count( $orders_to_flag ) < $batch_size ) {
        $remaining_quota = $batch_size - count( $orders_to_flag );
        $orphaned_orders = pt_wuclean_find_orphaned_orders_sql( $trash_orphaned_orders_days, $remaining_quota );
        foreach ( $orphaned_orders as $order_id ) {
            if ( !isset( $orders_to_flag[$order_id] ) ) {
                $orders_to_flag[$order_id] = 'Orphaned - ' . $trash_orphaned_orders_days . ' days or more';
            }
        }
    }
    // Priority 2: Find guest orders using SQL (only if enabled AND quota not filled).
    if ( $enable_trash_guest_orders && count( $orders_to_flag ) < $batch_size ) {
        $remaining_quota = $batch_size - count( $orders_to_flag );
        $guest_orders = pt_wuclean_find_guest_orders_sql( $trash_guest_orders_days, $remaining_quota );
        foreach ( $guest_orders as $order_id ) {
            if ( !isset( $orders_to_flag[$order_id] ) ) {
                $orders_to_flag[$order_id] = 'Guest - ' . $trash_guest_orders_days . ' days or more';
            }
        }
    }
    // Priority 3: Find all old orders using SQL (only if enabled AND quota still not filled).
    if ( $enable_trash_all_orders && count( $orders_to_flag ) < $batch_size ) {
        $remaining_quota = $batch_size - count( $orders_to_flag );
        $old_orders = pt_wuclean_find_old_orders_sql( $trash_all_orders_days, $remaining_quota );
        foreach ( $old_orders as $order_id ) {
            if ( !isset( $orders_to_flag[$order_id] ) ) {
                $orders_to_flag[$order_id] = 'Old - ' . $trash_all_orders_days . ' days or more';
            }
        }
    }
    return array(
        'orders'      => $orders_to_flag,
        'total_found' => count( $orders_to_flag ),
    );
}

/**
 * Helper function to check AJAX permissions
 */
function pt_wuclean_orders_check_ajax_permissions() {
    if ( !current_user_can( pt_wuclean_get_required_capability() ) ) {
        wp_send_json_error( array(
            'message' => __( 'Insufficient permissions', 'wuclean-database-cleaner-for-woocommerce' ),
        ) );
        exit;
    }
}

/**
 * AJAX handler to start order search
 */
function pt_wuclean_ajax_start_order_search() {
    pt_wuclean_orders_check_ajax_permissions();
    // Check if nonce is provided and valid.
    if ( !isset( $_POST['nonce'] ) || !wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'pt_wuclean_order_search' ) ) {
        wp_send_json_error( array(
            'message' => __( 'Invalid or missing nonce', 'wuclean-database-cleaner-for-woocommerce' ),
        ) );
        return;
    }
    try {
        // Check if WooCommerce is active.
        if ( !function_exists( 'wc_get_orders' ) ) {
            wp_send_json_error( array(
                'message' => __( 'WooCommerce is not active or not loaded', 'wuclean-database-cleaner-for-woocommerce' ),
            ) );
            return;
        }
        // Clear old progress data from previous runs.
        delete_option( 'pt_wuclean_order_scan_progress' );
        wp_cache_delete( 'pt_wuclean_order_scan_progress', 'options' );
        // Get settings.
        $settings = get_option( 'pt_wuclean_settings', array() );
        $batch_size = max( 1, (int) ($settings['order_cleanup_batch_size'] ?? 10) );
        // Manual flag scan progress: processed = cumulative orders flagged this run; total_items is a
        // sliding ceiling (processed + batch_size while more batches may run) so the bar moves each batch
        // instead of comparing flags to the entire order catalog (which rounded to 0% for most shops).
        $progress_data = array(
            'status'                  => 'running',
            'total_items'             => $batch_size,
            'processed'               => 0,
            'flagged_count'           => 0,
            'skipped_protected'       => 0,
            'skipped_already_flagged' => 0,
            'start_time'              => time(),
        );
        // Store progress data.
        update_option( 'pt_wuclean_order_scan_progress', $progress_data, false );
        wp_send_json_success( array(
            'message'  => __( 'Order scan started successfully.', 'wuclean-database-cleaner-for-woocommerce' ),
            'progress' => $progress_data,
        ) );
    } catch ( Exception $e ) {
        /* translators: %s: error message */
        $error_message = sprintf( __( 'Error starting scan: %s', 'wuclean-database-cleaner-for-woocommerce' ), $e->getMessage() );
        wp_send_json_error( array(
            'message' => $error_message,
        ) );
    }
}

/**
 * AJAX handler to process the next order-flagging batch for the admin manual scan UI.
 *
 * Background/cron flagging does not use this endpoint.
 */
function pt_wuclean_ajax_process_order_batch() {
    pt_wuclean_orders_check_ajax_permissions();
    check_ajax_referer( 'pt_wuclean_order_search', 'nonce' );
    try {
        // Check if WooCommerce is active.
        if ( !function_exists( 'wc_get_orders' ) ) {
            wp_send_json_error( array(
                'message' => __( 'WooCommerce is not active or not loaded', 'wuclean-database-cleaner-for-woocommerce' ),
            ) );
            return;
        }
        // Get current progress.
        $progress_data = get_option( 'pt_wuclean_order_scan_progress', array() );
        if ( empty( $progress_data ) ) {
            wp_send_json_error( array(
                'message' => __( 'No order scan found.', 'wuclean-database-cleaner-for-woocommerce' ),
            ) );
            return;
        }
        // Check if scan is already completed.
        if ( 'completed' === $progress_data['status'] ) {
            wp_send_json_success( array(
                'message'   => __( 'Scan completed.', 'wuclean-database-cleaner-for-woocommerce' ),
                'completed' => true,
                'progress'  => $progress_data,
            ) );
            return;
        }
        // Check if scan was stopped.
        if ( 'stopped' === ($progress_data['status'] ?? '') ) {
            wp_send_json_success( array(
                'message'   => __( 'Scan stopped by user.', 'wuclean-database-cleaner-for-woocommerce' ),
                'completed' => false,
                'progress'  => $progress_data,
            ) );
            return;
        }
        // Check if status is running.
        if ( 'running' !== ($progress_data['status'] ?? '') ) {
            wp_send_json_error( array(
                'message' => __( 'Order scan is not running.', 'wuclean-database-cleaner-for-woocommerce' ),
            ) );
            return;
        }
        // Process the next batch using unified SQL function.
        $settings = get_option( 'pt_wuclean_settings', array() );
        $batch_size = max( 1, (int) ($settings['order_cleanup_batch_size'] ?? 10) );
        $now = time();
        $flagged_count = $progress_data['flagged_count'] ?? 0;
        $processed = $progress_data['processed'] ?? 0;
        // Use unified SQL function to find orders to flag.
        // SQL already filters out protected and already-flagged orders.
        $result = pt_wuclean_find_orders_to_flag_sql( $batch_size );
        $orders_with_reasons = $result['orders'];
        // Associative array: order_id => flag_reason.
        $orders_found = $result['total_found'];
        if ( $orders_found > 0 ) {
            foreach ( $orders_with_reasons as $order_id => $flag_reason ) {
                $order = wc_get_order( $order_id );
                if ( !$order ) {
                    continue;
                }
                // Flag the order.
                $order->update_meta_data( '_pt_wuclean_flagged_at', $now );
                $order->delete_meta_data( '_pt_wuclean_marked_for_trash' );
                $order->update_meta_data( '_pt_wuclean_flag_reason', $flag_reason );
                $order->save();
                ++$flagged_count;
                ++$processed;
                // Track how many orders we've checked.
                // Log the flagging action.
                pt_wuclean_log_order_action( $order_id, 'Flagged for trash' );
            }
            // Update progress (processed = orders newly flagged this run, cumulative).
            $progress_data['flagged_count'] = $flagged_count;
            $progress_data['processed'] = $processed;
            // Finish when this batch is not full — nothing left matching criteria (same run continues until then).
            $should_complete = $orders_found < $batch_size;
            if ( !$should_complete ) {
                $progress_data['total_items'] = $processed + $batch_size;
            } else {
                $progress_data['total_items'] = max( $processed, 1 );
            }
            if ( $should_complete ) {
                $progress_data['status'] = 'completed';
            }
            update_option( 'pt_wuclean_order_scan_progress', $progress_data, false );
            // Create appropriate progress message.
            if ( 'completed' === $progress_data['status'] ) {
                $message = sprintf( 
                    // translators: %d: newly flagged orders.
                    __( 'Scan complete! New flags: %d', 'wuclean-database-cleaner-for-woocommerce' ),
                    $flagged_count
                 );
                // Include stored flagged orders count for redirect logic (may include orders from previous scans).
                $progress_data['stored_flagged_count'] = pt_wuclean_count_flagged_orders();
            } else {
                $message = sprintf( 
                    // translators: %d: newly flagged orders.
                    __( 'Processing... New flags: %d', 'wuclean-database-cleaner-for-woocommerce' ),
                    $flagged_count
                 );
            }
            wp_send_json_success( array(
                'message'   => $message,
                'completed' => 'completed' === $progress_data['status'],
                'progress'  => $progress_data,
            ) );
        } else {
            // No more orders to process.
            $progress_data['status'] = 'completed';
            $progress_data['total_items'] = max( (int) ($progress_data['processed'] ?? 0), 1 );
            // Include stored flagged orders count for redirect logic (may include orders from previous scans).
            $progress_data['stored_flagged_count'] = pt_wuclean_count_flagged_orders();
            update_option( 'pt_wuclean_order_scan_progress', $progress_data, false );
            $flagged_count = $progress_data['flagged_count'] ?? 0;
            $message = sprintf( 
                // translators: %d: newly flagged orders.
                __( 'Scan complete! New flags: %d', 'wuclean-database-cleaner-for-woocommerce' ),
                $flagged_count
             );
            wp_send_json_success( array(
                'message'   => $message,
                'completed' => true,
                'progress'  => $progress_data,
            ) );
        }
    } catch ( Exception $e ) {
        /* translators: %s: error message */
        $error_message = sprintf( __( 'Error processing batch: %s', 'wuclean-database-cleaner-for-woocommerce' ), $e->getMessage() );
        wp_send_json_error( array(
            'message' => $error_message,
        ) );
    }
}

/**
 * AJAX handler to stop order search
 */
function pt_wuclean_ajax_stop_order_search() {
    pt_wuclean_orders_check_ajax_permissions();
    check_ajax_referer( 'pt_wuclean_order_search', 'nonce' );
    try {
        // Get current progress and mark as stopped.
        $progress_data = get_option( 'pt_wuclean_order_scan_progress', array() );
        if ( !empty( $progress_data ) ) {
            $progress_data['status'] = 'stopped';
            update_option( 'pt_wuclean_order_scan_progress', $progress_data, false );
        }
        wp_send_json_success( array(
            'message' => __( 'Order scan stopped.', 'wuclean-database-cleaner-for-woocommerce' ),
        ) );
    } catch ( Exception $e ) {
        /* translators: %s: error message */
        $error_message = sprintf( __( 'Error stopping scan: %s', 'wuclean-database-cleaner-for-woocommerce' ), $e->getMessage() );
        wp_send_json_error( array(
            'message' => $error_message,
        ) );
    }
}

/**
 * Get all protected orders across all users (HPOS-only)
 * Returns array with order_id and protection_type for display
 * Optimized using direct SQL queries for performance
 *
 * @return array Array of arrays with 'order_id' and 'protection_type'
 */
function pt_wuclean_get_all_protected_orders() {
    global $wpdb;
    $orders_meta_table = pt_wuclean_safe_table_name( 'wc_orders_meta' );
    $orders_table = pt_wuclean_safe_table_name( 'wc_orders' );
    $usermeta_table = pt_wuclean_safe_table_name( 'usermeta' );
    if ( '' === $orders_meta_table || '' === $orders_table || '' === $usermeta_table ) {
        return array();
    }
    // Step 1: Get all orders with individual protection meta (fast SQL query).
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table names sanitized via pt_wuclean_safe_table_name().
    // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder -- Table names are sanitized via pt_wuclean_safe_table_name().
    // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table names from pt_wuclean_safe_table_name(), cannot be placeholders.
    // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table names are sanitized via pt_wuclean_safe_table_name(); query is prepared via $wpdb->prepare().
    $individually_protected_ids = $wpdb->get_col( $wpdb->prepare(
        "SELECT DISTINCT om.order_id \n         FROM {$orders_meta_table} om\n         INNER JOIN {$orders_table} o ON om.order_id = o.id\n         WHERE om.meta_key = %s\n         AND om.meta_value = %s\n         AND o.status != %s",
        '_pt_wuclean_protected',
        '1',
        'trash'
    ) );
    // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared.
    // Step 2: Get all users who have "protect all orders" enabled.
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table names sanitized via pt_wuclean_safe_table_name().
    // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder -- Table names are sanitized via pt_wuclean_safe_table_name().
    // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from pt_wuclean_safe_table_name(), cannot be a placeholder.
    // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table names are sanitized via pt_wuclean_safe_table_name(); query is prepared via $wpdb->prepare().
    $protected_user_ids = $wpdb->get_col( $wpdb->prepare( "SELECT user_id \n         FROM {$usermeta_table}\n         WHERE meta_key = %s\n         AND meta_value = %s", '_pt_wuclean_protect_orders', '1' ) );
    // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared.
    // Step 3: Get all order IDs for users with "protect all orders" enabled (if any).
    $user_level_protected_ids = array();
    if ( !empty( $protected_user_ids ) ) {
        $placeholders = implode( ', ', array_fill( 0, count( $protected_user_ids ), '%d' ) );
        $params = array_map( 'intval', $protected_user_ids );
        $params[] = 'trash';
        // PCP-compliant because prepare() is *directly* inside get_col().
        // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from pt_wuclean_safe_table_name(), cannot be a placeholder.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table names sanitized via pt_wuclean_safe_table_name().
        // phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter -- $placeholders contains only '%d' tokens; all dynamic values are integers in $params and bound via prepare().
        $user_level_protected_ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT o.id \n                 FROM {$orders_table} o\n                 WHERE o.customer_id IN ({$placeholders})\n                 AND o.customer_id > 0\n                 AND o.status != %s", ...$params ) );
        // phpcs:enable PluginCheck.Security.DirectDB.UnescapedDBParameter
        // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared.
    }
    // Combine both lists and get unique order IDs.
    $all_protected_order_ids = array_unique( array_merge( $individually_protected_ids, $user_level_protected_ids ) );
    // Build the result array with protection details.
    $protected_orders = array();
    foreach ( $all_protected_order_ids as $order_id ) {
        $order = wc_get_order( $order_id );
        if ( !$order ) {
            continue;
        }
        $is_individually_protected = in_array( $order_id, $individually_protected_ids, true );
        $is_user_level_protected = in_array( $order_id, $user_level_protected_ids, true );
        $user_id = $order->get_user_id();
        $user_data = ( $user_id ? get_user_by( 'id', $user_id ) : null );
        $protected_orders[] = array(
            'order_id'               => $order_id,
            'individually_protected' => $is_individually_protected,
            'user_level_protected'   => $is_user_level_protected,
            'user_id'                => $user_id,
            'user_login'             => ( $user_data ? $user_data->user_login : '' ),
        );
    }
    return $protected_orders;
}

/**
 * Get protected orders for a specific user (HPOS-only)
 * Returns array with order_id and protection_type for display
 * Optimized using direct SQL queries for performance
 *
 * Uses %i identifier placeholders in $wpdb->prepare() (requires WordPress 6.2+).
 *
 * @param int $user_id User ID.
 * @return array Array of arrays with 'order_id' and protection details.
 */
function pt_wuclean_get_user_protected_orders(  $user_id  ) {
    global $wpdb;
    // HPOS order + order meta tables use core WooCommerce naming with the site prefix.
    $orders_table = pt_wuclean_safe_table_name( 'wc_orders' );
    $orders_meta_table = pt_wuclean_safe_table_name( 'wc_orders_meta' );
    if ( '' === $orders_table || '' === $orders_meta_table ) {
        return array();
    }
    // Check if user has all orders protected.
    $user_protect_all = '1' === get_user_meta( $user_id, '_pt_wuclean_protect_orders', true );
    $user_data = get_user_by( 'id', $user_id );
    // Step 1: Get all orders for this user using SQL (fast).
    // Table name via %i (WP 6.2+ identifier placeholder); values via prepare().
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- HPOS batch query; no object cache equivalent.
    $user_order_ids = $wpdb->get_col( $wpdb->prepare(
        'SELECT DISTINCT o.id FROM %i o WHERE o.customer_id = %d AND o.customer_id > 0 AND o.status != %s',
        $orders_table,
        $user_id,
        'trash'
    ) );
    // Step 2: Individually protected orders for this user (JOIN avoids dynamic IN() and satisfies identifier escaping via %i).
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- HPOS batch query; no object cache equivalent.
    $individually_protected_ids = $wpdb->get_col( $wpdb->prepare(
        'SELECT DISTINCT m.order_id FROM %i m INNER JOIN %i o ON o.id = m.order_id WHERE o.customer_id = %d AND o.customer_id > 0 AND o.status != %s AND m.meta_key = %s AND m.meta_value = %s',
        $orders_meta_table,
        $orders_table,
        $user_id,
        'trash',
        '_pt_wuclean_protected',
        '1'
    ) );
    // Build result: if user has "protect all", include all their orders; otherwise just individually protected.
    $protected_orders = array();
    $orders_to_include = ( $user_protect_all ? $user_order_ids : $individually_protected_ids );
    foreach ( $orders_to_include as $order_id ) {
        $is_individually_protected = in_array( $order_id, $individually_protected_ids, true );
        $protected_orders[] = array(
            'order_id'               => $order_id,
            'individually_protected' => $is_individually_protected,
            'user_level_protected'   => $user_protect_all,
            'user_id'                => $user_id,
            'user_login'             => ( $user_data ? $user_data->user_login : '' ),
        );
    }
    return $protected_orders;
}

/**
 * Detect orphaned order meta in HPOS tables
 * Processes ALL orphaned order data in one run using conservative batching
 *
 * Detects two types:
 * 1. Orphaned wc_orders_meta (meta without parent order)
 * 2. Orphaned woocommerce_order_itemmeta (item meta without parent order item)
 *
 * @param bool $store_results Whether to store results in options/transients.
 * @return array Array of orphaned items with intelligent WooCommerce-specific filtering.
 *
 * Uses %i identifier placeholders in $wpdb->prepare() (requires WordPress 6.2+).
 */
function pt_wuclean_detect_orphaned_order_meta(  $store_results = true  ) {
    global $wpdb;
    // Set execution time limit to prevent timeouts.
    // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged, WordPress.PHP.NoSilencedErrors.Discouraged -- set_time_limit may be disabled; @ fails gracefully.
    @set_time_limit( 60 );
    // 60 seconds - conservative limit for shared hosting compatibility.
    // Get user's batch size setting for memory management.
    $settings = get_option( 'pt_wuclean_settings', array() );
    $batch_size = $settings['order_cleanup_batch_size'] ?? 10;
    $orphaned_data = array();
    /* translators: %1$s: meta key, %2$d: order ID. */
    $order_meta_desc_fmt = __( 'Orphaned order meta: %1$s (Order ID: %2$d)', 'wuclean-database-cleaner-for-woocommerce' );
    /* translators: %1$s: meta key, %2$d: order item ID. */
    $order_item_meta_desc_fmt = __( 'Orphaned order item meta: %1$s (Order Item ID: %2$d)', 'wuclean-database-cleaner-for-woocommerce' );
    // HPOS table names.
    $orders_table = pt_wuclean_safe_table_name( 'wc_orders' );
    $orders_meta_table = pt_wuclean_safe_table_name( 'wc_orders_meta' );
    $order_items_table = pt_wuclean_safe_table_name( 'woocommerce_order_items' );
    $order_itemmeta_table = pt_wuclean_safe_table_name( 'woocommerce_order_itemmeta' );
    if ( '' === $orders_table || '' === $orders_meta_table || '' === $order_items_table || '' === $order_itemmeta_table ) {
        return array();
    }
    // Step 1: Find ALL orphaned wc_orders_meta in batches.
    $meta_offset = 0;
    while ( true ) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Batched HPOS orphan scan.
        $orphaned_order_meta = $wpdb->get_results( $wpdb->prepare(
            'SELECT om.id, om.order_id, om.meta_key, om.meta_value FROM %i om LEFT JOIN %i o ON om.order_id = o.id WHERE o.id IS NULL AND om.order_id IS NOT NULL LIMIT %d OFFSET %d',
            $orders_meta_table,
            $orders_table,
            $batch_size,
            $meta_offset
        ) );
        if ( empty( $orphaned_order_meta ) ) {
            break;
            // No more results.
        }
        // Process this batch - only include WooCommerce-specific meta keys.
        foreach ( $orphaned_order_meta as $meta ) {
            // Intelligent filtering: Only include order-specific meta keys.
            $is_order_meta = strpos( $meta->meta_key, '_billing_' ) === 0 || strpos( $meta->meta_key, '_shipping_' ) === 0 || strpos( $meta->meta_key, '_customer_' ) === 0 || strpos( $meta->meta_key, '_order_' ) === 0 || strpos( $meta->meta_key, '_payment_' ) === 0 || strpos( $meta->meta_key, '_transaction_' ) === 0 || strpos( $meta->meta_key, '_cart_' ) === 0 || strpos( $meta->meta_key, '_wc_' ) === 0 || in_array( $meta->meta_key, array(
                '_prices_include_tax',
                '_customer_user',
                '_order_key',
                '_order_currency',
                '_cart_discount',
                '_cart_discount_tax',
                '_order_shipping',
                '_order_shipping_tax',
                '_order_tax',
                '_order_total',
                '_order_version',
                '_recorded_sales',
                '_recorded_coupon_usage_counts'
            ), true );
            if ( $is_order_meta ) {
                $orphaned_data[] = array(
                    'type'        => 'order_meta',
                    'id'          => $meta->id,
                    'order_id'    => $meta->order_id,
                    'meta_key'    => $meta->meta_key,
                    'meta_value'  => substr( $meta->meta_value, 0, 100 ),
                    'description' => sprintf( $order_meta_desc_fmt, $meta->meta_key, $meta->order_id ),
                );
            }
        }
        $meta_offset += count( $orphaned_order_meta );
        // Memory optimization.
        if ( function_exists( 'gc_collect_cycles' ) ) {
            gc_collect_cycles();
        }
    }
    // Step 2: Find ALL orphaned woocommerce_order_itemmeta in batches.
    $itemmeta_offset = 0;
    while ( true ) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Batched HPOS orphan scan.
        $orphaned_item_meta = $wpdb->get_results( $wpdb->prepare(
            'SELECT oim.meta_id, oim.order_item_id, oim.meta_key, oim.meta_value FROM %i oim LEFT JOIN %i oi ON oim.order_item_id = oi.order_item_id WHERE oi.order_item_id IS NULL AND oim.order_item_id IS NOT NULL LIMIT %d OFFSET %d',
            $order_itemmeta_table,
            $order_items_table,
            $batch_size,
            $itemmeta_offset
        ) );
        if ( empty( $orphaned_item_meta ) ) {
            break;
            // No more results.
        }
        // Process this batch.
        foreach ( $orphaned_item_meta as $meta ) {
            $orphaned_data[] = array(
                'type'          => 'order_item_meta',
                'id'            => $meta->meta_id,
                'order_item_id' => $meta->order_item_id,
                'meta_key'      => $meta->meta_key,
                'meta_value'    => substr( $meta->meta_value, 0, 100 ),
                'description'   => sprintf( $order_item_meta_desc_fmt, $meta->meta_key, $meta->order_item_id ),
            );
        }
        $itemmeta_offset += count( $orphaned_item_meta );
        // Memory optimization.
        if ( function_exists( 'gc_collect_cycles' ) ) {
            gc_collect_cycles();
        }
    }
    if ( $store_results ) {
        // Safety cap: prevent storing extremely large orphaned datasets as a single serialized option.
        $max_items = intval( apply_filters( 'pt_wuclean_orphaned_order_data_max_items', 10000 ) );
        if ( $max_items > 0 && is_array( $orphaned_data ) && count( $orphaned_data ) > $max_items ) {
            $orphaned_data = array_slice( $orphaned_data, 0, $max_items );
        }
        set_transient( 'pt_wuclean_orphaned_order_data', $orphaned_data, 24 * HOUR_IN_SECONDS );
        update_option( 'pt_wuclean_orphaned_order_data', $orphaned_data, false );
        update_option( 'pt_wuclean_orphaned_order_data_count', count( $orphaned_data ), false );
    } else {
        pt_wuclean_clear_orphaned_order_data();
    }
    // Final memory cleanup.
    if ( function_exists( 'gc_collect_cycles' ) ) {
        gc_collect_cycles();
    }
    return $orphaned_data;
}

/**
 * AJAX handler to start orphaned meta scan
 */
function pt_wuclean_ajax_start_orphaned_meta_scan() {
    pt_wuclean_orders_check_ajax_permissions();
    // Check if nonce is provided and valid.
    if ( !isset( $_POST['nonce'] ) || !wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'pt_wuclean_orphaned_meta_search' ) ) {
        wp_send_json_error( array(
            'message' => __( 'Invalid or missing nonce', 'wuclean-database-cleaner-for-woocommerce' ),
        ) );
        return;
    }
    try {
        // Clear old progress data from previous runs.
        delete_option( 'pt_wuclean_orphaned_meta_scan_progress' );
        wp_cache_delete( 'pt_wuclean_orphaned_meta_scan_progress', 'options' );
        // Run the detection function.
        $orphaned_data = pt_wuclean_detect_orphaned_order_meta();
        // Ensure we have an array (safety check).
        if ( !is_array( $orphaned_data ) ) {
            $orphaned_data = array();
        }
        $stored_count = count( $orphaned_data );
        // Return success with proper structure for JavaScript progress handler.
        wp_send_json_success( array(
            'message'        => __( 'Orphaned meta scan completed successfully.', 'wuclean-database-cleaner-for-woocommerce' ),
            'orphaned_count' => $stored_count,
            'completed'      => true,
            'progress'       => array(
                'status'            => 'completed',
                'total_found'       => $stored_count,
                'stored_data_count' => $stored_count,
            ),
        ) );
    } catch ( Exception $e ) {
        /* translators: %s: error message */
        $error_message = sprintf( __( 'Error starting scan: %s', 'wuclean-database-cleaner-for-woocommerce' ), $e->getMessage() );
        wp_send_json_error( array(
            'message' => $error_message,
        ) );
    }
}

/**
 * AJAX handler to process orphaned meta scan batch
 * Since detection completes in start handler, this just returns completed status
 */
function pt_wuclean_ajax_process_orphaned_meta_scan() {
    pt_wuclean_orders_check_ajax_permissions();
    check_ajax_referer( 'pt_wuclean_orphaned_meta_search', 'nonce' );
    // Get the scan results (already completed in start handler).
    $orphaned_data = get_transient( 'pt_wuclean_orphaned_order_data' );
    if ( false === $orphaned_data ) {
        $orphaned_data = get_option( 'pt_wuclean_orphaned_order_data', array() );
    }
    $stored_count = ( is_array( $orphaned_data ) ? count( $orphaned_data ) : 0 );
    // Return completed status.
    wp_send_json_success( array(
        'completed' => true,
        'progress'  => array(
            'status'            => 'completed',
            'total_found'       => $stored_count,
            'stored_data_count' => $stored_count,
        ),
    ) );
}

/**
 * AJAX handler to stop orphaned meta scan
 */
function pt_wuclean_ajax_stop_orphaned_meta_scan() {
    pt_wuclean_orders_check_ajax_permissions();
    // Check nonce.
    if ( !isset( $_POST['nonce'] ) || !wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'pt_wuclean_orphaned_meta_search' ) ) {
        wp_send_json_error( array(
            'message' => __( 'Invalid nonce', 'wuclean-database-cleaner-for-woocommerce' ),
        ) );
        return;
    }
    // Update progress to stopped.
    $progress_data = get_option( 'pt_wuclean_orphaned_meta_scan_progress', array() );
    $progress_data['status'] = 'stopped';
    $progress_data['message'] = __( 'Scan stopped by user', 'wuclean-database-cleaner-for-woocommerce' );
    update_option( 'pt_wuclean_orphaned_meta_scan_progress', $progress_data, false );
    wp_send_json_success( array(
        'message' => __( 'Scan stopped', 'wuclean-database-cleaner-for-woocommerce' ),
    ) );
}

/**
 * AJAX: clear order scan progress (after redirect / UI cleanup).
 */
function pt_wuclean_ajax_clear_order_scan_progress() {
    pt_wuclean_orders_check_ajax_permissions();
    check_ajax_referer( 'pt_wuclean_order_search', 'nonce' );
    delete_option( 'pt_wuclean_order_scan_progress' );
    wp_send_json_success( array(
        'message' => __( 'Progress cleared.', 'wuclean-database-cleaner-for-woocommerce' ),
    ) );
}

/**
 * AJAX: clear orphaned order meta scan progress option.
 */
function pt_wuclean_ajax_clear_orphaned_meta_scan_progress() {
    pt_wuclean_orders_check_ajax_permissions();
    check_ajax_referer( 'pt_wuclean_orphaned_meta_search', 'nonce' );
    delete_option( 'pt_wuclean_orphaned_meta_scan_progress' );
    wp_send_json_success( array(
        'message' => __( 'Progress cleared.', 'wuclean-database-cleaner-for-woocommerce' ),
    ) );
}

// Register AJAX actions.
add_action( 'wp_ajax_pt_wuclean_start_order_search', 'pt_wuclean_ajax_start_order_search' );
add_action( 'wp_ajax_pt_wuclean_process_order_batch', 'pt_wuclean_ajax_process_order_batch' );
add_action( 'wp_ajax_pt_wuclean_stop_order_search', 'pt_wuclean_ajax_stop_order_search' );
add_action( 'wp_ajax_pt_wuclean_clear_order_scan_progress', 'pt_wuclean_ajax_clear_order_scan_progress' );
add_action( 'wp_ajax_pt_wuclean_start_orphaned_meta_scan', 'pt_wuclean_ajax_start_orphaned_meta_scan' );
add_action( 'wp_ajax_pt_wuclean_process_orphaned_meta_scan', 'pt_wuclean_ajax_process_orphaned_meta_scan' );
add_action( 'wp_ajax_pt_wuclean_stop_orphaned_meta_scan', 'pt_wuclean_ajax_stop_orphaned_meta_scan' );
add_action( 'wp_ajax_pt_wuclean_clear_orphaned_meta_scan_progress', 'pt_wuclean_ajax_clear_orphaned_meta_scan_progress' );