<?php

/**
 * Admin settings and user profile meta
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
// Include required files from new admin folder structure
require_once SPAR_PLUGIN_PATH . 'includes/admin/pages/settings-page.php';
require_once SPAR_PLUGIN_PATH . 'includes/admin/tables/reward-vouchers-table.php';
require_once SPAR_PLUGIN_PATH . 'includes/admin/tables/customer-points-table.php';
require_once SPAR_PLUGIN_PATH . 'includes/admin/tables/referrals-table.php';
require_once SPAR_PLUGIN_PATH . 'includes/admin/pages/referrals-page.php';
require_once SPAR_PLUGIN_PATH . 'includes/functions-coupons.php';
require_once SPAR_PLUGIN_PATH . 'includes/functions-email.php';
require_once SPAR_PLUGIN_PATH . 'includes/functions-levels.php';
// Include referral link functionality
require_once SPAR_PLUGIN_PATH . 'includes/links/functions-referral-links.php';
require_once SPAR_PLUGIN_PATH . 'includes/links/functions-referral-tracking.php';
// Load core admin pages
require_once SPAR_PLUGIN_PATH . 'includes/admin/pages/log-page.php';
require_once SPAR_PLUGIN_PATH . 'includes/admin/pages/spins-log-page.php';
require_once SPAR_PLUGIN_PATH . 'includes/admin/pages/reports-page.php';
require_once SPAR_PLUGIN_PATH . 'includes/admin/pages/customer-detail-page.php';
require_once SPAR_PLUGIN_PATH . 'includes/migration/migration.php';
// Lightweight analytics helpers for admin header snapshot
require_once SPAR_PLUGIN_PATH . 'includes/admin/functions-analytics.php';
add_action( 'admin_menu', 'spar_register_admin_menus' );
function spar_register_admin_menus() {
    $options = get_option( 'spar_options', spar_settings_default() );
    $rewards_label = $options['rewards_label'] ?? 'Rewards';
    /* translators: %s: Rewards label */
    $vouchers_title = sprintf( esc_html__( '%s Vouchers', 'simple-points-and-rewards' ), esc_html( $rewards_label ) );
    add_menu_page(
        'Simple Points & Rewards',
        'Points & Rewards',
        'manage_woocommerce',
        'spar-settings',
        'spar_settings_page',
        'dashicons-tickets',
        58
    );
    add_submenu_page(
        'spar-settings',
        'Settings',
        'Settings',
        'manage_woocommerce',
        'spar-settings',
        'spar_settings_page',
        0
    );
    add_submenu_page(
        'spar-settings',
        esc_html__( 'Customer Points', 'simple-points-and-rewards' ),
        esc_html__( 'Customer Points', 'simple-points-and-rewards' ),
        'manage_woocommerce',
        'spar-customer-points',
        'spar_customer_points_admin_page_callback',
        25
    );
    add_submenu_page(
        null,
        esc_html__( 'Customer Detail', 'simple-points-and-rewards' ),
        esc_html__( 'Customer Detail', 'simple-points-and-rewards' ),
        'manage_woocommerce',
        'spar-customer',
        'spar_customer_detail_admin_page_callback'
    );
    if ( spar_fs()->can_use_premium_code__premium_only() && function_exists( 'spar_is_guest_tracking_enabled' ) && spar_is_guest_tracking_enabled() ) {
        add_submenu_page(
            'spar-settings',
            esc_html__( 'Guest Points', 'simple-points-and-rewards' ),
            esc_html__( 'Guest Points', 'simple-points-and-rewards' ),
            'manage_woocommerce',
            'spar-guest-points',
            'spar_guest_points_admin_page_callback',
            26
        );
    }
    // Visual submenu separator (non-clickable) between management pages and reporting
    add_submenu_page(
        'spar-settings',
        esc_html__( 'Separator', 'simple-points-and-rewards' ),
        '──────────',
        'manage_woocommerce',
        'spar-separator-1',
        '__return_null',
        35
    );
    add_submenu_page(
        'spar-settings',
        $vouchers_title . esc_html__( ' Coupons', 'simple-points-and-rewards' ),
        $vouchers_title,
        'manage_woocommerce',
        'spar-reward-vouchers',
        'spar_reward_vouchers_admin_page_callback',
        20
    );
    // Visual submenu separator (non-clickable) between management pages and reporting
    add_submenu_page(
        'spar-settings',
        esc_html__( 'Separator', 'simple-points-and-rewards' ),
        '──────────',
        'manage_woocommerce',
        'spar-separator-1',
        '__return_null',
        35
    );
    // Activity Log moved lower and retitled
    add_submenu_page(
        'spar-settings',
        esc_html__( 'Points Activity Log', 'simple-points-and-rewards' ),
        esc_html__( 'Points Activity Log', 'simple-points-and-rewards' ),
        'manage_woocommerce',
        'spar-log',
        'spar_points_log_page',
        40
    );
    add_submenu_page(
        'spar-settings',
        esc_html__( 'Reports & Analytics', 'simple-points-and-rewards' ),
        esc_html__( 'Reports & Analytics', 'simple-points-and-rewards' ),
        'manage_woocommerce',
        'spar-reports',
        'spar_reports_admin_page',
        50
    );
    add_submenu_page(
        'spar-settings',
        esc_html__( 'Referral Link Clicks', 'simple-points-and-rewards' ),
        esc_html__( 'Referral Link Clicks', 'simple-points-and-rewards' ),
        'manage_woocommerce',
        'spar-referrals',
        'spar_referrals_admin_page',
        45
    );
    // Visual submenu separator (non-clickable) between management pages and reporting
    add_submenu_page(
        'spar-settings',
        esc_html__( 'Separator', 'simple-points-and-rewards' ),
        '──────────',
        'manage_woocommerce',
        'spar-separator-1',
        '__return_null',
        35
    );
}

add_action( 'admin_enqueue_scripts', 'spar_enqueue_admin_assets' );
// Enqueue frontend assets late so plugin styles override theme styles.
add_action( 'wp_enqueue_scripts', 'spar_enqueue_frontend_assets', 120 );
// Redirect legacy (non-prefixed) admin page slugs to new prefixed ones.
add_action( 'admin_init', 'spar_legacy_admin_slug_redirects' );
function spar_legacy_admin_slug_redirects() {
    $page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
    if ( !$page ) {
        return;
    }
    // Legacy rewards options page now lives under Settings > Rewards tab.
    if ( $page === 'reward-options' ) {
        wp_safe_redirect( admin_url( 'admin.php?page=spar-settings&tab=rewards' ) );
        exit;
    }
    $map = array(
        'customer-points'  => 'spar-customer-points',
        'reward-vouchers'  => 'spar-reward-vouchers',
        'referral-coupons' => 'spar-referral-coupons',
    );
    if ( isset( $map[$page] ) ) {
        wp_safe_redirect( admin_url( 'admin.php?page=' . $map[$page] ) );
        exit;
    }
}

function spar_enqueue_admin_assets(  $hook  ) {
    // General plugin styles for admin
    wp_enqueue_style(
        'spar-styles',
        SPAR_PLUGIN_URL . 'assets/css/reward-points.css',
        [],
        '1.0'
    );
    // Check if we're on any of our admin pages
    $our_pages = array(
        'spar-settings',
        'spar-log',
        'spar-reports',
        'spar-reward-vouchers',
        'spar-customer-points',
        'spar-customer',
        'reward-options',
        'spar-referral-coupons',
        'spar-referrals',
        'spar-migration'
    );
    // Include Spins Activity Log page only when the Prize Wheel is enabled.
    $spar_enqueue_all_opts = get_option( 'spar_options', [] );
    if ( !empty( $spar_enqueue_all_opts['earn']['spin_wheel']['enabled'] ) ) {
        $our_pages[] = 'spar-spins-log';
    }
    // Include Guest Points page when guest tracking is available and enabled (PRO).
    if ( function_exists( 'spar_is_guest_tracking_enabled' ) && spar_is_guest_tracking_enabled() ) {
        $our_pages[] = 'spar-guest-points';
    }
    $is_our_page = false;
    // Read-only page param for UI decisions
    $current_page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
    foreach ( $our_pages as $page ) {
        if ( strpos( $hook, $page ) !== false || $current_page && $current_page === $page ) {
            $is_our_page = true;
            break;
        }
    }
    // Enqueue admin header styles on all our admin pages
    if ( $is_our_page ) {
        wp_enqueue_style(
            'spar-admin-header',
            SPAR_PLUGIN_URL . 'assets/css/admin-header.css',
            ['spar-styles'],
            '1.0'
        );
    }
    $asset_version = ( defined( 'SPAR_VERSION' ) ? SPAR_VERSION : '1.0' );
    // Settings page specific assets
    if ( strpos( $hook, 'spar-settings' ) !== false || $current_page && $current_page === 'spar-settings' ) {
        // Enqueue admin settings CSS and JS
        wp_enqueue_style(
            'spar-font-awesome',
            SPAR_PLUGIN_URL . 'assets/fonts/font-awesome/css/all.min.css',
            [],
            $asset_version
        );
        wp_enqueue_style(
            'spar-admin-settings',
            SPAR_PLUGIN_URL . 'assets/css/admin-settings.css',
            ['spar-font-awesome'],
            $asset_version
        );
        wp_enqueue_style(
            'spar-spin-wheel-admin',
            SPAR_PLUGIN_URL . 'premium-files/assets/css/spin-wheel.css',
            ['spar-admin-settings'],
            $asset_version
        );
        wp_enqueue_script(
            'spar-admin-settings',
            SPAR_PLUGIN_URL . 'assets/js/admin-settings.js',
            ['jquery'],
            $asset_version,
            true
        );
        // Sortable for Dashboard Customisation tab and Earn tab display order.
        wp_enqueue_script( 'jquery-ui-sortable' );
        wp_enqueue_script(
            'spar-admin-settings-dashboard-tab',
            SPAR_PLUGIN_URL . 'assets/js/admin-settings-dashboard-tab.js',
            ['jquery', 'jquery-ui-sortable'],
            $asset_version,
            true
        );
        wp_enqueue_script(
            'spar-admin-settings-earn-tab',
            SPAR_PLUGIN_URL . 'assets/js/admin-settings-earn-tab.js',
            ['jquery', 'jquery-ui-sortable', 'spar-admin-settings'],
            $asset_version,
            true
        );
        // Ensure SelectWoo (Select2) and styles are available locally via WooCommerce (no external CDNs)
        if ( function_exists( 'WC' ) ) {
            $wc_url = trailingslashit( WC()->plugin_url() );
            if ( !wp_script_is( 'selectWoo', 'registered' ) ) {
                wp_register_script(
                    'selectWoo',
                    $wc_url . 'assets/js/selectWoo/selectWoo.full.min.js',
                    array('jquery'),
                    ( defined( 'WC_VERSION' ) ? WC_VERSION : '1.0' ),
                    true
                );
            }
            if ( !wp_style_is( 'select2', 'registered' ) ) {
                wp_register_style(
                    'select2',
                    $wc_url . 'assets/css/select2.css',
                    array(),
                    ( defined( 'WC_VERSION' ) ? WC_VERSION : '1.0' )
                );
            }
            // WooCommerce-style tooltips.
            if ( !wp_script_is( 'jquery-tiptip', 'registered' ) ) {
                wp_register_script(
                    'jquery-tiptip',
                    $wc_url . 'assets/js/jquery-tiptip/jquery.tipTip.min.js',
                    array('jquery'),
                    ( defined( 'WC_VERSION' ) ? WC_VERSION : '1.0' ),
                    true
                );
            }
            if ( wp_script_is( 'selectWoo', 'registered' ) ) {
                wp_enqueue_script( 'selectWoo' );
            }
            if ( wp_style_is( 'select2', 'registered' ) ) {
                wp_enqueue_style( 'select2' );
            }
            if ( wp_script_is( 'jquery-tiptip', 'registered' ) ) {
                wp_enqueue_script( 'jquery-tiptip' );
            }
            if ( wp_style_is( 'woocommerce_admin_styles', 'registered' ) ) {
                wp_enqueue_style( 'woocommerce_admin_styles' );
            }
        }
        // Localize script data for premium status and ajax nonces
        wp_localize_script( 'spar-admin-settings', 'sparSettings', [
            'ajaxUrl'             => admin_url( 'admin-ajax.php' ),
            'nonce'               => wp_create_nonce( 'spar_admin_nonce' ),
            'settingsNonce'       => wp_create_nonce( 'spar_settings_nonce' ),
            'pageLinkNonce'       => wp_create_nonce( 'spar_retrieve_page_link' ),
            'createPageNonce'     => wp_create_nonce( 'spar_generate_rewards_page' ),
            'listPagesNonce'      => wp_create_nonce( 'spar_list_rewards_pages' ),
            'couponEditLinkNonce' => wp_create_nonce( 'spar_get_coupon_edit_link' ),
            'generateTermsNonce'  => wp_create_nonce( 'spar_generate_terms' ),
            'strings'             => [
                'saving'             => esc_html__( 'Saving...', 'simple-points-and-rewards' ),
                'saved'              => esc_html__( 'Saved!', 'simple-points-and-rewards' ),
                'error'              => esc_html__( 'Error saving option', 'simple-points-and-rewards' ),
                'missingNonce'       => esc_html__( 'Security token missing', 'simple-points-and-rewards' ),
                'generatingTerms'    => esc_html__( 'Generating...', 'simple-points-and-rewards' ),
                'generateTerms'      => esc_html__( 'Generate', 'simple-points-and-rewards' ),
                'generateTermsError' => esc_html__( 'Failed to generate terms. Please try again.', 'simple-points-and-rewards' ),
            ],
        ] );
        // Enqueue tab-specific scripts (file-based, replacing previous inline blocks)
        $settings_dep = 'spar-admin-settings';
        if ( !wp_script_is( 'spar-admin-settings', 'registered' ) ) {
            $settings_dep = ( wp_script_is( 'spar-admin-settings-fallback', 'registered' ) ? 'spar-admin-settings-fallback' : 'jquery' );
        }
        // Build deps for rewards tab script, making selectWoo optional to avoid blocking enqueue when not registered
        $rewards_deps = ['jquery', $settings_dep];
        if ( wp_script_is( 'selectWoo', 'registered' ) ) {
            $rewards_deps[] = 'selectWoo';
        }
        wp_enqueue_script(
            'spar-admin-settings-rewards-tab',
            SPAR_PLUGIN_URL . 'assets/js/admin-settings-rewards-tab.js',
            $rewards_deps,
            '1.0',
            true
        );
        wp_localize_script( 'spar-admin-settings-rewards-tab', 'sparRewardsTab', [
            'ajaxUrl'                   => admin_url( 'admin-ajax.php' ),
            'searchProductsNonce'       => wp_create_nonce( 'spar_search_products' ),
            'couponEditLinkNonce'       => wp_create_nonce( 'spar_get_coupon_edit_link' ),
            'createTemplateCouponNonce' => wp_create_nonce( 'spar_create_template_coupon' ),
            'i18n'                      => [
                'newReward'                => esc_html__( 'New Reward', 'simple-points-and-rewards' ),
                'edit'                     => esc_html__( 'Edit', 'simple-points-and-rewards' ),
                'close'                    => esc_html__( 'Close', 'simple-points-and-rewards' ),
                'delete'                   => esc_html__( 'Delete', 'simple-points-and-rewards' ),
                'confirmDelete'            => esc_html__( 'Are you sure you want to delete this reward?', 'simple-points-and-rewards' ),
                'untitledReward'           => esc_html__( 'Untitled Reward', 'simple-points-and-rewards' ),
                'rewardName'               => esc_html__( 'Reward Name:', 'simple-points-and-rewards' ),
                'rewardNamePlaceholder'    => esc_html__( '$10 Off Voucher', 'simple-points-and-rewards' ),
                'pointsRequired'           => esc_html__( 'Points Required:', 'simple-points-and-rewards' ),
                'rewardType'               => esc_html__( 'Reward Type:', 'simple-points-and-rewards' ),
                'voucher'                  => esc_html__( 'Discount Voucher/Coupon', 'simple-points-and-rewards' ),
                'product'                  => esc_html__( 'Free Product', 'simple-points-and-rewards' ),
                'status'                   => esc_html__( 'Status:', 'simple-points-and-rewards' ),
                'active'                   => esc_html__( 'Active', 'simple-points-and-rewards' ),
                'inactive'                 => esc_html__( 'Inactive', 'simple-points-and-rewards' ),
                'voucherSettings'          => esc_html__( 'Voucher Settings', 'simple-points-and-rewards' ),
                'voucherAmount'            => esc_html__( 'Voucher Amount:', 'simple-points-and-rewards' ),
                'discountType'             => esc_html__( 'Discount Type:', 'simple-points-and-rewards' ),
                'fixedAmount'              => esc_html__( 'Fixed Amount', 'simple-points-and-rewards' ),
                'percentage'               => esc_html__( 'Percentage', 'simple-points-and-rewards' ),
                'freeShipping'             => esc_html__( 'Free Shipping', 'simple-points-and-rewards' ),
                'productSettings'          => esc_html__( 'Product Settings', 'simple-points-and-rewards' ),
                'productLabel'             => esc_html__( 'Product:', 'simple-points-and-rewards' ),
                'productHelp'              => esc_html__( 'Search and select a WooCommerce product for the free product reward', 'simple-points-and-rewards' ),
                'expiryDate'               => esc_html__( 'Expiry Date:', 'simple-points-and-rewards' ),
                'searchProduct'            => esc_html__( 'Search for a product...', 'simple-points-and-rewards' ),
                'templateCoupon'           => esc_html__( 'Template Coupon (optional):', 'simple-points-and-rewards' ),
                'templateCouponNone'       => esc_html__( 'No template – use basic settings above', 'simple-points-and-rewards' ),
                'templateCouponHelp'       => esc_html__( 'Select an existing coupon to use as a template. All coupon settings (minimum spend, product restrictions, usage limits, etc) will be copied to the generated voucher. Discount type, amount, shipping, expiry, and customer email are always set by the reward.', 'simple-points-and-rewards' ),
                'templateCouponNoSelected' => esc_html__( 'No template selected.', 'simple-points-and-rewards' ),
                'templateCouponLoading'    => esc_html__( 'Loading…', 'simple-points-and-rewards' ),
                'templateCouponError'      => esc_html__( 'Error.', 'simple-points-and-rewards' ),
                'createNewTemplate'        => esc_html__( 'Create New Template', 'simple-points-and-rewards' ),
                'creatingTemplate'         => esc_html__( 'Creating…', 'simple-points-and-rewards' ),
                'enableTemplateCoupon'     => esc_html__( 'Enable Template Coupon', 'simple-points-and-rewards' ),
            ],
        ] );
        wp_enqueue_script(
            'spar-admin-settings-levels-tab',
            SPAR_PLUGIN_URL . 'assets/js/admin-settings-levels-tab.js',
            ['jquery', 'spar-admin-settings', 'jquery-ui-sortable'],
            '1.0',
            true
        );
        $badge_icons = ( function_exists( 'spar_get_badge_icons' ) ? spar_get_badge_icons() : [] );
        wp_localize_script( 'spar-admin-settings-levels-tab', 'sparLevelsTab', [
            'isPro'          => spar_fs()->can_use_premium_code__premium_only(),
            'badgeIcons'     => $badge_icons,
            'earnTypeLabels' => ( function_exists( 'spar_get_earn_type_labels' ) ? spar_get_earn_type_labels() : [] ),
            'defaults'       => [
                'subject' => spar_get_default_level_email_subject(),
                'body'    => spar_get_default_level_email_body(),
            ],
            'i18n'           => [
                'newLevel'                     => esc_html__( 'New Level', 'simple-points-and-rewards' ),
                'edit'                         => esc_html__( 'Edit', 'simple-points-and-rewards' ),
                'close'                        => esc_html__( 'Close', 'simple-points-and-rewards' ),
                'delete'                       => esc_html__( 'Delete', 'simple-points-and-rewards' ),
                'confirmDelete'                => esc_html__( 'Are you sure you want to delete this level?', 'simple-points-and-rewards' ),
                'untitledLevel'                => esc_html__( 'Untitled Level', 'simple-points-and-rewards' ),
                'levelName'                    => esc_html__( 'Level Name:', 'simple-points-and-rewards' ),
                'levelNamePlaceholder'         => esc_html__( 'Bronze Member', 'simple-points-and-rewards' ),
                'requiredPoints'               => esc_html__( 'Required Points:', 'simple-points-and-rewards' ),
                'badgeIcon'                    => esc_html__( 'Badge Icon:', 'simple-points-and-rewards' ),
                'selectIcon'                   => esc_html__( 'Select an icon', 'simple-points-and-rewards' ),
                'customBadgeUrl'               => esc_html__( 'Custom Badge (URL):', 'simple-points-and-rewards' ),
                'customBadgeHelp'              => esc_html__( 'Provide an image URL that will be used when Custom (Image URL) is selected.', 'simple-points-and-rewards' ),
                'customBadgeUrlHelp'           => esc_html__( 'Provide an image URL that will be used when Custom (Image URL) is selected.', 'simple-points-and-rewards' ),
                'customOptionUrl'              => esc_html__( 'Custom (Image URL)', 'simple-points-and-rewards' ),
                'customOptionText'             => esc_html__( 'Custom (Text/Emoji)', 'simple-points-and-rewards' ),
                'customBadgeText'              => esc_html__( 'Custom Badge (Text/Emoji):', 'simple-points-and-rewards' ),
                'customBadgeTextHelp'          => esc_html__( 'Shown instead of the preset icon when Custom (Text/Emoji) is selected.', 'simple-points-and-rewards' ),
                'customBadgeTextPlaceholder'   => esc_html__( 'e.g. VIP or 😎', 'simple-points-and-rewards' ),
                'levelBenefits'                => esc_html__( 'Level Benefits', 'simple-points-and-rewards' ),
                'pointsMultiplier'             => esc_html__( 'Points Multiplier:', 'simple-points-and-rewards' ),
                'pointsMultiplierHelp'         => esc_html__( 'Multiply all earned points by this amount (e.g., 1.5 = 50% bonus on all activities)', 'simple-points-and-rewards' ),
                'customBenefits'               => esc_html__( 'Custom Benefits:', 'simple-points-and-rewards' ),
                'customBenefitsPlaceholder'    => esc_html__( 'Enter one benefit per line...', 'simple-points-and-rewards' ),
                'customBenefitsHelp'           => esc_html__( 'Enter one benefit per line. These will be displayed to customers alongside the automatic benefits.', 'simple-points-and-rewards' ),
                'sendLevelEmail'               => esc_html__( 'Send email to customer on level up', 'simple-points-and-rewards' ),
                'levelEmailHelp'               => esc_html__( 'Email is sent as soon as the customer reaches this level.', 'simple-points-and-rewards' ),
                'customiseEmail'               => esc_html__( 'Customise Email', 'simple-points-and-rewards' ),
                'emailSubject'                 => esc_html__( 'Subject:', 'simple-points-and-rewards' ),
                'emailBody'                    => esc_html__( 'Email Body:', 'simple-points-and-rewards' ),
                'levelEmailSubjectPlaceholder' => esc_html__( 'Congratulations! You reached {level_name}', 'simple-points-and-rewards' ),
                'levelEmailPlaceholders'       => esc_html__( 'Available placeholders: {user_name}, {user_email}, {level_name}, {previous_level_name}, {points_label}, {total_points}, {site_name}, {site_url}, {rewards_url}', 'simple-points-and-rewards' ),
                'customMultipliers'            => esc_html__( 'Use different multipliers per earn type', 'simple-points-and-rewards' ),
                'customMultipliersHelp'        => esc_html__( 'Override the global multiplier above with a unique multiplier for each way to earn points.', 'simple-points-and-rewards' ),
                'earnMultipliersBlankHelp'     => esc_html__( 'Leave blank to use the global multiplier for that earn type.', 'simple-points-and-rewards' ),
                'pointsMultiplierTitle'        => esc_html__( 'Points Multiplier', 'simple-points-and-rewards' ),
                'customBenefitsTitle'          => esc_html__( 'Custom Benefits', 'simple-points-and-rewards' ),
                'levelUpEmailTitle'            => esc_html__( 'Level Up Email', 'simple-points-and-rewards' ),
            ],
        ] );
        // Conditional Rules (PRO) under Ways to Earn.
        if ( function_exists( 'spar_fs' ) && spar_fs()->can_use_premium_code__premium_only() ) {
            $cr_deps = ['jquery', 'spar-admin-settings'];
            if ( wp_script_is( 'selectWoo', 'registered' ) ) {
                $cr_deps[] = 'selectWoo';
            }
            if ( wp_script_is( 'jquery-tiptip', 'registered' ) ) {
                $cr_deps[] = 'jquery-tiptip';
            }
            wp_enqueue_script(
                'spar-admin-conditional-rules',
                SPAR_PLUGIN_URL . 'assets/js/admin-conditional-rules.js',
                $cr_deps,
                $asset_version,
                true
            );
            wp_localize_script( 'spar-admin-conditional-rules', 'sparConditionalRules', [
                'ajaxUrl'               => admin_url( 'admin-ajax.php' ),
                'searchProductsNonce'   => wp_create_nonce( 'spar_search_products' ),
                'searchCategoriesNonce' => wp_create_nonce( 'spar_cr_search_categories' ),
                'searchCustomersNonce'  => wp_create_nonce( 'spar_cr_search_customers' ),
                'i18n'                  => [
                    'ruleTitle'     => esc_html__( 'Conditional Rule', 'simple-points-and-rewards' ),
                    'edit'          => esc_html__( 'Edit', 'simple-points-and-rewards' ),
                    'close'         => esc_html__( 'Close', 'simple-points-and-rewards' ),
                    'delete'        => esc_html__( 'Delete', 'simple-points-and-rewards' ),
                    'confirmDelete' => esc_html__( 'Are you sure you want to delete this rule?', 'simple-points-and-rewards' ),
                ],
            ] );
        }
        // Email preview functionality
        wp_enqueue_script(
            'spar-admin-email',
            SPAR_PLUGIN_URL . 'assets/js/admin-email.js',
            ['jquery'],
            '1.0',
            true
        );
        wp_localize_script( 'spar-admin-email', 'sparEmail', [
            'ajaxUrl' => admin_url( 'admin-ajax.php' ),
            'nonce'   => wp_create_nonce( 'spar_preview_email' ),
        ] );
        // Also enqueue the admin styles and scripts that were being loaded separately
        wp_enqueue_script(
            'spar-admin-scripts',
            SPAR_PLUGIN_URL . 'assets/js/admin.js',
            ['jquery'],
            '1.0',
            true
        );
    }
    // Points Activity Log and Spins Activity Log page specific assets
    $spar_spins_log_enqueue_pages = ['spar-log'];
    if ( !empty( $spar_enqueue_all_opts['earn']['spin_wheel']['enabled'] ) ) {
        $spar_spins_log_enqueue_pages[] = 'spar-spins-log';
    }
    if ( strpos( $hook, 'spar-log' ) !== false || $current_page && in_array( $current_page, $spar_spins_log_enqueue_pages, true ) ) {
        wp_enqueue_style(
            'spar-log-styles',
            SPAR_PLUGIN_URL . 'assets/css/admin-log.css',
            ['spar-styles'],
            '1.0'
        );
    }
    // Reports page specific assets
    if ( strpos( $hook, 'spar-reports' ) !== false || $current_page && $current_page === 'spar-reports' ) {
        // Font Awesome for icons
        wp_enqueue_style(
            'spar-font-awesome',
            SPAR_PLUGIN_URL . 'assets/fonts/font-awesome/css/all.min.css',
            [],
            '6.6.0'
        );
        // Bump version to ensure latest CSS (filters one-line fix) is loaded
        wp_enqueue_style(
            'spar-admin-reports',
            SPAR_PLUGIN_URL . 'assets/css/admin-reports.css',
            ['spar-styles'],
            '1.3'
        );
    }
    // Reward options page specific assets
    if ( strpos( $hook, 'reward-options' ) !== false || $current_page && $current_page === 'reward-options' ) {
        wp_enqueue_style(
            'spar-admin-tables',
            SPAR_PLUGIN_URL . 'assets/css/admin-tables.css',
            ['spar-styles'],
            '1.0'
        );
        wp_enqueue_script(
            'spar-admin-tables',
            SPAR_PLUGIN_URL . 'assets/js/admin-tables.js',
            ['jquery'],
            '1.0',
            true
        );
    }
    // Customer points page specific assets
    if ( strpos( $hook, 'spar-customer-points' ) !== false || $current_page && $current_page === 'spar-customer-points' ) {
        wp_enqueue_style(
            'spar-admin-tables',
            SPAR_PLUGIN_URL . 'assets/css/admin-tables.css',
            ['spar-styles'],
            '1.0'
        );
        wp_enqueue_script(
            'spar-admin-tables',
            SPAR_PLUGIN_URL . 'assets/js/admin-tables.js',
            ['jquery'],
            '1.0',
            true
        );
    }
    // Reward vouchers page specific assets
    if ( strpos( $hook, 'spar-reward-vouchers' ) !== false || $current_page && $current_page === 'spar-reward-vouchers' ) {
        wp_enqueue_style(
            'spar-admin-tables',
            SPAR_PLUGIN_URL . 'assets/css/admin-tables.css',
            ['spar-styles'],
            '1.0'
        );
        wp_enqueue_script(
            'spar-admin-tables',
            SPAR_PLUGIN_URL . 'assets/js/admin-tables.js',
            ['jquery'],
            '1.0',
            true
        );
    }
    if ( strpos( $hook, 'spar-referral-coupons' ) !== false || $current_page && $current_page === 'spar-referral-coupons' ) {
        wp_enqueue_style(
            'spar-admin-tables',
            SPAR_PLUGIN_URL . 'assets/css/admin-tables.css',
            ['spar-styles'],
            '1.0'
        );
        wp_enqueue_script(
            'spar-admin-tables',
            SPAR_PLUGIN_URL . 'assets/js/admin-tables.js',
            ['jquery'],
            '1.0',
            true
        );
    }
    if ( strpos( $hook, 'spar-migration' ) !== false || $current_page && $current_page === 'spar-migration' ) {
        wp_enqueue_style(
            'spar-admin-migration',
            SPAR_PLUGIN_URL . 'assets/css/admin-migration.css',
            array('spar-styles'),
            SPAR_VERSION
        );
        wp_enqueue_script(
            'spar-admin-migration',
            SPAR_PLUGIN_URL . 'assets/js/admin-migration.js',
            ['jquery'],
            SPAR_VERSION,
            true
        );
        $active_plugins = ( function_exists( 'spar_migration_get_active_plugins' ) ? spar_migration_get_active_plugins() : array() );
        $plugin_labels = array();
        foreach ( $active_plugins as $key => $data ) {
            $label = ( isset( $data['label'] ) ? $data['label'] : $key );
            $plugin_labels[$key] = $label;
        }
        wp_localize_script( 'spar-admin-migration', 'sparMigration', array(
            'ajaxUrl'   => admin_url( 'admin-ajax.php' ),
            'nonce'     => wp_create_nonce( 'spar_migration_nonce' ),
            'batchSize' => ( function_exists( 'spar_migration_get_batch_size' ) ? spar_migration_get_batch_size() : 10 ),
            'plugins'   => $plugin_labels,
            'supports'  => array(
                'userStatus' => ( function_exists( 'spar_migration_get_user_status_supported_plugins' ) ? array_values( spar_migration_get_user_status_supported_plugins() ) : array() ),
            ),
            'strings'   => array(
                'selectPlugin' => esc_html__( 'Please select a plugin before starting the migration.', 'simple-points-and-rewards' ),
                'starting'     => esc_html__( 'Starting migration from %1$s using %2$s mode.', 'simple-points-and-rewards' ),
                'completed'    => esc_html__( 'Migration completed. Please review and verify the amounts.', 'simple-points-and-rewards' ),
                'failed'       => esc_html__( 'Migration request failed: %s', 'simple-points-and-rewards' ),
                'modeAdd'      => esc_html__( 'add', 'simple-points-and-rewards' ),
                'modeOverride' => esc_html__( 'override', 'simple-points-and-rewards' ),
                'genericError' => esc_html__( 'Unexpected response from the server.', 'simple-points-and-rewards' ),
            ),
        ) );
    }
}

function spar_enqueue_frontend_assets() {
    $options = get_option( 'spar_options', [] );
    // Determine if the current page needs plugin frontend assets.
    $on_rewards_page = is_account_page() || function_exists( 'spar_page_has_rewards_shortcode' ) && spar_page_has_rewards_shortcode();
    $on_shop_page = function_exists( 'is_shop' ) && is_shop() || function_exists( 'is_product_taxonomy' ) && is_product_taxonomy() || is_post_type_archive( 'product' ) || is_search() && isset( $_GET['post_type'] ) && 'product' === sanitize_text_field( wp_unslash( $_GET['post_type'] ) );
    $needs_assets = $on_rewards_page || is_product() || is_cart() || is_checkout() || $on_shop_page;
    if ( !$needs_assets ) {
        return;
    }
    // General plugin styles — only load on pages that use them.
    wp_enqueue_style(
        'spar-styles',
        SPAR_PLUGIN_URL . 'assets/css/reward-points.css',
        [],
        '1.0'
    );
    // Add dynamic CSS variables for theme colors (sanitize to valid hex colors).
    $theme_color_1_raw = ( isset( $options['rewards_theme_color_1'] ) && is_string( $options['rewards_theme_color_1'] ) && $options['rewards_theme_color_1'] !== '' ? $options['rewards_theme_color_1'] : '#667eea' );
    $theme_color_2_raw = ( isset( $options['rewards_theme_color_2'] ) && is_string( $options['rewards_theme_color_2'] ) && $options['rewards_theme_color_2'] !== '' ? $options['rewards_theme_color_2'] : '#764ba2' );
    $theme_accent_color_raw = ( isset( $options['rewards_theme_accent_color'] ) && is_string( $options['rewards_theme_accent_color'] ) && $options['rewards_theme_accent_color'] !== '' ? $options['rewards_theme_accent_color'] : '#2ca58d' );
    $theme_color_1 = ( sanitize_hex_color( $theme_color_1_raw ) ?: '#667eea' );
    $theme_color_2 = ( sanitize_hex_color( $theme_color_2_raw ) ?: '#764ba2' );
    $theme_accent_color = ( sanitize_hex_color( $theme_accent_color_raw ) ?: '#2ca58d' );
    $vars_css = ':root{--spar-theme-color-1:' . $theme_color_1 . ';--spar-theme-color-2:' . $theme_color_2 . ';--spar-theme-accent-color:' . $theme_accent_color . ';}';
    wp_add_inline_style( 'spar-styles', $vars_css );
    // Redeemed vouchers — only needed on account/shortcode pages, cart, and checkout.
    if ( $on_rewards_page || is_cart() || is_checkout() ) {
        wp_register_script(
            'spar-redeemed-vouchers',
            SPAR_PLUGIN_URL . 'assets/js/redeemed-vouchers.js',
            [],
            '1.0',
            true
        );
        wp_register_style(
            'spar-redeemed-vouchers',
            SPAR_PLUGIN_URL . 'assets/css/redeemed-vouchers.css',
            [],
            '1.0'
        );
        wp_localize_script( 'spar-redeemed-vouchers', 'sparVouchers', [
            'ajaxUrl' => admin_url( 'admin-ajax.php' ),
            'nonce'   => wp_create_nonce( 'spar_apply_voucher' ),
            'cartUrl' => ( function_exists( 'wc_get_cart_url' ) ? wc_get_cart_url() : '' ),
            'i18n'    => [
                'applying'    => esc_html__( 'Applying...', 'simple-points-and-rewards' ),
                'applied'     => esc_html__( 'Applied!', 'simple-points-and-rewards' ),
                'saved'       => esc_html__( 'Saved for later', 'simple-points-and-rewards' ),
                'deferredMsg' => esc_html__( "We'll apply your voucher as soon as you add products to your cart.", 'simple-points-and-rewards' ),
                'error'       => esc_html__( 'Could not apply voucher. Please try again.', 'simple-points-and-rewards' ),
            ],
        ] );
        wp_enqueue_style( 'spar-redeemed-vouchers' );
        wp_enqueue_script( 'spar-redeemed-vouchers' );
    }
    // Enqueue My Account / shortcode page assets.
    $should_enqueue = $on_rewards_page;
    // Register scripts and styles so they can be enqueued late if needed.
    wp_register_style(
        'spar-account-rewards',
        SPAR_PLUGIN_URL . 'assets/css/account-rewards.css',
        ['spar-styles'],
        '1.0'
    );
    wp_register_style(
        'spar-dashboard-dark-mode',
        SPAR_PLUGIN_URL . 'assets/css/dashboard-dark-mode.css',
        ['spar-account-rewards'],
        '1.0'
    );
    wp_register_style(
        'spar-referral-system-css',
        SPAR_PLUGIN_URL . 'assets/css/referral-system.css',
        ['spar-styles', 'spar-font-awesome'],
        '1.0'
    );
    wp_register_script(
        'spar-referral-system',
        SPAR_PLUGIN_URL . 'assets/js/referral-system.js',
        ['jquery'],
        time(),
        true
    );
    wp_register_script(
        'spar-rewards-tabs',
        SPAR_PLUGIN_URL . 'assets/js/rewards-tabs.js',
        [],
        '1.0',
        true
    );
    if ( $should_enqueue ) {
        // Localize script for referral system — only needed on account/shortcode pages.
        $referral_options = ( isset( $options['earn']['referral'] ) && is_array( $options['earn']['referral'] ) ? $options['earn']['referral'] : [] );
        $social_sharing = ( isset( $options['earn']['social_sharing'] ) && is_array( $options['earn']['social_sharing'] ) ? $options['earn']['social_sharing'] : [] );
        $tracking_mode = ( isset( $referral_options['coupon_tracking_mode'] ) ? $referral_options['coupon_tracking_mode'] : 'cookie_required' );
        $offer_enabled = !empty( $referral_options['offer_enabled'] );
        $referral_offer_enabled = $offer_enabled && in_array( $tracking_mode, ['always_track', 'flexible'], true );
        $social_points_enabled = !empty( $social_sharing['referral_social_points_enabled'] );
        $social_points = ( isset( $social_sharing['social_points'] ) ? (int) $social_sharing['social_points'] : 10 );
        $social_limit_mode = ( isset( $social_sharing['social_limit_mode'] ) ? (string) $social_sharing['social_limit_mode'] : 'per_social' );
        wp_localize_script( 'spar-referral-system', 'sparReferralSystem', [
            'strings'              => [
                'copy'      => esc_html__( 'Copy', 'simple-points-and-rewards' ),
                'copied'    => esc_html__( 'Copied!', 'simple-points-and-rewards' ),
                'copyError' => esc_html__( 'Copy Failed', 'simple-points-and-rewards' ),
            ],
            'ajaxurl'              => admin_url( 'admin-ajax.php' ),
            'referralNonce'        => wp_create_nonce( 'spar_referral_nonce' ),
            'referralOfferEnabled' => (bool) $referral_offer_enabled,
            'socialShare'          => [
                'enabled'   => (bool) $social_points_enabled,
                'points'    => $social_points,
                'limitMode' => $social_limit_mode,
                'nonce'     => wp_create_nonce( 'spar_social_share_nonce' ),
            ],
        ] );
        // Enqueue Font Awesome for better social icons
        wp_enqueue_style(
            'spar-font-awesome',
            SPAR_PLUGIN_URL . 'assets/fonts/font-awesome/css/all.min.css',
            [],
            '6.6.0'
        );
        // Account rewards styles (static file)
        wp_enqueue_style( 'spar-account-rewards' );
        wp_enqueue_style( 'spar-dashboard-dark-mode' );
        wp_enqueue_style( 'spar-referral-system-css' );
        wp_enqueue_script( 'spar-referral-system' );
        // Rewards tabs behavior
        wp_enqueue_script( 'spar-rewards-tabs' );
        // Localize dark mode settings for dashboard
        $dashboard_dark_mode_toggle = !empty( $options['dashboard_dark_mode_toggle'] );
        $dashboard_dark_mode_default = !empty( $options['dashboard_dark_mode_default'] );
        $dashboard_dark_mode_header = !empty( $options['dashboard_dark_mode_header'] );
        $dashboard_dark_mode_hide_toggle_when_default = !empty( $options['dashboard_dark_mode_hide_toggle_when_default'] );
        wp_localize_script( 'spar-rewards-tabs', 'sparDashboard', [
            'darkModeToggle'                => $dashboard_dark_mode_toggle,
            'darkModeDefault'               => $dashboard_dark_mode_default,
            'darkModeHeader'                => $dashboard_dark_mode_header,
            'darkModeHideToggleWhenDefault' => $dashboard_dark_mode_hide_toggle_when_default,
            'strings'                       => [
                'darkMode'  => esc_html__( 'Enable dark mode', 'simple-points-and-rewards' ),
                'lightMode' => esc_html__( 'Enable light mode', 'simple-points-and-rewards' ),
            ],
        ] );
    }
    // My Account: Confetti and voucher success assets after redeem
    $redeemed_param = filter_input( INPUT_GET, 'redeemed', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
    $redeemed_nonce = filter_input( INPUT_GET, 'redeemed_nonce', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
    if ( $on_rewards_page && !empty( $redeemed_param ) && $redeemed_nonce && wp_verify_nonce( $redeemed_nonce, 'spar_redeemed_notice' ) ) {
        wp_enqueue_script(
            'spar-confetti',
            SPAR_PLUGIN_URL . 'assets/js/confetti/confetti.browser.js',
            [],
            '1.9.3',
            true
        );
        wp_register_script(
            'spar-confetti-runner',
            SPAR_PLUGIN_URL . 'assets/js/confetti-runner.js',
            ['spar-confetti'],
            '1.0',
            true
        );
        wp_enqueue_script( 'spar-confetti-runner' );
        // Voucher success notice styles and script
        wp_enqueue_style(
            'spar-voucher-success',
            SPAR_PLUGIN_URL . 'assets/css/voucher-success.css',
            [],
            '1.0.0'
        );
        wp_enqueue_script(
            'spar-voucher-success',
            SPAR_PLUGIN_URL . 'assets/js/voucher-success.js',
            ['jquery'],
            '1.0.0',
            true
        );
    }
    // My Account: Points history AJAX pagination
    if ( $on_rewards_page ) {
        wp_register_script(
            'spar-points-history',
            SPAR_PLUGIN_URL . 'assets/js/points-history.js',
            ['jquery'],
            '1.0',
            true
        );
        wp_localize_script( 'spar-points-history', 'sparPointsHistory', [
            'ajaxUrl' => admin_url( 'admin-ajax.php' ),
            'nonce'   => wp_create_nonce( 'spar_points_history_nonce' ),
            'i18n'    => [
                'error' => esc_html__( 'Error loading history. Please try again.', 'simple-points-and-rewards' ),
            ],
        ] );
        wp_enqueue_script( 'spar-points-history' );
    }
    // My Account: Referral clicks AJAX pagination
    $show_referral_clicks_log = !empty( $referral_options['show_clicks_log'] );
    if ( $show_referral_clicks_log ) {
        wp_register_script(
            'spar-referral-clicks',
            SPAR_PLUGIN_URL . 'assets/js/referral-clicks.js',
            ['jquery'],
            '1.0',
            true
        );
        wp_localize_script( 'spar-referral-clicks', 'sparReferralClicks', [
            'ajaxUrl' => admin_url( 'admin-ajax.php' ),
            'nonce'   => wp_create_nonce( 'spar_referral_clicks_nonce' ),
            'i18n'    => [
                'error' => esc_html__( 'Error loading referral clicks. Please try again.', 'simple-points-and-rewards' ),
            ],
        ] );
        wp_enqueue_script( 'spar-referral-clicks' );
    }
}

/**
 * Render admin header for plugin pages
 */
function spar_render_admin_header(  $page_title  ) {
    // Include the header template
    $header_template = SPAR_PLUGIN_PATH . 'includes/admin/templates/header.php';
    if ( file_exists( $header_template ) ) {
        include $header_template;
    } else {
        // Fallback if template doesn't exist
        echo '<div class="wrap">';
        echo '<h1>' . esc_html( $page_title ) . '</h1>';
    }
}

add_action( 'show_user_profile', 'spar_user_profile_points_field' );
add_action( 'edit_user_profile', 'spar_user_profile_points_field' );
function spar_user_profile_points_field(  $user  ) {
    if ( !current_user_can( 'edit_users' ) ) {
        return;
    }
    $points = spar_get_user_points( $user->ID );
    $status = get_user_meta( $user->ID, 'spar_user_status', true );
    $allowed_statuses = ['active', 'banned'];
    if ( !in_array( $status, $allowed_statuses, true ) ) {
        $status = 'active';
    }
    wp_nonce_field( 'spar_update_user_points', 'spar_points_nonce' );
    ?>
	<h3><?php 
    esc_html_e( 'Reward Points', 'simple-points-and-rewards' );
    ?></h3>
	<table class="form-table">
		<tr>
			<th><label for="spar_points"><?php 
    esc_html_e( 'Points', 'simple-points-and-rewards' );
    ?></label></th>
			<td><input type="number" name="spar_points" id="spar_points" value="<?php 
    echo esc_attr( $points );
    ?>" /></td>
		</tr>
		<tr>
			<th><label for="spar_total_earned_points"><?php 
    esc_html_e( 'Total Earned Points', 'simple-points-and-rewards' );
    ?></label></th>
			<td>
				<?php 
    $total_earned = ( function_exists( 'spar_get_user_total_points_earned' ) ? spar_get_user_total_points_earned( $user->ID ) : 0 );
    ?>
				<input type="number" name="spar_total_earned_points" id="spar_total_earned_points" min="0" value="<?php 
    echo esc_attr( (int) $total_earned );
    ?>" />
				<button type="button" class="button button-secondary" id="spar-reset-total-earned">
					<?php 
    esc_html_e( 'Reset', 'simple-points-and-rewards' );
    ?>
				</button>
				<p class="description"><?php 
    esc_html_e( 'Lifetime points earned. Adjust manually or use Reset to set back to 0.', 'simple-points-and-rewards' );
    ?></p>
				<input type="hidden" name="spar_reset_total_earned" id="spar_reset_total_earned" value="0" />
				<script>
				(function(){
					var btn = document.getElementById('spar-reset-total-earned');
					if(!btn){ return; }
					var confirmMsg = <?php 
    echo wp_json_encode( wp_kses_decode_entities( esc_html__( 'Are you sure you want to reset this user\'s Total Earned points to 0? This action cannot be undone.', 'simple-points-and-rewards' ) ) );
    ?>;
					btn.addEventListener('click', function(e){
						if(window.confirm(confirmMsg)){
							var input = document.getElementById('spar_reset_total_earned');
							if(input){ input.value = '1'; }
							var totalField = document.getElementById('spar_total_earned_points');
							if(totalField){ totalField.value = '0'; }
							var form = btn.closest('form');
							if(form){ form.submit(); }
						}
					});
				})();
				</script>
			</td>
		</tr>
		<tr>
			<th><label for="spar_user_status"><?php 
    esc_html_e( 'Status', 'simple-points-and-rewards' );
    ?></label></th>
			<td>
				<select name="spar_user_status" id="spar_user_status">
					<option value="active" <?php 
    selected( 'active', $status );
    ?>><?php 
    esc_html_e( 'Active', 'simple-points-and-rewards' );
    ?></option>
					<option value="banned" <?php 
    selected( 'banned', $status );
    ?>><?php 
    esc_html_e( 'Banned', 'simple-points-and-rewards' );
    ?></option>
				</select>
				<p class="description">
					<?php 
    esc_html_e( 'Banned users cannot earn points or see rewards interfaces.', 'simple-points-and-rewards' );
    ?>
				</p>
			</td>
		</tr>
	</table>
	<?php 
}

add_action( 'personal_options_update', 'spar_save_user_profile_points' );
add_action( 'edit_user_profile_update', 'spar_save_user_profile_points' );
function spar_save_user_profile_points(  $user_id  ) {
    // Verify nonce and permissions
    if ( !isset( $_POST['spar_points_nonce'] ) || !wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['spar_points_nonce'] ) ), 'spar_update_user_points' ) ) {
        return;
    }
    if ( !current_user_can( 'edit_users', $user_id ) ) {
        return;
    }
    // Handle reset of Total Earned Points (lifetime) when requested
    $reset_flag = ( isset( $_POST['spar_reset_total_earned'] ) ? sanitize_text_field( wp_unslash( $_POST['spar_reset_total_earned'] ) ) : '0' );
    if ( '1' === $reset_flag ) {
        update_user_meta( $user_id, '_spar_total_earned_points', 0 );
    } elseif ( isset( $_POST['spar_total_earned_points'] ) ) {
        $total_earned_points = absint( wp_unslash( $_POST['spar_total_earned_points'] ) );
        update_user_meta( $user_id, '_spar_total_earned_points', $total_earned_points );
    }
    if ( isset( $_POST['spar_points'] ) ) {
        $points = absint( $_POST['spar_points'] );
        update_user_meta( $user_id, '_spar_points', $points );
    }
    if ( isset( $_POST['spar_user_status'] ) ) {
        $status = sanitize_text_field( wp_unslash( $_POST['spar_user_status'] ) );
        $allowed_statuses = ['active', 'banned'];
        if ( !in_array( $status, $allowed_statuses, true ) ) {
            $status = 'active';
        }
        update_user_meta( $user_id, 'spar_user_status', $status );
    }
}

// Add meta box to WooCommerce order edit pages (supports both traditional and HPOS)
add_action( 'add_meta_boxes', 'spar_add_order_points_meta_box' );
function spar_add_order_points_meta_box(  $post_type = null  ) {
    // For traditional post-based orders
    if ( $post_type === 'shop_order' || is_admin() && function_exists( 'get_current_screen' ) && get_current_screen() && get_current_screen()->id === 'shop_order' ) {
        add_meta_box(
            'spar_order_points',
            esc_html__( 'Reward Points', 'simple-points-and-rewards' ),
            'spar_order_points_meta_box_callback',
            'shop_order',
            'side',
            'high'
        );
        // Separate meta box for Referral details
        add_meta_box(
            'spar_order_referral',
            esc_html__( 'Referral Order', 'simple-points-and-rewards' ),
            'spar_order_referral_meta_box_callback',
            'shop_order',
            'side',
            'high'
        );
    }
    // For HPOS orders
    if ( function_exists( 'wc_get_container' ) && wc_get_container()->get( \Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ) {
        add_meta_box(
            'spar_order_points',
            esc_html__( 'Reward Points', 'simple-points-and-rewards' ),
            'spar_order_points_meta_box_callback_hpos',
            wc_get_page_screen_id( 'shop-order' ),
            'side',
            'high'
        );
        // Referral meta box for HPOS
        add_meta_box(
            'spar_order_referral',
            esc_html__( 'Points Referral', 'simple-points-and-rewards' ),
            'spar_order_referral_meta_box_callback_hpos',
            wc_get_page_screen_id( 'shop-order' ),
            'side',
            'high'
        );
    }
}

function spar_order_points_meta_box_callback(  $post  ) {
    $order = wc_get_order( $post->ID );
    spar_render_order_points_content( $order );
}

function spar_order_points_meta_box_callback_hpos(  $order  ) {
    spar_render_order_points_content( $order );
}

function spar_order_referral_meta_box_callback(  $post  ) {
    $order = wc_get_order( $post->ID );
    spar_render_order_referral_content( $order );
}

function spar_order_referral_meta_box_callback_hpos(  $order  ) {
    spar_render_order_referral_content( $order );
}

function spar_render_order_referral_content(  $order  ) {
    if ( !$order ) {
        echo '<p>' . esc_html__( 'Invalid order.', 'simple-points-and-rewards' ) . '</p>';
        return;
    }
    $options = get_option( 'spar_options', [] );
    $points_label = ( isset( $options['points_label'] ) ? sanitize_text_field( $options['points_label'] ) : esc_html__( 'Points', 'simple-points-and-rewards' ) );
    $referrer_code = $order->get_meta( 'referrer_code' );
    $referral_points_awarded = (int) $order->get_meta( 'referral_points_awarded' );
    if ( !$referrer_code ) {
        echo '<p>' . esc_html__( 'No referral data for this order.', 'simple-points-and-rewards' ) . '</p>';
        return;
    }
    // Referred by
    echo '<p><strong>' . esc_html__( 'Referrer code:', 'simple-points-and-rewards' ) . '</strong> ' . esc_html( $referrer_code ) . '</p>';
    // Get the referrer user (if any)
    $referrer_val = spar_get_user_by_referral_code( $referrer_code );
    $referrer_id = ( is_object( $referrer_val ) ? (int) $referrer_val->ID : (int) $referrer_val );
    if ( $referrer_id > 0 ) {
        $referrer_user = get_userdata( $referrer_id );
        if ( $referrer_user ) {
            $ref_name = ( $referrer_user->display_name ? $referrer_user->display_name : $referrer_user->user_login );
            echo '<p><strong>' . esc_html__( 'Referrer User:', 'simple-points-and-rewards' ) . '</strong> ' . esc_html( $ref_name ) . '</p>';
        }
    }
    // Awarded points (if any)
    if ( $referral_points_awarded > 0 ) {
        echo '<p><strong>' . esc_html__( 'Referral points:', 'simple-points-and-rewards' ) . '</strong> ' . esc_html( (int) $referral_points_awarded ) . ' ' . esc_html( strtolower( $points_label ) ) . '</p>';
    }
}

function spar_render_order_points_content(  $order  ) {
    if ( !$order ) {
        echo '<p>' . esc_html__( 'Invalid order.', 'simple-points-and-rewards' ) . '</p>';
        return;
    }
    $options = get_option( 'spar_options', [] );
    $points_label = ( isset( $options['points_label'] ) ? sanitize_text_field( $options['points_label'] ) : esc_html__( 'Points', 'simple-points-and-rewards' ) );
    // Check if order points earning is enabled
    if ( empty( $options['earn']['order']['enabled'] ) ) {
        echo '<p>' . sprintf( 
            /* translators: %s: points label (lowercase) */
            esc_html__( 'Order %s earning is currently disabled.', 'simple-points-and-rewards' ),
            esc_html( strtolower( $points_label ) )
         ) . '</p>';
        echo '<p><a href="' . esc_url( admin_url( 'admin.php?page=spar-settings' ) ) . '">' . esc_html__( 'Enable in Settings', 'simple-points-and-rewards' ) . '</a></p>';
        return;
    }
    // Check user has permission to view this information
    if ( !current_user_can( 'edit_shop_orders' ) ) {
        return;
    }
    $award_timing = ( isset( $options['earn']['order']['award_timing'] ) ? sanitize_text_field( $options['earn']['order']['award_timing'] ) : 'thankyou' );
    $completed_status = ( isset( $options['earn']['order']['completed_status'] ) ? sanitize_key( $options['earn']['order']['completed_status'] ) : 'completed' );
    if ( empty( $completed_status ) ) {
        $completed_status = 'completed';
    }
    // Calculate potential points using helper (respects multi-currency via premium filter)
    $potential_points = ( function_exists( 'spar_calculate_order_base_points' ) ? spar_calculate_order_base_points( $order ) : 0 );
    $user_id = $order->get_user_id();
    // Include first-order bonus from order meta if it exists, otherwise check if user qualifies
    $first_order_bonus_meta = (int) $order->get_meta( 'points_first_order_bonus' );
    if ( $first_order_bonus_meta > 0 ) {
        $potential_points += $first_order_bonus_meta;
    } else {
        if ( $user_id && function_exists( 'spar_user_qualifies_for_first_order_bonus' ) ) {
            if ( spar_user_qualifies_for_first_order_bonus( $user_id ) ) {
                $first_order_points = (int) ($options['earn']['first_order']['points'] ?? 0);
                if ( $first_order_points > 0 ) {
                    $potential_points += $first_order_points;
                }
            }
        }
    }
    // Check if points have been awarded
    $points_awarded = $order->get_meta( 'points_earned' );
    $points_deducted = $order->get_meta( 'points_deducted' );
    echo '<div class="spar-order-points-info">';
    // Order total and calculation
    if ( !$points_awarded ) {
        echo '<p><strong>' . esc_html__( 'Potential Points:', 'simple-points-and-rewards' ) . '</strong> <span id="spar-potential-points-display">' . esc_html( $potential_points ) . '</span>';
        // Add Modify button
        echo ' &nbsp; ' . '<button type="button" class="button button-small" id="spar-modify-potential-points">' . esc_html__( 'Modify', 'simple-points-and-rewards' ) . '</button>';
        echo '</p>';
        // Custom points input (initially hidden and disabled)
        echo '<div id="spar-custom-points-container" style="display:none; margin-bottom: 10px;">';
        echo '<input type="number" name="spar_custom_potential_points" id="spar_custom_potential_points" value="' . esc_attr( $potential_points ) . '" style="width: 80px;" />';
        wp_nonce_field( 'spar_save_order_points_meta', 'spar_order_points_meta_nonce' );
        echo ' <button type="button" class="button button-primary button-small" id="spar-save-potential-points" data-order-id="' . esc_attr( $order->get_id() ) . '" data-nonce="' . esc_attr( wp_create_nonce( 'spar_save_potential_points' ) ) . '">' . esc_html__( 'Save', 'simple-points-and-rewards' ) . '</button>';
        echo ' <button type="button" class="button button-small" id="spar-cancel-modify-points">' . esc_html__( 'Cancel', 'simple-points-and-rewards' ) . '</button>';
        echo '<span class="spinner" style="float:none; margin-top: 0;"></span>';
        echo '</div>';
    }
    // Award status
    if ( $points_awarded ) {
        if ( $points_deducted ) {
            echo '<p>';
            echo '<strong>↩️ ' . esc_html__( 'Points Deducted:', 'simple-points-and-rewards' ) . '</strong> ' . esc_html( $points_deducted ) . ' ' . esc_html( strtolower( $points_label ) );
            echo '<br /><small>' . esc_html__( 'Points were deducted due to order refund/cancellation', 'simple-points-and-rewards' ) . '</small>';
            echo '</p>';
        } else {
            echo '<p>';
            echo '<strong>' . esc_html__( 'Points Awarded:', 'simple-points-and-rewards' ) . '</strong> ' . esc_html( $points_awarded ) . ' ' . esc_html( strtolower( $points_label ) );
            echo '</p>';
        }
    } else {
        if ( $user_id ) {
            $status_icon = '⏳';
            $status_message = esc_html__( 'Points not yet awarded', 'simple-points-and-rewards' );
            $status_class = 'notice-warning';
            $show_award_button = true;
            // Check order status to determine if points should have been awarded
            $order_status = $order->get_status();
            if ( $award_timing === 'thankyou' ) {
                // Points should be awarded immediately, so if not awarded something might be wrong
                if ( in_array( $order_status, ['processing', 'completed', 'on-hold'], true ) ) {
                    $status_icon = '❌';
                    $status_message = esc_html__( 'Points should have been awarded but were not', 'simple-points-and-rewards' );
                    $status_class = 'notice-error';
                    $show_award_button = true;
                }
            } elseif ( $award_timing === 'completed' && $order_status === $completed_status ) {
                // Order is completed but points not awarded
                $status_icon = '❌';
                $status_message = esc_html__( 'Order completed but points not awarded', 'simple-points-and-rewards' );
                $status_class = 'notice-error';
                $show_award_button = true;
            }
            echo '<p style="background-color: #fff3cd; padding: 10px; border-left: 4px solid #ffeeba;" class="' . esc_attr( $status_class ) . '">';
            echo '<strong>' . esc_html( $status_icon ) . ' ' . esc_html( $status_message ) . '</strong>';
            if ( $show_award_button ) {
                $award_nonce = wp_create_nonce( 'spar_award_order_points' );
                echo '<br /><br />';
                echo '<button type="button" class="button button-small spar-award-order-points" data-order-id="' . esc_attr( $order->get_id() ) . '" data-nonce="' . esc_attr( $award_nonce ) . '">' . esc_html__( 'Grant points now', 'simple-points-and-rewards' ) . '</button>';
                echo '<span class="spinner" style="float:none; margin-left: 6px;"></span>';
            }
            echo '</p>';
        }
    }
    // Show customer info if logged in
    if ( $user_id ) {
        $user = get_userdata( $user_id );
        if ( $user ) {
            $current_points = spar_get_user_points( $user_id );
            echo '<p><strong>' . esc_html__( 'Customer:', 'simple-points-and-rewards' ) . '</strong> ' . esc_html( $user->display_name ) . '</p>';
            echo '<p style="margin-bottom: 0;"><strong>' . esc_html__( 'Current Points:', 'simple-points-and-rewards' ) . '</strong> ' . esc_html( $current_points ) . '</p>';
        }
    }
    /**
     * Fires after the order points meta box content is rendered.
     *
     * @param WC_Order $order The order object.
     */
    do_action( 'spar_after_order_points_meta_box', $order );
    echo '</div>';
}

function spar_enqueue_admin_scripts(  $hook  ) {
    // Enqueue on order edit pages
    $screen = ( function_exists( 'get_current_screen' ) ? get_current_screen() : null );
    if ( $screen && ('shop_order' === $screen->id || 'woocommerce_page_wc-orders' === $screen->id) ) {
        wp_enqueue_script(
            'spar-admin-order',
            plugin_dir_url( __DIR__ ) . 'assets/js/admin-order.js',
            ['jquery'],
            '1.0',
            true
        );
    }
    // Only load on our settings page
    if ( $hook !== 'toplevel_page_spar-settings' ) {
        return;
    }
    wp_enqueue_style(
        'spar-admin-settings',
        plugin_dir_url( __DIR__ ) . 'assets/css/admin-settings.css',
        [],
        '1.0'
    );
    wp_enqueue_script(
        'spar-admin-settings',
        plugin_dir_url( __DIR__ ) . 'assets/js/admin-settings.js',
        ['jquery'],
        '1.0',
        true
    );
    // Do NOT re-localize 'sparSettings' here; primary localization occurs in spar_enqueue_admin_assets.
    // Re-localizing was overwriting settingsNonce causing AJAX saves (e.g. levels_enabled) to fail and revert.
    // If for some reason the primary localization did not run, provide a minimal fallback without nonce (read-only UI).
    wp_add_inline_script( 'spar-admin-settings', "if(!window.sparSettings){window.sparSettings={ajaxUrl:'" . esc_js( admin_url( 'admin-ajax.php' ) ) . "'};}" );
}

add_action( 'admin_enqueue_scripts', 'spar_enqueue_admin_scripts' );
// AJAX handler for updating customer points
add_action( 'wp_ajax_spar_update_customer_points', 'spar_handle_update_customer_points_ajax' );
// AJAX handler to update user status (active/banned)
add_action( 'wp_ajax_spar_update_user_status', 'spar_handle_update_user_status_ajax' );
// Add AJAX handler for points history pagination
add_action( 'wp_ajax_spar_load_points_history', 'spar_handle_load_points_history_ajax' );
// Add AJAX handler for referral clicks pagination
add_action( 'wp_ajax_spar_load_referral_clicks', 'spar_handle_load_referral_clicks_ajax' );
add_action( 'wp_ajax_spar_admin_load_points_history', 'spar_handle_admin_load_points_history_ajax' );
add_action( 'wp_ajax_spar_admin_load_referral_clicks', 'spar_handle_admin_load_referral_clicks_ajax' );
add_action( 'wp_ajax_spar_admin_load_customer_orders', 'spar_handle_admin_load_customer_orders_ajax' );
// AJAX: Search products for the Rewards settings "Free Product" select
add_action( 'wp_ajax_spar_search_products', 'spar_ajax_search_products' );
function spar_ajax_search_products() {
    // Verify nonce from request (supports GET or POST)
    $nonce = ( isset( $_REQUEST['nonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['nonce'] ) ) : '' );
    if ( !$nonce || !wp_verify_nonce( $nonce, 'spar_search_products' ) ) {
        wp_send_json_error( [
            'message' => esc_html__( 'Invalid request', 'simple-points-and-rewards' ),
        ], 403 );
    }
    // Capability check: limit to store managers/admins
    if ( !current_user_can( 'manage_woocommerce' ) && !current_user_can( 'manage_options' ) ) {
        wp_send_json_error( [
            'message' => esc_html__( 'Not allowed', 'simple-points-and-rewards' ),
        ], 403 );
    }
    // Search term from SelectWoo (sent as `q`)
    $term = ( isset( $_GET['q'] ) ? sanitize_text_field( wp_unslash( $_GET['q'] ) ) : '' );
    $results = [];
    if ( $term !== '' ) {
        // Prefer WooCommerce products; basic title search via WP_Query for reliability
        $args = [
            'post_type'           => ['product'],
            's'                   => $term,
            'post_status'         => ['publish'],
            'posts_per_page'      => 20,
            'orderby'             => 'title',
            'order'               => 'ASC',
            'ignore_sticky_posts' => true,
            'fields'              => 'ids',
            'suppress_filters'    => false,
        ];
        $query = new WP_Query($args);
        if ( $query->have_posts() ) {
            foreach ( $query->posts as $product_id ) {
                $product = ( function_exists( 'wc_get_product' ) ? wc_get_product( $product_id ) : null );
                if ( !$product ) {
                    continue;
                }
                $name = $product->get_name();
                $results[] = [
                    'id'   => (int) $product_id,
                    'text' => sprintf( '%1$s (#%2$d)', $name, (int) $product_id ),
                ];
            }
        }
        wp_reset_postdata();
    }
    // Shape for Select2/SelectWoo: [{id, text}, ...]
    wp_send_json_success( $results );
}

function spar_handle_load_points_history_ajax() {
    // Verify nonce
    check_ajax_referer( 'spar_points_history_nonce', 'nonce', true );
    $user_id = get_current_user_id();
    if ( !$user_id ) {
        wp_send_json_error( esc_html__( 'User not logged in.', 'simple-points-and-rewards' ) );
    }
    $page = max( 1, intval( $_POST['page'] ?? 1 ) );
    $per_page = 10;
    // Fixed to 10 per page
    // Get points history data
    $history_data = spar_get_user_points_history( $user_id, $page, $per_page );
    $logs = $history_data['logs'];
    $pagination = $history_data['pagination'];
    // Get points label
    $options = get_option( 'spar_options', [] );
    $points_label = $options['points_label'] ?? esc_html__( 'Points', 'simple-points-and-rewards' );
    // Generate timeline HTML
    $table_html = '';
    if ( !empty( $logs ) ) {
        $current_date_group = '';
        foreach ( $logs as $log ) {
            $date_group = date_i18n( 'Y-m-d', strtotime( $log['date'] ) );
            if ( $date_group !== $current_date_group ) {
                $current_date_group = $date_group;
                $table_html .= sprintf( '<div class="spar-tl-date-sep"><span>%s</span></div>', esc_html( date_i18n( 'M j', strtotime( $log['date'] ) ) ) );
            }
            $table_html .= spar_build_history_timeline_item( $log, $points_label );
        }
    } else {
        $table_html = sprintf( '<div class="spar-tl-empty"><span class="spar-tl-empty-icon">📋</span><span class="spar-tl-empty-text">%s</span></div>', sprintf( 
            /* translators: %s: points label (lowercase) */
            esc_html__( 'No %s history yet. Start earning points by making purchases!', 'simple-points-and-rewards' ),
            esc_html( strtolower( $points_label ) )
         ) );
    }
    // Generate pagination HTML
    $pagination_html = '';
    if ( $pagination['total_pages'] > 1 ) {
        $pagination_html .= '</div><div class="spar-pagination-controls">';
        // Previous button
        $prev_disabled = ( !$pagination['has_previous'] ? 'disabled' : '' );
        $pagination_html .= sprintf(
            '<button class="spar-pagination-btn spar-pagination-prev" data-page="%d" %s>%s</button>',
            $pagination['current_page'] - 1,
            $prev_disabled,
            esc_html__( '← Previous', 'simple-points-and-rewards' )
        );
        // Page number buttons
        $current_page = (int) $pagination['current_page'];
        $total_pages = (int) $pagination['total_pages'];
        $start_page = max( 1, $current_page - 2 );
        $end_page = min( $total_pages, $current_page + 2 );
        if ( $start_page === 1 ) {
            $end_page = min( $total_pages, 5 );
        } elseif ( $end_page === $total_pages ) {
            $start_page = max( 1, $total_pages - 4 );
        }
        for ($i = $start_page; $i <= $end_page; $i++) {
            $is_current = $i === $pagination['current_page'];
            $btn_class = ( $is_current ? 'spar-pagination-btn spar-pagination-current' : 'spar-pagination-btn spar-pagination-page' );
            $btn_disabled = ( $is_current ? 'disabled' : '' );
            $pagination_html .= sprintf(
                '<button class="%s" data-page="%d" %s>%d</button>',
                esc_attr( $btn_class ),
                $i,
                $btn_disabled,
                $i
            );
        }
        // Next button
        $next_disabled = ( !$pagination['has_next'] ? 'disabled' : '' );
        $pagination_html .= sprintf(
            '<button class="spar-pagination-btn spar-pagination-next" data-page="%d" %s>%s</button>',
            $pagination['current_page'] + 1,
            $next_disabled,
            esc_html__( 'Next →', 'simple-points-and-rewards' )
        );
        $pagination_html .= '</div>';
    }
    wp_send_json_success( [
        'table_html'      => $table_html,
        'pagination_html' => $pagination_html,
        'current_page'    => $pagination['current_page'],
        'total_pages'     => $pagination['total_pages'],
    ] );
}

function spar_handle_load_referral_clicks_ajax() {
    // Verify nonce
    check_ajax_referer( 'spar_referral_clicks_nonce', 'nonce', true );
    $user_id = get_current_user_id();
    if ( !$user_id ) {
        wp_send_json_error( esc_html__( 'User not logged in.', 'simple-points-and-rewards' ) );
    }
    $options = get_option( 'spar_options', [] );
    $show_clicks_log = !empty( $options['earn']['referral']['show_clicks_log'] );
    if ( !$show_clicks_log ) {
        wp_send_json_error( esc_html__( 'Referral clicks log is disabled.', 'simple-points-and-rewards' ) );
    }
    $page = max( 1, intval( $_POST['page'] ?? 1 ) );
    $context = ( isset( $_POST['context'] ) ? sanitize_text_field( wp_unslash( $_POST['context'] ) ) : 'dashboard' );
    if ( !in_array( $context, ['dashboard', 'widget'], true ) ) {
        $context = 'dashboard';
    }
    $per_page = 5;
    if ( !function_exists( 'spar_get_user_referral_clicks_history' ) ) {
        wp_send_json_error( esc_html__( 'Referral clicks unavailable.', 'simple-points-and-rewards' ) );
    }
    $history_data = spar_get_user_referral_clicks_history( $user_id, $page, $per_page );
    $logs = $history_data['logs'];
    $pagination = $history_data['pagination'];
    $table_html = ( function_exists( 'spar_build_referral_clicks_table_rows' ) ? spar_build_referral_clicks_table_rows( $logs, $context ) : '' );
    $pagination_html = ( function_exists( 'spar_build_referral_clicks_pagination_html' ) ? spar_build_referral_clicks_pagination_html( $pagination, $context ) : '' );
    wp_send_json_success( [
        'table_html'      => $table_html,
        'pagination_html' => $pagination_html,
    ] );
}

function spar_handle_update_customer_points_ajax() {
    // Verify nonce
    check_ajax_referer( 'spar_update_points_nonce', 'nonce', true );
    // Check permissions
    if ( !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Insufficient permissions.', 'simple-points-and-rewards' ) );
    }
    $user_id = ( isset( $_POST['user_id'] ) ? absint( $_POST['user_id'] ) : 0 );
    $points_amount = ( isset( $_POST['points'] ) ? absint( $_POST['points'] ) : 0 );
    $action = ( isset( $_POST['points_action'] ) ? sanitize_key( wp_unslash( $_POST['points_action'] ) ) : 'set' );
    // Reason: accept custom text when provided; fallback to default when missing/blank
    $reason_raw = ( isset( $_POST['reason'] ) ? wp_unslash( $_POST['reason'] ) : '' );
    $reason_trimmed = ( is_string( $reason_raw ) ? trim( $reason_raw ) : '' );
    $reason = ( $reason_trimmed !== '' ? sanitize_text_field( $reason_trimmed ) : esc_html__( 'Admin adjustment', 'simple-points-and-rewards' ) );
    if ( !$user_id || !get_userdata( $user_id ) ) {
        wp_send_json_error( esc_html__( 'Invalid user ID.', 'simple-points-and-rewards' ) );
    }
    // Perform update using core helper which also logs the change
    if ( 'add' === $action || 'remove' === $action ) {
        spar_update_user_points(
            $user_id,
            $points_amount,
            ( 'add' === $action ? 'add' : 'remove' ),
            $reason,
            'admin_adjustment'
        );
    } else {
        // Fallback: set absolute by applying the delta via helper
        $current_points = spar_get_user_points( $user_id );
        $delta = (int) $points_amount - (int) $current_points;
        if ( 0 !== $delta ) {
            $delta_action = ( $delta > 0 ? 'add' : 'remove' );
            spar_update_user_points(
                $user_id,
                abs( $delta ),
                $delta_action,
                $reason,
                'admin_adjustment'
            );
        }
    }
    // Fetch new balance to return
    $new_points = spar_get_user_points( $user_id );
    $total_earned = ( function_exists( 'spar_get_user_total_points_earned' ) ? spar_get_user_total_points_earned( $user_id ) : 0 );
    wp_send_json_success( [
        'message'      => sprintf( 
            /* translators: %d: updated points balance */
            esc_html__( 'Points updated successfully. User now has %d points.', 'simple-points-and-rewards' ),
            $new_points
         ),
        'new_points'   => $new_points,
        'total_earned' => $total_earned,
    ] );
}

/**
 * AJAX: Update user status (Active/Banned)
 */
function spar_handle_update_user_status_ajax() {
    check_ajax_referer( 'spar_update_user_status', 'nonce', true );
    if ( !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Insufficient permissions.', 'simple-points-and-rewards' ) );
    }
    $user_id = ( isset( $_POST['user_id'] ) ? absint( $_POST['user_id'] ) : 0 );
    $status = ( isset( $_POST['status'] ) ? sanitize_key( wp_unslash( $_POST['status'] ) ) : 'active' );
    if ( !$user_id || !get_userdata( $user_id ) ) {
        wp_send_json_error( esc_html__( 'Invalid user ID.', 'simple-points-and-rewards' ) );
    }
    if ( !in_array( $status, array('active', 'banned'), true ) ) {
        wp_send_json_error( esc_html__( 'Invalid status.', 'simple-points-and-rewards' ) );
    }
    update_user_meta( $user_id, 'spar_user_status', $status );
    wp_send_json_success( array(
        'status' => $status,
    ) );
}

/**
 * AJAX: Create a new Rewards Dashboard page with the shortcode.
 */
function spar_ajax_generate_rewards_page() {
    // Allow Admins (manage_options) and Shop Managers (manage_woocommerce)
    if ( !current_user_can( 'manage_woocommerce' ) && !current_user_can( 'manage_options' ) ) {
        wp_send_json_error( esc_html__( 'Insufficient permissions', 'simple-points-and-rewards' ) );
    }
    check_ajax_referer( 'spar_generate_rewards_page', 'nonce', true );
    $title = ( isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : esc_html__( 'Rewards', 'simple-points-and-rewards' ) );
    // Choose a safe status depending on capabilities
    $status = 'draft';
    if ( current_user_can( 'publish_pages' ) ) {
        $status = 'publish';
    } elseif ( !current_user_can( 'edit_pages' ) ) {
        wp_send_json_error( esc_html__( 'Your role cannot create pages. Please create a page manually and add the shortcode.', 'simple-points-and-rewards' ) );
    }
    $page_id = wp_insert_post( [
        'post_title'   => $title,
        'post_content' => '[spar_points_rewards]',
        'post_status'  => $status,
        'post_type'    => 'page',
    ] );
    if ( is_wp_error( $page_id ) || !$page_id ) {
        wp_send_json_error( esc_html__( 'Failed to create page', 'simple-points-and-rewards' ) );
    }
    // Save option
    spar_update_single_option( 'rewards_page_id', absint( $page_id ) );
    wp_send_json_success( [
        'id'     => $page_id,
        'url'    => get_permalink( $page_id ),
        'title'  => get_the_title( $page_id ),
        'status' => $status,
    ] );
}

add_action( 'wp_ajax_spar_generate_rewards_page', 'spar_ajax_generate_rewards_page' );
/**
 * AJAX: List all pages that contain the [spar_points_rewards] shortcode.
 */
function spar_ajax_list_rewards_pages() {
    // Allow Admins (manage_options) and Shop Managers (manage_woocommerce)
    if ( !current_user_can( 'manage_woocommerce' ) && !current_user_can( 'manage_options' ) ) {
        wp_send_json_error( esc_html__( 'Insufficient permissions', 'simple-points-and-rewards' ) );
    }
    check_ajax_referer( 'spar_list_rewards_pages', 'nonce', true );
    $pages = get_pages( array(
        'sort_column' => 'post_title',
        'post_status' => array('publish', 'draft'),
    ) );
    $result = array();
    if ( !empty( $pages ) ) {
        foreach ( $pages as $page ) {
            if ( function_exists( 'has_shortcode' ) && has_shortcode( $page->post_content, 'spar_points_rewards' ) ) {
                $result[] = array(
                    'id'    => $page->ID,
                    'title' => $page->post_title,
                );
            }
        }
    }
    wp_send_json_success( array(
        'pages' => $result,
    ) );
}

add_action( 'wp_ajax_spar_list_rewards_pages', 'spar_ajax_list_rewards_pages' );
/**
 * AJAX: Get page link for selected page ID.
 */
function spar_ajax_get_page_link() {
    // Match plugin menu capability so Shop Managers can use this tool
    if ( !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Insufficient permissions', 'simple-points-and-rewards' ) );
    }
    check_ajax_referer( 'spar_retrieve_page_link', 'nonce', true );
    $page_id = ( isset( $_POST['page_id'] ) ? absint( $_POST['page_id'] ) : 0 );
    if ( !$page_id ) {
        wp_send_json_success( [
            'url' => '',
        ] );
    }
    $url = get_permalink( $page_id );
    if ( !$url ) {
        wp_send_json_error( esc_html__( 'Invalid page', 'simple-points-and-rewards' ) );
    }
    wp_send_json_success( [
        'url' => $url,
    ] );
}

add_action( 'wp_ajax_spar_get_page_link', 'spar_ajax_get_page_link' );
/**
 * AJAX: Get admin edit link for a coupon ID (used by Template Coupon select).
 */
function spar_ajax_get_coupon_edit_link() {
    // Capability: match settings page capability (Shop Managers usually have manage_woocommerce)
    if ( !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Insufficient permissions', 'simple-points-and-rewards' ) );
    }
    check_ajax_referer( 'spar_get_coupon_edit_link', 'nonce', true );
    $coupon_id = ( isset( $_POST['coupon_id'] ) ? absint( $_POST['coupon_id'] ) : 0 );
    if ( !$coupon_id ) {
        wp_send_json_success( [
            'url'  => '',
            'text' => esc_html__( 'No template selected.', 'simple-points-and-rewards' ),
        ] );
    }
    $post_type = get_post_type( $coupon_id );
    if ( 'shop_coupon' !== $post_type ) {
        wp_send_json_error( esc_html__( 'Invalid coupon.', 'simple-points-and-rewards' ) );
    }
    $edit_url = admin_url( 'post.php?post=' . $coupon_id . '&action=edit' );
    $title = get_the_title( $coupon_id );
    $text = sprintf( esc_html__( 'Edit coupon: %1$s (#%2$d)', 'simple-points-and-rewards' ), $title, $coupon_id );
    wp_send_json_success( [
        'url'  => $edit_url,
        'text' => $text,
    ] );
}

add_action( 'wp_ajax_spar_get_coupon_edit_link', 'spar_ajax_get_coupon_edit_link' );
/**
 * AJAX: Create a new blank template coupon for use as a reward template.
 */
add_action( 'wp_ajax_spar_create_template_coupon', 'spar_ajax_create_template_coupon' );
function spar_ajax_create_template_coupon() {
    if ( !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Insufficient permissions', 'simple-points-and-rewards' ) );
    }
    check_ajax_referer( 'spar_create_template_coupon', 'nonce', true );
    $random = strtolower( wp_generate_password( 8, false, false ) );
    $coupon_code = 'template_' . $random;
    $coupon_id = wp_insert_post( [
        'post_title'   => $coupon_code,
        'post_content' => '',
        'post_status'  => 'publish',
        'post_author'  => get_current_user_id(),
        'post_type'    => 'shop_coupon',
    ] );
    if ( is_wp_error( $coupon_id ) ) {
        wp_send_json_error( $coupon_id->get_error_message() );
    }
    // Set sensible defaults so it shows up properly in WooCommerce.
    update_post_meta( $coupon_id, 'discount_type', 'fixed_cart' );
    update_post_meta( $coupon_id, 'coupon_amount', '0' );
    update_post_meta( $coupon_id, 'individual_use', 'no' );
    update_post_meta( $coupon_id, 'usage_limit', '' );
    update_post_meta( $coupon_id, 'usage_limit_per_user', '' );
    update_post_meta( $coupon_id, 'usage_count', '0' );
    update_post_meta( $coupon_id, 'free_shipping', 'no' );
    $edit_url = get_edit_post_link( $coupon_id, 'raw' );
    wp_send_json_success( [
        'coupon_id'    => $coupon_id,
        'coupon_title' => $coupon_code,
        'edit_url'     => ( $edit_url ? esc_url( $edit_url ) : '' ),
    ] );
}

// Save custom potential points meta
add_action(
    'woocommerce_process_shop_order_meta',
    'spar_save_order_points_meta',
    10,
    2
);
function spar_save_order_points_meta(  $post_id, $post  ) {
    // Verify nonce
    if ( !isset( $_POST['spar_order_points_meta_nonce'] ) || !wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['spar_order_points_meta_nonce'] ) ), 'spar_save_order_points_meta' ) ) {
        return;
    }
    if ( isset( $_POST['spar_custom_potential_points'] ) ) {
        $custom_points = absint( $_POST['spar_custom_potential_points'] );
        update_post_meta( $post_id, 'custom_potential_points', $custom_points );
    }
}

// AJAX handler for saving custom potential points
add_action( 'wp_ajax_spar_save_custom_potential_points', 'spar_save_custom_potential_points_ajax' );
function spar_save_custom_potential_points_ajax() {
    check_ajax_referer( 'spar_save_potential_points', 'nonce' );
    if ( !current_user_can( 'edit_shop_orders' ) && !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( 'Permission denied' );
    }
    $order_id = ( isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0 );
    $points = ( isset( $_POST['points'] ) ? absint( $_POST['points'] ) : 0 );
    if ( !$order_id ) {
        wp_send_json_error( 'Invalid order ID' );
    }
    $order = wc_get_order( $order_id );
    if ( !$order ) {
        wp_send_json_error( 'Order not found' );
    }
    // Check if points already awarded
    if ( $order->get_meta( 'points_earned' ) ) {
        wp_send_json_error( 'Points already awarded for this order' );
    }
    $order->update_meta_data( 'custom_potential_points', $points );
    $order->save();
    wp_send_json_success();
}

// AJAX handler for manually awarding order points in admin.
add_action( 'wp_ajax_spar_award_order_points_admin', 'spar_award_order_points_admin_ajax' );
function spar_award_order_points_admin_ajax() {
    check_ajax_referer( 'spar_award_order_points', 'nonce' );
    if ( !current_user_can( 'edit_shop_orders' ) && !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Permission denied', 'simple-points-and-rewards' ), 403 );
    }
    $order_id = ( isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0 );
    if ( !$order_id ) {
        wp_send_json_error( esc_html__( 'Invalid order ID', 'simple-points-and-rewards' ) );
    }
    $order = wc_get_order( $order_id );
    if ( !$order ) {
        wp_send_json_error( esc_html__( 'Order not found', 'simple-points-and-rewards' ) );
    }
    $options = get_option( 'spar_options', [] );
    $completed_status = ( isset( $options['earn']['order']['completed_status'] ) ? sanitize_key( $options['earn']['order']['completed_status'] ) : 'completed' );
    if ( empty( $completed_status ) ) {
        $completed_status = 'completed';
    }
    $award_timing = ( isset( $options['earn']['order']['award_timing'] ) ? sanitize_key( $options['earn']['order']['award_timing'] ) : 'completed' );
    $order_status = $order->get_status();
    $allowed_statuses = [$completed_status];
    if ( 'thankyou' === $award_timing ) {
        // When award_timing is thankyou, also allow common post-payment statuses
        $allowed_statuses = array_unique( array_merge( $allowed_statuses, ['processing', 'on-hold', 'pending'] ) );
    }
    if ( $order->get_meta( 'points_earned' ) ) {
        wp_send_json_error( esc_html__( 'Points already awarded for this order.', 'simple-points-and-rewards' ) );
    }
    if ( !function_exists( 'spar_award_order_points' ) ) {
        wp_send_json_error( esc_html__( 'Internal error: Points function missing.', 'simple-points-and-rewards' ) );
    }
    spar_award_order_points( $order_id, true );
    // Also trigger the first-order bonus if applicable (not fired automatically when forcing via admin)
    if ( function_exists( 'spar_award_first_order_points' ) ) {
        $first_order_bonus = (int) $order->get_meta( 'points_first_order_bonus' );
        if ( $first_order_bonus > 0 ) {
            $user_id = $order->get_user_id();
            if ( $user_id ) {
                $user_rewards_earned = get_user_meta( $user_id, '_spar_rewards_earned', true );
                if ( !is_array( $user_rewards_earned ) || empty( $user_rewards_earned['first_order'] ) ) {
                    $options = get_option( 'spar_options', [] );
                    $action_name = $options['earn']['first_order']['name'] ?? esc_html__( 'First Order Bonus', 'simple-points-and-rewards' );
                    spar_update_user_points(
                        $user_id,
                        $first_order_bonus,
                        'add',
                        sanitize_text_field( $action_name ),
                        'first_order'
                    );
                    if ( !is_array( $user_rewards_earned ) ) {
                        $user_rewards_earned = [];
                    }
                    $user_rewards_earned['first_order'] = true;
                    update_user_meta( $user_id, '_spar_rewards_earned', $user_rewards_earned );
                }
            }
        }
    }
    $order = wc_get_order( $order_id );
    $points_awarded = ( $order ? (int) $order->get_meta( 'points_earned' ) : 0 );
    if ( $points_awarded > 0 ) {
        wp_send_json_success( [
            'points' => $points_awarded,
        ] );
    }
    wp_send_json_error( esc_html__( 'Points could not be awarded. Please try again.', 'simple-points-and-rewards' ) );
}

/**
 * Process settings save
 *
 * @param array $post_data Data to save (usually $_POST)
 * @return bool True on success
 */
function spar_process_settings_save(  $post_data  ) {
    if ( !function_exists( 'spar_get_option_fields' ) ) {
        require_once SPAR_PLUGIN_PATH . 'includes/admin/settings/settings-utilities.php';
    }
    $form_complete = !empty( $post_data['spar_form_end'] );
    $options = get_option( 'spar_options', spar_settings_default() );
    $defaults = spar_settings_default();
    // Ensure earn array exists
    if ( !isset( $options['earn'] ) || !is_array( $options['earn'] ) ) {
        $options['earn'] = $defaults['earn'];
    }
    $fields = spar_get_option_fields();
    foreach ( $fields as $key => $field_data ) {
        if ( $key === 'earn' ) {
            // Handle nested earn fields
            foreach ( $field_data as $earn_key => $earn_data ) {
                // Check if this earn type has ANY field submitted in the POST data.
                // If none were submitted, the form section was not rendered (e.g., PRO-only
                // section on a free install), so we should preserve existing DB values rather
                // than forcing checkboxes to false.
                $earn_type_in_form = false;
                foreach ( $earn_data as $ck => $cv ) {
                    if ( array_key_exists( $earn_key . '_' . $ck, $post_data ) ) {
                        $earn_type_in_form = true;
                        break;
                    }
                }
                foreach ( $earn_data as $subkey => $subfield ) {
                    $post_key = $earn_key . '_' . $subkey;
                    $has_post = array_key_exists( $post_key, $post_data );
                    if ( $has_post ) {
                        $value = wp_unslash( $post_data[$post_key] );
                        $options['earn'][$earn_key][$subkey] = spar_sanitize_field_value( $value, $subfield['type'], $subfield['default'] );
                    } elseif ( 'checkbox' === $subfield['type'] && $form_complete && $earn_type_in_form ) {
                        $options['earn'][$earn_key][$subkey] = false;
                    } elseif ( !isset( $options['earn'][$earn_key][$subkey] ) ) {
                        $options['earn'][$earn_key][$subkey] = $subfield['default'];
                    }
                }
            }
        } elseif ( 'rewards_widget_icon' === $key ) {
            // Allow empty selection for "No Icon".
            if ( array_key_exists( $key, $post_data ) ) {
                $value = wp_unslash( $post_data[$key] );
                $options[$key] = sanitize_text_field( $value );
            }
        } elseif ( 'rewards_widget_icon_custom_url' === $key ) {
            if ( array_key_exists( $key, $post_data ) ) {
                $value = wp_unslash( $post_data[$key] );
                $options[$key] = esc_url_raw( $value );
            }
        } elseif ( 'rewards_widget_icon_custom_text' === $key ) {
            if ( array_key_exists( $key, $post_data ) ) {
                $value = wp_unslash( $post_data[$key] );
                $options[$key] = sanitize_text_field( $value );
            }
        } elseif ( 'levels_starter_badge_icon' === $key ) {
            $raw_icon = ( isset( $post_data['levels_starter_badge_icon'] ) ? sanitize_text_field( wp_unslash( $post_data['levels_starter_badge_icon'] ) ) : '' );
            $raw_url = ( isset( $post_data['levels_starter_badge_url'] ) ? esc_url_raw( wp_unslash( $post_data['levels_starter_badge_url'] ) ) : '' );
            $raw_text = ( isset( $post_data['levels_starter_badge_text'] ) ? sanitize_text_field( wp_unslash( $post_data['levels_starter_badge_text'] ) ) : '' );
            $raw_type = ( isset( $post_data['levels_starter_badge_type'] ) ? sanitize_text_field( wp_unslash( $post_data['levels_starter_badge_type'] ) ) : '' );
            $allowed_badge_types = ['preset', 'custom_url', 'custom_text'];
            $badge_type = ( in_array( $raw_type, $allowed_badge_types, true ) ? $raw_type : '' );
            if ( '' === $badge_type ) {
                if ( '' !== $raw_url ) {
                    $badge_type = 'custom_url';
                } elseif ( '' !== $raw_text ) {
                    $badge_type = 'custom_text';
                } elseif ( '' !== $raw_icon ) {
                    $badge_type = 'preset';
                } else {
                    $badge_type = 'preset';
                }
            }
            $badge_icon = $raw_icon;
            $badge_url = $raw_url;
            $badge_text = $raw_text;
            if ( 'custom_url' === $badge_type ) {
                $badge_icon = '';
                $badge_text = '';
            } elseif ( 'custom_text' === $badge_type ) {
                if ( '' === $badge_text && '' !== $badge_icon ) {
                    $badge_text = $badge_icon;
                }
                $badge_icon = '';
                $badge_url = '';
            } else {
                $badge_type = 'preset';
                if ( '' === $badge_icon && '' !== $badge_text ) {
                    $badge_icon = $badge_text;
                }
                $badge_url = '';
                $badge_text = '';
            }
            $options['levels_starter_badge_type'] = $badge_type;
            $options['levels_starter_badge_icon'] = $badge_icon;
            $options['levels_starter_badge_url'] = $badge_url;
            $options['levels_starter_badge_text'] = $badge_text;
            continue;
        } elseif ( in_array( $key, ['levels_starter_badge_type', 'levels_starter_badge_text', 'levels_starter_badge_url'], true ) ) {
            // Starter badge fields are processed together above.
            continue;
        } elseif ( 'earn_conditional_rules' === $key ) {
            // Handle earn conditional rules array (PRO).
            $has_rules = isset( $post_data['earn_conditional_rules'] );
            $raw_rules = ( $has_rules ? wp_unslash( $post_data['earn_conditional_rules'] ) : [] );
            $deleted_ids = ( isset( $post_data['earn_conditional_rules_deleted_ids'] ) ? wp_unslash( $post_data['earn_conditional_rules_deleted_ids'] ) : [] );
            if ( !is_array( $deleted_ids ) ) {
                $deleted_ids = [$deleted_ids];
            }
            $deleted_ids = array_values( array_filter( array_map( 'sanitize_text_field', $deleted_ids ) ) );
            $has_deleted = !empty( $deleted_ids );
            // If neither rules nor deleted IDs were submitted, do not modify the saved rules.
            if ( !$has_rules && !$has_deleted ) {
                continue;
            }
            // If rules are missing but we have deleted IDs, perform a delete-only update
            // against the existing saved rules. This is resilient to truncated form payloads.
            if ( !$has_rules && $has_deleted ) {
                $existing = ( isset( $options['earn_conditional_rules'] ) && is_array( $options['earn_conditional_rules'] ) ? $options['earn_conditional_rules'] : [] );
                $existing = array_values( array_filter( $existing, function ( $rule ) use($deleted_ids) {
                    if ( !is_array( $rule ) ) {
                        return false;
                    }
                    $rid = ( isset( $rule['id'] ) ? sanitize_text_field( (string) $rule['id'] ) : '' );
                    if ( '' === $rid ) {
                        return true;
                    }
                    return !in_array( $rid, $deleted_ids, true );
                } ) );
                $options['earn_conditional_rules'] = $existing;
                continue;
            }
            $sanitized_rules = [];
            $allowed_types = [
                'product',
                'product_category',
                'product_item',
                'product_item_category',
                'cart_total',
                'cart_coupon',
                'customer',
                'user_role',
                'customer_total_points',
                'customer_available_points'
            ];
            $allowed_ways_order_only = ['order', 'order_fixed'];
            $allowed_ways_all = [
                'order',
                'order_fixed',
                'signup',
                'daily_login',
                'daily_login_streak',
                'birthday',
                'review',
                'referral',
                'social_share',
                'first_order',
                'nth_order'
            ];
            $allowed_in_cart_ops = ['in_cart', 'not_in_cart'];
            $allowed_cart_ops = ['above', 'below'];
            $all_roles = [];
            if ( function_exists( 'wp_roles' ) && wp_roles() ) {
                $all_roles = array_keys( wp_roles()->roles );
            }
            if ( is_array( $raw_rules ) ) {
                foreach ( $raw_rules as $rule ) {
                    if ( !is_array( $rule ) ) {
                        continue;
                    }
                    $rule_id = ( isset( $rule['id'] ) ? sanitize_text_field( (string) $rule['id'] ) : '' );
                    if ( '' !== $rule_id && !empty( $deleted_ids ) && in_array( $rule_id, $deleted_ids, true ) ) {
                        continue;
                    }
                    $condition_types = [];
                    if ( isset( $rule['conditions'] ) && is_array( $rule['conditions'] ) ) {
                        foreach ( $rule['conditions'] as $cond ) {
                            if ( !is_array( $cond ) ) {
                                continue;
                            }
                            $cond_type = ( isset( $cond['condition_type'] ) ? sanitize_text_field( (string) $cond['condition_type'] ) : '' );
                            if ( in_array( $cond_type, $allowed_types, true ) ) {
                                $condition_types[] = $cond_type;
                            }
                        }
                    }
                    if ( empty( $condition_types ) ) {
                        $condition_type = ( isset( $rule['condition_type'] ) ? sanitize_text_field( (string) $rule['condition_type'] ) : '' );
                        if ( !in_array( $condition_type, $allowed_types, true ) ) {
                            continue;
                        }
                        $condition_types[] = $condition_type;
                    }
                    $condition_types = array_values( array_unique( $condition_types ) );
                    $condition_type = $condition_types[0];
                    $all_non_order = empty( array_diff( $condition_types, [
                        'customer',
                        'user_role',
                        'customer_total_points',
                        'customer_available_points'
                    ] ) ) && !empty( $condition_types );
                    $ways = [];
                    $allowed_ways = ( $all_non_order ? $allowed_ways_all : $allowed_ways_order_only );
                    if ( isset( $rule['ways'] ) ) {
                        $raw_ways = $rule['ways'];
                        if ( !is_array( $raw_ways ) ) {
                            $raw_ways = [$raw_ways];
                        }
                        $raw_ways = array_map( 'sanitize_text_field', $raw_ways );
                        $ways = array_values( array_intersect( $raw_ways, $allowed_ways ) );
                    }
                    if ( empty( $ways ) ) {
                        $ways = $allowed_ways;
                    }
                    $multiplier = ( isset( $rule['multiplier'] ) ? (float) $rule['multiplier'] : 1.0 );
                    $multiplier = max( 0.0, (float) $multiplier );
                    $hide_disable_raw = $rule['hide_disable_way'] ?? '';
                    if ( is_array( $hide_disable_raw ) && !empty( $hide_disable_raw ) ) {
                        $hide_disable_raw = end( $hide_disable_raw );
                    }
                    $hide_disable = false;
                    if ( true === $hide_disable_raw || 1 === $hide_disable_raw ) {
                        $hide_disable = true;
                    } elseif ( is_string( $hide_disable_raw ) ) {
                        $hide_disable = in_array( strtolower( $hide_disable_raw ), [
                            '1',
                            'true',
                            'yes',
                            'on'
                        ], true );
                    }
                    $fixed_points_delta = ( isset( $rule['fixed_points_delta'] ) ? (int) $rule['fixed_points_delta'] : 0 );
                    $min_points = ( isset( $rule['min_points'] ) ? absint( $rule['min_points'] ) : 0 );
                    $max_points = ( isset( $rule['max_points'] ) ? absint( $rule['max_points'] ) : 0 );
                    if ( $min_points > 0 && $max_points > 0 && $max_points < $min_points ) {
                        $max_points = $min_points;
                    }
                    $san = [
                        'id'                 => $rule_id,
                        'label'              => ( isset( $rule['label'] ) ? sanitize_text_field( (string) $rule['label'] ) : '' ),
                        'condition_type'     => $condition_type,
                        'ways'               => $ways,
                        'multiplier'         => $multiplier,
                        'hide_disable_way'   => $hide_disable,
                        'fixed_points_delta' => $fixed_points_delta,
                        'min_points'         => $min_points,
                        'max_points'         => $max_points,
                    ];
                    if ( empty( $san['id'] ) && function_exists( 'wp_generate_uuid4' ) ) {
                        $san['id'] = wp_generate_uuid4();
                    }
                    $conditions = [];
                    $conditions_input = ( isset( $rule['conditions'] ) && is_array( $rule['conditions'] ) ? $rule['conditions'] : [] );
                    if ( empty( $conditions_input ) ) {
                        $conditions_input = [$rule];
                    }
                    foreach ( $conditions_input as $cond ) {
                        if ( !is_array( $cond ) ) {
                            continue;
                        }
                        $cond_type = ( isset( $cond['condition_type'] ) ? sanitize_text_field( (string) $cond['condition_type'] ) : '' );
                        if ( !in_array( $cond_type, $allowed_types, true ) ) {
                            continue;
                        }
                        $cond_valid = true;
                        $cond_san = [
                            'condition_type' => $cond_type,
                        ];
                        switch ( $cond_type ) {
                            case 'product_item':
                                $ids = ( isset( $cond['product_ids'] ) && is_array( $cond['product_ids'] ) ? array_map( 'absint', $cond['product_ids'] ) : [] );
                                $ids = array_values( array_unique( array_filter( $ids ) ) );
                                $cond_san['product_ids'] = $ids;
                                if ( empty( $ids ) ) {
                                    $cond_valid = false;
                                }
                                break;
                            case 'product_item_category':
                                $op = ( isset( $cond['category_item_operator'] ) ? sanitize_text_field( (string) $cond['category_item_operator'] ) : 'is' );
                                $op = ( in_array( $op, ['is', 'is_not'], true ) ? $op : 'is' );
                                $ids = ( isset( $cond['category_ids'] ) && is_array( $cond['category_ids'] ) ? array_map( 'absint', $cond['category_ids'] ) : [] );
                                $ids = array_values( array_unique( array_filter( $ids ) ) );
                                $cond_san['category_item_operator'] = $op;
                                $cond_san['category_ids'] = $ids;
                                if ( empty( $ids ) ) {
                                    $cond_valid = false;
                                }
                                break;
                            case 'product':
                                $op = ( isset( $cond['product_operator'] ) ? sanitize_text_field( (string) $cond['product_operator'] ) : 'in_cart' );
                                $op = ( in_array( $op, $allowed_in_cart_ops, true ) ? $op : 'in_cart' );
                                $ids = ( isset( $cond['product_ids'] ) && is_array( $cond['product_ids'] ) ? array_map( 'absint', $cond['product_ids'] ) : [] );
                                $ids = array_values( array_unique( array_filter( $ids ) ) );
                                $cond_san['product_operator'] = $op;
                                $cond_san['product_ids'] = $ids;
                                if ( empty( $ids ) ) {
                                    $cond_valid = false;
                                }
                                break;
                            case 'product_category':
                                $op = ( isset( $cond['category_operator'] ) ? sanitize_text_field( (string) $cond['category_operator'] ) : 'in_cart' );
                                $op = ( in_array( $op, $allowed_in_cart_ops, true ) ? $op : 'in_cart' );
                                $ids = ( isset( $cond['category_ids'] ) && is_array( $cond['category_ids'] ) ? array_map( 'absint', $cond['category_ids'] ) : [] );
                                $ids = array_values( array_unique( array_filter( $ids ) ) );
                                $cond_san['category_operator'] = $op;
                                $cond_san['category_ids'] = $ids;
                                if ( empty( $ids ) ) {
                                    $cond_valid = false;
                                }
                                break;
                            case 'cart_total':
                                $op = ( isset( $cond['cart_total_operator'] ) ? sanitize_text_field( (string) $cond['cart_total_operator'] ) : 'above' );
                                $op = ( in_array( $op, $allowed_cart_ops, true ) ? $op : 'above' );
                                $amt = ( isset( $cond['cart_total_amount'] ) ? (float) $cond['cart_total_amount'] : 0.0 );
                                $amt = max( 0.0, (float) $amt );
                                $cond_san['cart_total_operator'] = $op;
                                $cond_san['cart_total_amount'] = $amt;
                                if ( $amt <= 0 ) {
                                    $cond_valid = false;
                                }
                                break;
                            case 'cart_coupon':
                                $codes = [];
                                if ( isset( $cond['coupon_codes'] ) ) {
                                    $raw_codes = $cond['coupon_codes'];
                                    if ( !is_array( $raw_codes ) ) {
                                        $raw_codes = explode( ',', (string) $raw_codes );
                                    }
                                    foreach ( $raw_codes as $code ) {
                                        $code = sanitize_text_field( (string) $code );
                                        $code = trim( $code );
                                        if ( '' !== $code ) {
                                            if ( function_exists( 'wc_format_coupon_code' ) ) {
                                                $code = wc_format_coupon_code( $code );
                                            } else {
                                                $code = strtolower( $code );
                                            }
                                            $codes[] = $code;
                                        }
                                    }
                                }
                                $codes = array_values( array_unique( array_filter( $codes ) ) );
                                $cond_san['coupon_codes'] = $codes;
                                if ( empty( $codes ) ) {
                                    $cond_valid = false;
                                }
                                break;
                            case 'customer':
                                $ids = ( isset( $cond['customer_ids'] ) && is_array( $cond['customer_ids'] ) ? array_map( 'absint', $cond['customer_ids'] ) : [] );
                                $ids = array_values( array_filter( $ids ) );
                                $cond_san['customer_ids'] = $ids;
                                if ( empty( $ids ) ) {
                                    $cond_valid = false;
                                }
                                break;
                            case 'user_role':
                                $roles = ( isset( $cond['user_roles'] ) && is_array( $cond['user_roles'] ) ? array_map( 'sanitize_text_field', $cond['user_roles'] ) : [] );
                                $roles = array_values( array_filter( $roles ) );
                                if ( !empty( $all_roles ) ) {
                                    $roles = array_values( array_intersect( $roles, $all_roles ) );
                                }
                                $cond_san['user_roles'] = $roles;
                                if ( empty( $roles ) ) {
                                    $cond_valid = false;
                                }
                                break;
                            case 'customer_total_points':
                                $op = ( isset( $cond['customer_total_points_operator'] ) ? sanitize_text_field( (string) $cond['customer_total_points_operator'] ) : 'above' );
                                $op = ( in_array( $op, $allowed_cart_ops, true ) ? $op : 'above' );
                                $amt = ( isset( $cond['customer_total_points_amount'] ) ? (int) $cond['customer_total_points_amount'] : 0 );
                                $amt = max( 0, (int) $amt );
                                $cond_san['customer_total_points_operator'] = $op;
                                $cond_san['customer_total_points_amount'] = $amt;
                                break;
                            case 'customer_available_points':
                                $op = ( isset( $cond['customer_available_points_operator'] ) ? sanitize_text_field( (string) $cond['customer_available_points_operator'] ) : 'above' );
                                $op = ( in_array( $op, $allowed_cart_ops, true ) ? $op : 'above' );
                                $amt = ( isset( $cond['customer_available_points_amount'] ) ? (int) $cond['customer_available_points_amount'] : 0 );
                                $amt = max( 0, (int) $amt );
                                $cond_san['customer_available_points_operator'] = $op;
                                $cond_san['customer_available_points_amount'] = $amt;
                                break;
                        }
                        if ( $cond_valid ) {
                            $conditions[] = $cond_san;
                        }
                    }
                    if ( !empty( $conditions ) ) {
                        $san['conditions'] = $conditions;
                        $first_condition = $conditions[0];
                        $san['condition_type'] = $first_condition['condition_type'];
                        foreach ( [
                            'product_operator',
                            'product_ids',
                            'category_operator',
                            'category_ids',
                            'category_item_operator',
                            'cart_total_operator',
                            'cart_total_amount',
                            'coupon_codes',
                            'customer_total_points_operator',
                            'customer_total_points_amount',
                            'customer_available_points_operator',
                            'customer_available_points_amount',
                            'customer_ids',
                            'user_roles'
                        ] as $cond_key ) {
                            if ( isset( $first_condition[$cond_key] ) ) {
                                $san[$cond_key] = $first_condition[$cond_key];
                            }
                        }
                    }
                    $valid = !empty( $conditions );
                    $san['draft'] = ( $valid ? 0 : 1 );
                    $sanitized_rules[] = $san;
                }
            }
            $options['earn_conditional_rules'] = array_values( $sanitized_rules );
            continue;
        } elseif ( $key === 'levels' ) {
            if ( !array_key_exists( 'levels', $post_data ) && !$form_complete ) {
                continue;
            }
            // Handle levels array specially with detailed processing
            $levels = ( isset( $post_data['levels'] ) ? wp_unslash( $post_data['levels'] ) : [] );
            $sanitized_levels = [];
            if ( !empty( $levels ) && is_array( $levels ) ) {
                foreach ( $levels as $level_data ) {
                    if ( !is_array( $level_data ) ) {
                        continue;
                    }
                    $raw_badge_type = sanitize_text_field( $level_data['badge_type'] ?? '' );
                    $allowed_badge_types = ['preset', 'custom_url', 'custom_text'];
                    $badge_type = ( in_array( $raw_badge_type, $allowed_badge_types, true ) ? $raw_badge_type : '' );
                    $badge_icon = sanitize_text_field( $level_data['badge_icon'] ?? '' );
                    $badge_url = esc_url_raw( $level_data['badge_url'] ?? '' );
                    $badge_text = sanitize_text_field( $level_data['badge_text'] ?? '' );
                    if ( '' === $badge_type ) {
                        if ( '' !== $badge_url ) {
                            $badge_type = 'custom_url';
                        } elseif ( '' !== $badge_text ) {
                            $badge_type = 'custom_text';
                        } elseif ( '' !== $badge_icon ) {
                            $badge_type = 'preset';
                        } else {
                            $badge_type = 'preset';
                        }
                    }
                    if ( 'custom_url' === $badge_type ) {
                        $badge_icon = '';
                        $badge_text = '';
                    } elseif ( 'custom_text' === $badge_type ) {
                        if ( '' === $badge_text && '' !== $badge_icon ) {
                            $badge_text = $badge_icon;
                        }
                        $badge_icon = '';
                        $badge_url = '';
                    } else {
                        $badge_type = 'preset';
                        $badge_text = '';
                        $badge_url = '';
                    }
                    $enabled_raw = $level_data['level_up_email_enabled'] ?? '';
                    if ( is_array( $enabled_raw ) && !empty( $enabled_raw ) ) {
                        $enabled_raw = end( $enabled_raw );
                    }
                    $enabled_raw = ( is_string( $enabled_raw ) ? trim( $enabled_raw ) : $enabled_raw );
                    $level_email_enabled = false;
                    if ( true === $enabled_raw || 1 === $enabled_raw ) {
                        $level_email_enabled = true;
                    } elseif ( is_numeric( $enabled_raw ) ) {
                        $level_email_enabled = (int) $enabled_raw > 0;
                    } elseif ( is_string( $enabled_raw ) ) {
                        $level_email_enabled = in_array( strtolower( $enabled_raw ), array(
                            '1',
                            'true',
                            'yes',
                            'on'
                        ), true );
                    }
                    $email_subject = ( isset( $level_data['level_up_email_subject'] ) ? sanitize_text_field( $level_data['level_up_email_subject'] ) : '' );
                    $email_body = ( isset( $level_data['level_up_email_body'] ) ? wp_kses_post( $level_data['level_up_email_body'] ) : '' );
                    // Sanitize per-earn-type multipliers (PRO only).
                    $custom_multipliers_on = false;
                    $sanitized_earn_multipliers = [];
                    $sanitized_level = [
                        'id'                         => sanitize_text_field( $level_data['id'] ?? '' ),
                        'name'                       => sanitize_text_field( $level_data['name'] ?? '' ),
                        'required_points'            => absint( $level_data['required_points'] ?? 0 ),
                        'badge_type'                 => $badge_type,
                        'badge_icon'                 => $badge_icon,
                        'badge_url'                  => $badge_url,
                        'badge_text'                 => $badge_text,
                        'points_multiplier'          => max( 0.0, floatval( $level_data['points_multiplier'] ?? 1.0 ) ),
                        'custom_multipliers_enabled' => $custom_multipliers_on,
                        'earn_type_multipliers'      => $sanitized_earn_multipliers,
                        'custom_benefits'            => sanitize_textarea_field( $level_data['custom_benefits'] ?? '' ),
                        'level_up_email_enabled'     => $level_email_enabled,
                        'level_up_email_subject'     => $email_subject,
                        'level_up_email_body'        => $email_body,
                    ];
                    if ( !empty( $sanitized_level['id'] ) ) {
                        $sanitized_levels[] = $sanitized_level;
                    }
                }
            }
            $options['levels'] = $sanitized_levels;
        } elseif ( $key === 'rewards' ) {
            if ( !array_key_exists( 'rewards', $post_data ) && !$form_complete ) {
                continue;
            }
            $rewards = ( isset( $post_data['rewards'] ) ? wp_unslash( $post_data['rewards'] ) : [] );
            $sanitized_rewards = [];
            $seen_dev_ids = [];
            if ( !empty( $rewards ) && is_array( $rewards ) ) {
                foreach ( $rewards as $reward_data ) {
                    if ( !is_array( $reward_data ) ) {
                        continue;
                    }
                    $sanitized_reward = [
                        'id'             => sanitize_text_field( $reward_data['id'] ?? '' ),
                        'name'           => sanitize_text_field( $reward_data['name'] ?? '' ),
                        'points'         => absint( $reward_data['points'] ?? 0 ),
                        'type'           => sanitize_text_field( $reward_data['type'] ?? 'voucher' ),
                        'status'         => sanitize_text_field( $reward_data['status'] ?? 'active' ),
                        'voucher_amount' => floatval( $reward_data['voucher_amount'] ?? 0 ),
                        'discount_type'  => sanitize_text_field( $reward_data['discount_type'] ?? 'fixed_cart' ),
                        'free_shipping'  => !empty( $reward_data['free_shipping'] ),
                        'product_id'     => absint( $reward_data['product_id'] ?? 0 ),
                    ];
                    if ( 'custom' === ($sanitized_reward['type'] ?? '') ) {
                        $custom_desc = sanitize_textarea_field( $reward_data['custom_description'] ?? '' );
                        if ( !empty( $custom_desc ) ) {
                            $sanitized_reward['custom_description'] = $custom_desc;
                        }
                        $developer_id = sanitize_key( $reward_data['developer_id'] ?? '' );
                        if ( !empty( $developer_id ) ) {
                            if ( in_array( $developer_id, $seen_dev_ids, true ) ) {
                                $dedupe_suffix = 2;
                                while ( in_array( $developer_id . '_' . $dedupe_suffix, $seen_dev_ids, true ) ) {
                                    $dedupe_suffix++;
                                }
                                $developer_id = $developer_id . '_' . $dedupe_suffix;
                            }
                            $sanitized_reward['developer_id'] = $developer_id;
                            $seen_dev_ids[] = $developer_id;
                        }
                    }
                    // Template coupon: validate it is a real published shop_coupon before saving.
                    $template_coupon_id = absint( $reward_data['template_coupon_id'] ?? 0 );
                    if ( $template_coupon_id > 0 && 'shop_coupon' === get_post_type( $template_coupon_id ) ) {
                        $sanitized_reward['template_coupon_id'] = $template_coupon_id;
                    }
                    if ( !empty( $sanitized_reward['id'] ) ) {
                        $sanitized_rewards[] = $sanitized_reward;
                    }
                }
            }
            $options['rewards'] = $sanitized_rewards;
        } else {
            $has_post = array_key_exists( $key, $post_data );
            if ( $has_post ) {
                $value = wp_unslash( $post_data[$key] );
                $options[$key] = spar_sanitize_field_value( $value, $field_data['type'], $field_data['default'] );
            } elseif ( 'checkbox' === $field_data['type'] && $form_complete ) {
                $options[$key] = false;
            }
        }
    }
    update_option( 'spar_options', $options );
    if ( !get_option( 'spar_user_settings_first_saved', 0 ) ) {
        update_option( 'spar_user_settings_first_saved', current_time( 'timestamp' ) );
    }
    return true;
}

add_action( 'wp_ajax_spar_generate_terms', 'spar_ajax_generate_terms' );
/**
 * AJAX: Generate Terms and Conditions content based on current plugin settings.
 */
function spar_ajax_generate_terms() {
    check_ajax_referer( 'spar_generate_terms', 'nonce' );
    if ( !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Permission denied', 'simple-points-and-rewards' ) );
    }
    $options = get_option( 'spar_options', [] );
    $site_name = get_bloginfo( 'name' );
    $points_label = ( isset( $options['points_label'] ) && '' !== $options['points_label'] ? esc_html( $options['points_label'] ) : esc_html__( 'Reward Points', 'simple-points-and-rewards' ) );
    $rewards_label = ( isset( $options['rewards_label'] ) && '' !== $options['rewards_label'] ? esc_html( $options['rewards_label'] ) : esc_html__( 'Rewards', 'simple-points-and-rewards' ) );
    $date_str = date_i18n( get_option( 'date_format' ) );
    // ---- Collect active earning methods ----
    $earn = ( isset( $options['earn'] ) && is_array( $options['earn'] ) ? $options['earn'] : [] );
    $earn_items = [];
    if ( !empty( $earn['signup']['enabled'] ) ) {
        $pts = ( isset( $earn['signup']['points'] ) ? (int) $earn['signup']['points'] : 0 );
        /* translators: %1$s: points amount, %2$s: points label */
        $earn_items[] = sprintf( esc_html__( 'Creating a new account (%1$s %2$s)', 'simple-points-and-rewards' ), number_format_i18n( $pts ), $points_label );
    }
    if ( !empty( $earn['order']['enabled'] ) || !empty( $earn['order_fixed']['enabled'] ) ) {
        $earn_items[] = esc_html__( 'Completing a qualifying purchase', 'simple-points-and-rewards' );
    }
    if ( !empty( $earn['first_order']['enabled'] ) ) {
        $pts = ( isset( $earn['first_order']['points'] ) ? (int) $earn['first_order']['points'] : 0 );
        /* translators: %1$s: points amount, %2$s: points label */
        $earn_items[] = sprintf( esc_html__( 'Placing your first order (%1$s %2$s bonus)', 'simple-points-and-rewards' ), number_format_i18n( $pts ), $points_label );
    }
    if ( !empty( $earn['nth_order']['enabled'] ) ) {
        $n = ( isset( $earn['nth_order']['order_count'] ) ? (int) $earn['nth_order']['order_count'] : 0 );
        $pts = ( isset( $earn['nth_order']['points'] ) ? (int) $earn['nth_order']['points'] : 0 );
        /* translators: %1$s: order number, %2$s: points amount, %3$s: points label */
        $earn_items[] = sprintf(
            esc_html__( 'Reaching your %1$s%2$s order (%3$s %4$s bonus)', 'simple-points-and-rewards' ),
            number_format_i18n( $n ),
            esc_html__( 'th', 'simple-points-and-rewards' ),
            number_format_i18n( $pts ),
            $points_label
        );
    }
    if ( !empty( $earn['referral']['enabled'] ) ) {
        $earn_items[] = esc_html__( 'Referring a friend who makes a purchase', 'simple-points-and-rewards' );
    }
    if ( !empty( $earn['review']['enabled'] ) ) {
        $earn_items[] = esc_html__( 'Leaving a verified product review', 'simple-points-and-rewards' );
    }
    if ( !empty( $earn['birthday']['enabled'] ) ) {
        $pts = ( isset( $earn['birthday']['points'] ) ? (int) $earn['birthday']['points'] : 0 );
        /* translators: %1$s: points amount, %2$s: points label */
        $earn_items[] = sprintf( esc_html__( 'Your birthday (%1$s %2$s gift)', 'simple-points-and-rewards' ), number_format_i18n( $pts ), $points_label );
    }
    if ( !empty( $earn['daily_login']['enabled'] ) ) {
        $pts = ( isset( $earn['daily_login']['points'] ) ? (int) $earn['daily_login']['points'] : 0 );
        /* translators: %1$s: points amount, %2$s: points label */
        $earn_items[] = sprintf( esc_html__( 'Logging in daily (%1$s %2$s per day)', 'simple-points-and-rewards' ), number_format_i18n( $pts ), $points_label );
    }
    if ( !empty( $earn['spin_wheel']['enabled'] ) ) {
        $name = ( isset( $earn['spin_wheel']['name'] ) && '' !== $earn['spin_wheel']['name'] ? esc_html( $earn['spin_wheel']['name'] ) : esc_html__( 'Prize Wheel', 'simple-points-and-rewards' ) );
        /* translators: %s: spin wheel name */
        $earn_items[] = sprintf( esc_html__( 'Spinning the %s', 'simple-points-and-rewards' ), $name );
    }
    // ---- Collect active rewards ----
    $rewards = ( isset( $options['rewards'] ) && is_array( $options['rewards'] ) ? $options['rewards'] : [] );
    $reward_items = [];
    foreach ( $rewards as $reward ) {
        if ( !is_array( $reward ) ) {
            continue;
        }
        $status = ( isset( $reward['status'] ) ? $reward['status'] : 'active' );
        if ( 'inactive' === $status ) {
            continue;
        }
        $name = ( isset( $reward['name'] ) && '' !== $reward['name'] ? esc_html( $reward['name'] ) : '' );
        $points = ( isset( $reward['points'] ) ? (int) $reward['points'] : 0 );
        if ( '' === $name || $points <= 0 ) {
            continue;
        }
        /* translators: %1$s: reward name, %2$s: points cost, %3$s: points label */
        $reward_items[] = sprintf(
            esc_html__( '%1$s (%2$s %3$s)', 'simple-points-and-rewards' ),
            $name,
            number_format_i18n( $points ),
            $points_label
        );
    }
    // ---- Collect active levels ----
    $levels_enabled = !empty( $options['levels_enabled'] );
    $level_names = [];
    if ( $levels_enabled ) {
        $starter_name = ( isset( $options['levels_starter_name'] ) && '' !== $options['levels_starter_name'] ? esc_html( $options['levels_starter_name'] ) : esc_html__( 'Starter', 'simple-points-and-rewards' ) );
        $level_names[] = $starter_name;
        $saved_levels = ( isset( $options['levels'] ) && is_array( $options['levels'] ) ? $options['levels'] : [] );
        foreach ( $saved_levels as $level ) {
            if ( is_array( $level ) && !empty( $level['name'] ) ) {
                $level_names[] = esc_html( $level['name'] );
            }
        }
    }
    // ---- Expiry ----
    $expiry_enabled = !empty( $options['points_inactivity_expiry_enabled'] );
    $expiry_days = ( isset( $options['points_inactivity_expiry_days'] ) ? (int) $options['points_inactivity_expiry_days'] : 0 );
    // ---- Voucher redemption ----
    $redeem_rate_points = ( isset( $options['redeem_points_per_points'] ) ? (float) $options['redeem_points_per_points'] : 100 );
    $redeem_rate_amount = ( isset( $options['redeem_points_per_amount'] ) ? (float) $options['redeem_points_per_amount'] : 1 );
    $currency_symbol = ( function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : '$' );
    // ---- Build the HTML content ----
    $section_num = 0;
    $html = '<h3>' . sprintf( 
        /* translators: %1$s: rewards label, %2$s: site name */
        esc_html__( '%1$s Program – Terms and Conditions', 'simple-points-and-rewards' ),
        $rewards_label
     ) . '</h3>';
    $html .= '<p>' . sprintf( 
        /* translators: %1$s: site name, %2$s: rewards label */
        esc_html__( 'Welcome to the %1$s %2$s Program. By participating, you agree to the following terms and conditions. Please read them carefully.', 'simple-points-and-rewards' ),
        esc_html( $site_name ),
        $rewards_label
     ) . '</p>';
    // 1. Eligibility
    $section_num++;
    $html .= '<h4>' . $section_num . '. ' . esc_html__( 'Eligibility', 'simple-points-and-rewards' ) . '</h4>';
    $html .= '<p>' . esc_html__( 'The rewards program is open to registered account holders only. By creating an account and participating in the program, you confirm that you agree to these terms. We reserve the right to refuse or revoke participation at our discretion.', 'simple-points-and-rewards' ) . '</p>';
    // 2. Earning Points
    $section_num++;
    $html .= '<h4>' . $section_num . '. ' . sprintf( esc_html__( 'Earning %s', 'simple-points-and-rewards' ), $points_label ) . '</h4>';
    if ( !empty( $earn_items ) ) {
        $html .= '<p>' . sprintf( 
            /* translators: %s: points label */
            esc_html__( 'You can earn %s by completing the following actions:', 'simple-points-and-rewards' ),
            $points_label
         ) . '</p>';
        $html .= '<ul>';
        foreach ( $earn_items as $item ) {
            $html .= '<li>' . $item . '</li>';
        }
        $html .= '</ul>';
    } else {
        $html .= '<p>' . sprintf( 
            /* translators: %s: points label */
            esc_html__( '%s are awarded for qualifying actions as outlined in the rewards dashboard. Points are credited once the qualifying action is confirmed.', 'simple-points-and-rewards' ),
            $points_label
         ) . '</p>';
    }
    $html .= '<p>' . sprintf( 
        /* translators: %s: points label */
        esc_html__( '%s have no cash value and are non-transferable. They may not be combined with other offers unless stated otherwise.', 'simple-points-and-rewards' ),
        $points_label
     ) . '</p>';
    // 3. Redeeming / Rewards
    $section_num++;
    $html .= '<h4>' . $section_num . '. ' . sprintf( esc_html__( 'Redeeming %s', 'simple-points-and-rewards' ), $points_label ) . '</h4>';
    if ( !empty( $reward_items ) ) {
        $html .= '<p>' . sprintf( 
            /* translators: %s: points label */
            esc_html__( 'Accumulated %s may be exchanged for the following rewards:', 'simple-points-and-rewards' ),
            $points_label
         ) . '</p>';
        $html .= '<ul>';
        foreach ( $reward_items as $item ) {
            $html .= '<li>' . $item . '</li>';
        }
        $html .= '</ul>';
    } else {
        $html .= '<p>' . sprintf( 
            /* translators: %s: points label */
            esc_html__( 'Accumulated %s may be redeemed for rewards as listed in the rewards dashboard. Rewards are subject to availability and may change at any time.', 'simple-points-and-rewards' ),
            $points_label
         ) . '</p>';
    }
    // Exchange rate clause
    $rate_amount_fmt = ( function_exists( 'wc_price' ) ? wc_price( $redeem_rate_amount ) : $currency_symbol . number_format_i18n( $redeem_rate_amount, 2 ) );
    $html .= '<p>' . sprintf(
        /* translators: %1$s: points amount, %2$s: points label, %3$s: currency amount */
        esc_html__( 'The standard redemption rate is %1$s %2$s = %3$s discount, unless otherwise specified for a particular reward.', 'simple-points-and-rewards' ),
        number_format_i18n( $redeem_rate_points ),
        $points_label,
        $rate_amount_fmt
    ) . '</p>';
    // 4. Levels (conditional)
    if ( $levels_enabled && !empty( $level_names ) ) {
        $section_num++;
        $html .= '<h4>' . $section_num . '. ' . esc_html__( 'Levels & Badges', 'simple-points-and-rewards' ) . '</h4>';
        $html .= '<p>' . sprintf( 
            /* translators: %s: points label */
            esc_html__( 'The program features a tiered level system. Your level is determined by your total %s earned over time. Levels may unlock additional benefits such as bonus multipliers. Current levels include:', 'simple-points-and-rewards' ),
            $points_label
         ) . ' ' . implode( ', ', $level_names ) . '.</p>';
    }
    // 5. Validity / Expiry
    $section_num++;
    $html .= '<h4>' . $section_num . '. ' . sprintf( esc_html__( '%s Validity', 'simple-points-and-rewards' ), $points_label ) . '</h4>';
    if ( $expiry_enabled && $expiry_days > 0 ) {
        $html .= '<p>' . sprintf( 
            /* translators: %1$s: points label, %2$s: number of days */
            _n(
                '%1$s will expire after %2$s day of account inactivity (no new %1$s earned). Earning new points at any time resets the inactivity timer.',
                '%1$s will expire after %2$s days of account inactivity (no new %1$s earned). Earning new points at any time resets the inactivity timer.',
                $expiry_days,
                'simple-points-and-rewards'
            ),
            $points_label,
            number_format_i18n( $expiry_days )
         ) . '</p>';
    } else {
        $html .= '<p>' . sprintf( 
            /* translators: %s: points label */
            esc_html__( '%s remain valid while your account is in good standing. We reserve the right to introduce expiry policies with reasonable advance notice.', 'simple-points-and-rewards' ),
            $points_label
         ) . '</p>';
    }
    // 6. Account Conduct
    $section_num++;
    $html .= '<h4>' . $section_num . '. ' . esc_html__( 'Account Conduct & Fraud', 'simple-points-and-rewards' ) . '</h4>';
    $html .= '<p>' . sprintf( 
        /* translators: %s: points label */
        esc_html__( 'Any attempt to manipulate, abuse, or fraudulently earn %s — including but not limited to fake accounts, fraudulent orders, or automated activity — may result in the immediate suspension of your account, cancellation of all accumulated points, and exclusion from the program.', 'simple-points-and-rewards' ),
        $points_label
     ) . '</p>';
    // 7. Program Changes
    $section_num++;
    $html .= '<h4>' . $section_num . '. ' . esc_html__( 'Program Changes', 'simple-points-and-rewards' ) . '</h4>';
    $html .= '<p>' . sprintf( 
        /* translators: %s: site name */
        esc_html__( '%s reserves the right to modify, suspend, or discontinue any aspect of the rewards program at any time, including point values, reward availability, and earning rules. Where possible, significant changes will be communicated in advance.', 'simple-points-and-rewards' ),
        esc_html( $site_name )
     ) . '</p>';
    // 8. Governing Law
    $section_num++;
    $html .= '<h4>' . $section_num . '. ' . esc_html__( 'Governing Law', 'simple-points-and-rewards' ) . '</h4>';
    $html .= '<p>' . esc_html__( 'These terms are governed by applicable local laws. Continued participation in the program constitutes acceptance of these terms and any future amendments.', 'simple-points-and-rewards' ) . '</p>';
    $html .= '<p><em>' . esc_html__( 'Last updated: ', 'simple-points-and-rewards' ) . esc_html( $date_str ) . '</em></p>';
    wp_send_json_success( [
        'html' => $html,
    ] );
}

add_action( 'wp_ajax_spar_save_settings_ajax', 'spar_save_settings_ajax_handler' );
function spar_save_settings_ajax_handler() {
    check_ajax_referer( 'spar_save_settings_action', 'nonce' );
    if ( !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Permission denied', 'simple-points-and-rewards' ) );
    }
    // Parse the serialized form data
    if ( empty( $_POST['data'] ) || !is_string( $_POST['data'] ) ) {
        wp_send_json_error( esc_html__( 'Settings payload missing', 'simple-points-and-rewards' ) );
    }
    $params = [];
    parse_str( wp_unslash( $_POST['data'] ), $params );
    if ( empty( $params ) || !is_array( $params ) ) {
        wp_send_json_error( esc_html__( 'Settings payload invalid', 'simple-points-and-rewards' ) );
    }
    if ( empty( $params['spar_form_end'] ) ) {
        wp_send_json_error( esc_html__( 'Settings form truncated', 'simple-points-and-rewards' ) );
    }
    if ( spar_process_settings_save( $params ) ) {
        wp_send_json_success( esc_html__( 'Settings saved', 'simple-points-and-rewards' ) );
    } else {
        wp_send_json_error( esc_html__( 'Failed to save settings', 'simple-points-and-rewards' ) );
    }
}

function spar_handle_admin_load_points_history_ajax() {
    check_ajax_referer( 'spar_customer_detail_pagination', 'nonce', true );
    if ( !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Insufficient permissions.', 'simple-points-and-rewards' ) );
    }
    $user_id = ( isset( $_POST['user_id'] ) ? absint( $_POST['user_id'] ) : 0 );
    if ( !$user_id || !get_userdata( $user_id ) ) {
        wp_send_json_error( esc_html__( 'Invalid user ID.', 'simple-points-and-rewards' ) );
    }
    $page = max( 1, intval( $_POST['page'] ?? 1 ) );
    $per_page = 10;
    $history_data = ( function_exists( 'spar_get_user_points_history' ) ? spar_get_user_points_history( $user_id, $page, $per_page ) : array(
        'logs'       => array(),
        'pagination' => array(),
    ) );
    $logs = $history_data['logs'] ?? array();
    $pagination = $history_data['pagination'] ?? array();
    $table_html = ( function_exists( 'spar_build_customer_detail_points_rows' ) ? spar_build_customer_detail_points_rows( $logs ) : '' );
    $pagination_html = ( function_exists( 'spar_build_customer_detail_pagination_html' ) ? spar_build_customer_detail_pagination_html( $pagination, 'points' ) : '' );
    wp_send_json_success( array(
        'table_html'      => $table_html,
        'pagination_html' => $pagination_html,
    ) );
}

function spar_handle_admin_load_referral_clicks_ajax() {
    check_ajax_referer( 'spar_customer_detail_pagination', 'nonce', true );
    if ( !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Insufficient permissions.', 'simple-points-and-rewards' ) );
    }
    $user_id = ( isset( $_POST['user_id'] ) ? absint( $_POST['user_id'] ) : 0 );
    if ( !$user_id || !get_userdata( $user_id ) ) {
        wp_send_json_error( esc_html__( 'Invalid user ID.', 'simple-points-and-rewards' ) );
    }
    $page = max( 1, intval( $_POST['page'] ?? 1 ) );
    $per_page = 10;
    $history_data = ( function_exists( 'spar_get_user_referral_clicks_history_full' ) ? spar_get_user_referral_clicks_history_full( $user_id, $page, $per_page ) : array(
        'logs'       => array(),
        'pagination' => array(),
    ) );
    $logs = $history_data['logs'] ?? array();
    $pagination = $history_data['pagination'] ?? array();
    $table_html = ( function_exists( 'spar_build_customer_detail_referral_rows' ) ? spar_build_customer_detail_referral_rows( $logs ) : '' );
    $pagination_html = ( function_exists( 'spar_build_customer_detail_pagination_html' ) ? spar_build_customer_detail_pagination_html( $pagination, 'referrals' ) : '' );
    wp_send_json_success( array(
        'table_html'      => $table_html,
        'pagination_html' => $pagination_html,
    ) );
}

function spar_handle_admin_load_customer_orders_ajax() {
    check_ajax_referer( 'spar_customer_detail_pagination', 'nonce', true );
    if ( !current_user_can( 'manage_woocommerce' ) ) {
        wp_send_json_error( esc_html__( 'Insufficient permissions.', 'simple-points-and-rewards' ) );
    }
    $user_id = ( isset( $_POST['user_id'] ) ? absint( $_POST['user_id'] ) : 0 );
    if ( !$user_id || !get_userdata( $user_id ) ) {
        wp_send_json_error( esc_html__( 'Invalid user ID.', 'simple-points-and-rewards' ) );
    }
    if ( !function_exists( 'wc_get_orders' ) ) {
        wp_send_json_error( esc_html__( 'Orders unavailable.', 'simple-points-and-rewards' ) );
    }
    $page = max( 1, intval( $_POST['page'] ?? 1 ) );
    $per_page = 10;
    $orders_query = wc_get_orders( array(
        'customer_id' => $user_id,
        'limit'       => $per_page,
        'orderby'     => 'date',
        'order'       => 'DESC',
        'paginate'    => true,
        'paged'       => $page,
    ) );
    $orders = array();
    $pagination = array();
    if ( is_object( $orders_query ) ) {
        $orders = $orders_query->orders;
        $total_pages = (int) $orders_query->max_num_pages;
        $pagination = array(
            'current_page' => $page,
            'per_page'     => $per_page,
            'total_count'  => (int) $orders_query->total,
            'total_pages'  => $total_pages,
            'has_previous' => $page > 1,
            'has_next'     => $page < $total_pages,
        );
    }
    $points_label = ( function_exists( 'spar_get_option' ) ? spar_get_option( '', 'points_label' ) : '' );
    $points_label = ( $points_label ? $points_label : esc_html__( 'Points', 'simple-points-and-rewards' ) );
    $table_html = ( function_exists( 'spar_build_customer_detail_orders_rows' ) ? spar_build_customer_detail_orders_rows( $orders, $points_label, $user_id ) : '' );
    $pagination_html = ( function_exists( 'spar_build_customer_detail_pagination_html' ) ? spar_build_customer_detail_pagination_html( $pagination, 'orders' ) : '' );
    wp_send_json_success( array(
        'table_html'      => $table_html,
        'pagination_html' => $pagination_html,
    ) );
}
