<?php

/**
 * Settings utilities and common functions
 *
 * Central place for settings schema, defaults, sanitization, and helpers used by the admin UI.
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
function spar_get_option_fields() {
    // Define all settings and their defaults/types in one array.
    // Keeping this centralized makes rendering, sanitizing, and migrations simpler.
    $fields = [
        'earn'                                         => [
            'signup'         => [
                'enabled'            => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'points'             => [
                    'type'    => 'number',
                    'default' => 100,
                ],
                'name'               => [
                    'type'    => 'text',
                    'default' => esc_html__( 'Signup Bonus', 'simple-points-and-rewards' ),
                ],
                'show_guest_message' => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
            ],
            'daily_login'    => [
                'enabled'        => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'points'         => [
                    'type'    => 'number',
                    'default' => 10,
                ],
                'name'           => [
                    'type'    => 'text',
                    'default' => esc_html__( 'Daily Login Bonus', 'simple-points-and-rewards' ),
                ],
                'streak_enabled' => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'streak_days'    => [
                    'type'    => 'number',
                    'default' => 7,
                ],
                'streak_points'  => [
                    'type'    => 'number',
                    'default' => 50,
                ],
                'streak_type'    => [
                    'type'    => 'select',
                    'default' => 'recurring',
                ],
            ],
            'spin_wheel'     => [
                'enabled'                => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'name'                   => [
                    'type'    => 'text',
                    'default' => '',
                ],
                'description'            => [
                    'type'    => 'text',
                    'default' => '',
                ],
                'button_text'            => [
                    'type'    => 'text',
                    'default' => '',
                ],
                'spin_button_text'       => [
                    'type'    => 'text',
                    'default' => '',
                ],
                'popup_style'            => [
                    'type'    => 'select',
                    'default' => 'light',
                ],
                'cooldown'               => [
                    'type'    => 'select',
                    'default' => 'daily',
                ],
                'cooldown_hours'         => [
                    'type'    => 'number',
                    'default' => 24,
                ],
                'show_probabilities'     => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'spin_sound'             => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'celebration_sound'      => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'show_confetti'          => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'prizes'                 => [
                    'type'    => 'array',
                    'default' => [],
                ],
                'order_spins_enabled'    => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'order_spins_amount'     => [
                    'type'    => 'float',
                    'default' => 0,
                ],
                'level_up_spins_enabled' => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'level_up_spins_amount'  => [
                    'type'    => 'number',
                    'default' => 1,
                ],
                'buy_spins_enabled'      => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'buy_spins_points_cost'  => [
                    'type'    => 'number',
                    'default' => 100,
                ],
                'buy_spins_heading'      => [
                    'type'    => 'text',
                    'default' => '',
                ],
                'buy_spins_description'  => [
                    'type'    => 'text',
                    'default' => '',
                ],
                'buy_spins_button_text'  => [
                    'type'    => 'text',
                    'default' => '',
                ],
            ],
            'birthday'       => [
                'enabled' => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'points'  => [
                    'type'    => 'number',
                    'default' => 100,
                ],
                'name'    => [
                    'type'    => 'text',
                    'default' => esc_html__( 'Birthday Bonus', 'simple-points-and-rewards' ),
                ],
            ],
            'review'         => [
                'enabled' => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'points'  => [
                    'type'    => 'number',
                    'default' => 100,
                ],
                'limit'   => [
                    'type'    => 'number',
                    'default' => 1,
                ],
                'name'    => [
                    'type'    => 'text',
                    'default' => esc_html__( 'Write a Review', 'simple-points-and-rewards' ),
                ],
            ],
            'order'          => [
                'enabled'                      => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'points_per'                   => [
                    'type'    => 'float',
                    'default' => 5,
                ],
                'points_per_points'            => [
                    'type'    => 'float',
                    'default' => 5,
                ],
                'points_per_amount'            => [
                    'type'    => 'float',
                    'default' => 1,
                ],
                'calculation_total_mode'       => [
                    'type'    => 'select',
                    'default' => 'subtotal_after_discount',
                ],
                'calculation_include_shipping' => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'calculation_include_taxes'    => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'name'                         => [
                    'type'    => 'text',
                    'default' => esc_html__( 'Place an Order', 'simple-points-and-rewards' ),
                ],
                'award_timing'                 => [
                    'type'    => 'select',
                    'default' => 'thankyou',
                ],
                'completed_status'             => [
                    'type'    => 'select',
                    'default' => 'completed',
                ],
                'deduct_on_refund'             => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
            ],
            'order_fixed'    => [
                'enabled'                      => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'name'                         => [
                    'type'    => 'text',
                    'default' => esc_html__( 'Points for Orders', 'simple-points-and-rewards' ),
                ],
                'calculation_total_mode'       => [
                    'type'    => 'select',
                    'default' => 'subtotal_after_discount',
                ],
                'calculation_include_shipping' => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'calculation_include_taxes'    => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'deduct_on_refund'             => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'tiers'                        => [
                    'type'    => 'array',
                    'default' => [],
                ],
            ],
            'referral'       => [
                'enabled'                   => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'earning_type'              => [
                    'type'    => 'select',
                    'default' => 'fixed',
                ],
                'percentage'                => [
                    'type'    => 'float',
                    'default' => 10,
                ],
                'name'                      => [
                    'type'    => 'text',
                    'default' => esc_html__( 'Referral Bonus', 'simple-points-and-rewards' ),
                ],
                'points_per'                => [
                    'type'    => 'float',
                    'default' => 5,
                ],
                'fixed_points'              => [
                    'type'    => 'number',
                    'default' => 100,
                ],
                'award_timing'              => [
                    'type'    => 'select',
                    'default' => 'completed',
                ],
                'deduct_on_refund'          => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'show_clicks_log'           => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'social_enabled'            => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'social_email_enabled'      => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'social_facebook_enabled'   => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'social_twitter_enabled'    => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'social_whatsapp_enabled'   => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'offer_enabled'             => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'offer_type'                => [
                    'type'    => 'select',
                    'default' => 'discount',
                ],
                'offer_value'               => [
                    'type'    => 'float',
                    'default' => 10,
                ],
                'offer_text'                => [
                    'type'    => 'text',
                    'default' => esc_html__( 'Get {value} off your order!', 'simple-points-and-rewards' ),
                ],
                'offer_show_countdown'      => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'offer_expiry_hours'        => [
                    'type'    => 'number',
                    'default' => 24,
                ],
                'gift_widget_enabled'       => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'gift_cart_enabled'         => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'gift_checkout_enabled'     => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'gift_widget_position'      => [
                    'type'    => 'select',
                    'default' => 'bottom-right',
                ],
                'gift_widget_color'         => [
                    'type'    => 'text',
                    'default' => '#667eea',
                ],
                'gift_widget_message'       => [
                    'type'    => 'text',
                    'default' => esc_html__( 'A customer has sent you a gift coupon!', 'simple-points-and-rewards' ),
                ],
                'gift_widget_personalize'   => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'gift_first_order_only'     => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'gift_attribution_model'    => [
                    'type'    => 'select',
                    'default' => 'first_click',
                ],
                'block_self_referral'       => [
                    'type'    => 'checkbox',
                    'default' => true,
                ],
                'template_coupon_id'        => [
                    'type'    => 'number',
                    'default' => 0,
                ],
                'gift_usage_limit_per_user' => [
                    'type'    => 'select',
                    'default' => 'email',
                ],
                'coupon_tracking_mode'      => [
                    'type'    => 'select',
                    'default' => 'cookie_required',
                ],
            ],
            'social_sharing' => [
                'referral_social_points_enabled' => [
                    'type'    => 'checkbox',
                    'default' => false,
                ],
                'social_points'                  => [
                    'type'    => 'number',
                    'default' => 10,
                ],
                'social_limit_mode'              => [
                    'type'    => 'select',
                    'default' => 'per_social',
                ],
            ],
        ],
        'earn_conditional_rules'                       => [
            'type'    => 'array',
            'default' => [],
        ],
        'earn_ways_order'                              => [
            'type'    => 'array',
            'default' => [
                'signup',
                'order',
                'order_fixed',
                'referral',
                'first_order',
                'nth_order',
                'review',
                'birthday',
                'daily_login',
                'spin_wheel'
            ],
        ],
        'rewards_label'                                => [
            'type'    => 'text',
            'default' => esc_html__( 'Rewards', 'simple-points-and-rewards' ),
        ],
        'points_label'                                 => [
            'type'    => 'text',
            'default' => esc_html__( 'Reward Points', 'simple-points-and-rewards' ),
        ],
        'rewards_vouchers_enabled'                     => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'show_on_product'                              => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'product_points_location'                      => [
            'type'    => 'select',
            'default' => 'summary_after_price',
        ],
        'product_points_message'                       => [
            'type'    => 'text',
            'default' => '',
        ],
        'product_points_text_color'                    => [
            'type'    => 'color',
            'default' => '',
        ],
        'product_points_bg_color'                      => [
            'type'    => 'color',
            'default' => '',
        ],
        'show_on_product_loop'                         => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'product_loop_points_location'                 => [
            'type'    => 'select',
            'default' => 'loop_after_title',
        ],
        'product_loop_points_message'                  => [
            'type'    => 'text',
            'default' => '',
        ],
        'product_loop_points_text_color'               => [
            'type'    => 'color',
            'default' => '',
        ],
        'product_loop_points_bg_color'                 => [
            'type'    => 'color',
            'default' => '',
        ],
        'show_on_cart'                                 => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'show_on_checkout'                             => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'show_on_thankyou'                             => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'show_for_guests'                              => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'checkout_box_show_points_summary'             => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'rewards_box_theme'                            => [
            'type'    => 'select',
            'default' => 'default',
        ],
        'checkout_box_theme_style'                     => [
            'type'    => 'select',
            'default' => 'light',
        ],
        'checkout_box_redeem_button_text'              => [
            'type'    => 'text',
            'default' => '',
        ],
        'checkout_box_primary_color'                   => [
            'type'    => 'color',
            'default' => '#667eea',
        ],
        'checkout_box_secondary_color'                 => [
            'type'    => 'color',
            'default' => '#28a745',
        ],
        'checkout_box_enable_confetti'                 => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'link_to_rewards'                              => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'show_rewards_in_my_account'                   => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'rewards_page_id'                              => [
            'type'    => 'number',
            'default' => 0,
        ],
        'dashboard_header_text'                        => [
            'type'    => 'text',
            'default' => '',
        ],
        'dashboard_subheader_text'                     => [
            'type'    => 'text',
            'default' => '',
        ],
        'dashboard_tabs_order'                         => [
            'type'    => 'array',
            'default' => [
                'earn',
                'claim',
                'vouchers',
                'levels',
                'history',
                'settings'
            ],
        ],
        'dashboard_tab_labels'                         => [
            'type'    => 'array',
            'default' => [
                'earn'     => esc_html__( 'Earn Points', 'simple-points-and-rewards' ),
                'claim'    => esc_html__( 'Claim Rewards', 'simple-points-and-rewards' ),
                'vouchers' => esc_html__( 'My Vouchers', 'simple-points-and-rewards' ),
                'levels'   => esc_html__( 'Levels', 'simple-points-and-rewards' ),
                'history'  => esc_html__( 'History', 'simple-points-and-rewards' ),
                'settings' => esc_html__( 'Settings', 'simple-points-and-rewards' ),
            ],
        ],
        'earn_points_text_enabled'                     => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'earn_points_text'                             => [
            'type'    => 'textarea',
            'default' => '',
        ],
        'redeem_text'                                  => [
            'type'    => 'textarea',
            'default' => '',
        ],
        'reward_name'                                  => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'reward_points'                                => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'reward_value'                                 => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'remaining_text'                               => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'redemption_bar'                               => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'account_enable_confetti'                      => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'redeem_individual_enabled'                    => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'redeem_individual_display'                    => [
            'type'    => 'select',
            'default' => 'both',
        ],
        'redeem_individual_message'                    => [
            'type'    => 'text',
            'default' => '',
        ],
        'redeem_button_text'                           => [
            'type'    => 'text',
            'default' => '',
        ],
        'redeem_text_color'                            => [
            'type'    => 'color',
            'default' => '',
        ],
        'redeem_points_per_points'                     => [
            'type'    => 'float',
            'default' => 100,
        ],
        'redeem_points_per_amount'                     => [
            'type'    => 'float',
            'default' => 1,
        ],
        'redeem_points_min'                            => [
            'type'    => 'number',
            'default' => 0,
        ],
        'redeem_points_max'                            => [
            'type'    => 'number',
            'default' => 0,
        ],
        'redeem_max_cart_percentage'                   => [
            'type'    => 'number',
            'default' => 0,
        ],
        'rewards_theme_color_1'                        => [
            'type'    => 'color',
            'default' => '#667eea',
        ],
        'rewards_theme_color_2'                        => [
            'type'    => 'color',
            'default' => '#764ba2',
        ],
        'rewards_theme_accent_color'                   => [
            'type'    => 'color',
            'default' => '#2ca58d',
        ],
        'dashboard_dark_mode_toggle'                   => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'dashboard_dark_mode_default'                  => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'dashboard_dark_mode_header'                   => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'dashboard_dark_mode_hide_toggle_when_default' => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'enable_earned'                                => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'enable_claimed'                               => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'subject_earned'                               => [
            'type'    => 'text',
            'default' => esc_html__( 'Great news! You\'ve earned {points} {points_label}!', 'simple-points-and-rewards' ),
        ],
        'subject_claimed'                              => [
            'type'    => 'text',
            'default' => esc_html__( 'Your reward is ready! Voucher code: {voucher_code}', 'simple-points-and-rewards' ),
        ],
        'body_earned'                                  => [
            'type'    => 'editor',
            'default' => '<p>Hi {user_name},</p>

	<p>🎉 <strong>Congratulations!</strong> You\'ve just earned <strong>{points} {points_label}</strong> for: <em>{action}</em></p>

	<p><strong>Your account summary:</strong><br>
	💰 Total {points_label}: <strong>{total_points}</strong></p>

	<p>Ready to turn your points into rewards? <a href="{rewards_url}">Visit your rewards dashboard</a> to see what\'s available!</p>

	<p>Keep earning and enjoying the benefits!</p>

	<p>Best regards,<br>
	The {site_name} Team</p>',
        ],
        'body_claimed'                                 => [
            'type'    => 'editor',
            'default' => '<p>Hi {user_name},</p>

	<p>🎊 <strong>Fantastic!</strong> Your reward has been successfully claimed!</p>

	<p><strong>Reward Details:</strong><br>
	🎁 Reward: <strong>{voucher_type}</strong><br>
	🎫 Voucher Code: <strong>{voucher_code}</strong><br>
	💳 Points Used: <strong>{points} {points_label}</strong></p>

	<p><strong>How to use your voucher:</strong><br>
	Simply enter the code <strong>{voucher_code}</strong> at checkout, or <a href="{apply_coupon_url}">click here to apply it automatically</a>.</p>

	<p><strong>Your current balance:</strong><br>
	💰 Remaining {points_label}: <strong>{total_points}</strong></p>

	<p>Want to earn more points? <a href="{rewards_url}">Check out all the ways to earn</a>!</p>

	<p>Happy shopping!</p>

	<p>Best regards,<br>
	The {site_name} Team</p>',
        ],
        'enable_earned_signup'                         => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'subject_earned_signup'                        => [
            'type'    => 'text',
            'default' => esc_html__( 'Welcome! You earned {points} {points_label} for signing up', 'simple-points-and-rewards' ),
        ],
        'body_earned_signup'                           => [
            'type'    => 'editor',
            'default' => '<p>Hi {user_name},</p><p>You just earned <strong>{points} {points_label}</strong> for: <em>{action}</em>.</p><p>Your current balance is <strong>{total_points}</strong>.</p><p><a href="{rewards_url}">See your rewards</a></p><p>- {site_name}</p>',
        ],
        'enable_earned_order'                          => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'subject_earned_order'                         => [
            'type'    => 'text',
            'default' => esc_html__( 'You earned {points} {points_label} for your order', 'simple-points-and-rewards' ),
        ],
        'body_earned_order'                            => [
            'type'    => 'editor',
            'default' => '<p>Hi {user_name},</p><p>Thanks for your purchase! You earned <strong>{points} {points_label}</strong>.</p><p>Total balance: <strong>{total_points}</strong>.</p><p><a href="{rewards_url}">Redeem your points</a></p><p>- {site_name}</p>',
        ],
        'enable_earned_first_order'                    => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'subject_earned_first_order'                   => [
            'type'    => 'text',
            'default' => esc_html__( 'First order bonus: {points} {points_label} earned!', 'simple-points-and-rewards' ),
        ],
        'body_earned_first_order'                      => [
            'type'    => 'editor',
            'default' => '<p>Hi {user_name},</p><p>Awesome! You earned a first order bonus of <strong>{points} {points_label}</strong>.</p><p>Balance: <strong>{total_points}</strong></p><p><a href="{rewards_url}">Start redeeming</a></p><p>- {site_name}</p>',
        ],
        'enable_earned_nth_order'                      => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'subject_earned_nth_order'                     => [
            'type'    => 'text',
            'default' => esc_html__( 'Milestone bonus: {points} {points_label} earned!', 'simple-points-and-rewards' ),
        ],
        'body_earned_nth_order'                        => [
            'type'    => 'editor',
            'default' => '<p>Hi {user_name},</p><p>You hit a milestone and earned <strong>{points} {points_label}</strong> for: <em>{action}</em>.</p><p>Balance: <strong>{total_points}</strong></p><p><a href="{rewards_url}">See rewards</a></p><p>- {site_name}</p>',
        ],
        'enable_earned_referral'                       => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'subject_earned_referral'                      => [
            'type'    => 'text',
            'default' => esc_html__( 'You earned {points} {points_label} for a referral', 'simple-points-and-rewards' ),
        ],
        'body_earned_referral'                         => [
            'type'    => 'editor',
            'default' => '<p>Hi {user_name},</p><p>Your referral just paid off! You earned <strong>{points} {points_label}</strong>.</p><p>Balance: <strong>{total_points}</strong></p><p><a href="{rewards_url}">Keep earning and redeeming</a></p><p>- {site_name}</p>',
        ],
        'enable_earned_review'                         => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'subject_earned_review'                        => [
            'type'    => 'text',
            'default' => esc_html__( 'Thanks for the review! {points} {points_label} earned', 'simple-points-and-rewards' ),
        ],
        'body_earned_review'                           => [
            'type'    => 'editor',
            'default' => '<p>Hi {user_name},</p><p>Thanks for sharing your feedback. You earned <strong>{points} {points_label}</strong> for: <em>{action}</em>.</p><p>Balance: <strong>{total_points}</strong></p><p><a href="{rewards_url}">Explore rewards</a></p><p>- {site_name}</p>',
        ],
        'enable_earned_birthday'                       => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'subject_earned_birthday'                      => [
            'type'    => 'text',
            'default' => esc_html__( 'Happy Birthday! You earned {points} {points_label}', 'simple-points-and-rewards' ),
        ],
        'body_earned_birthday'                         => [
            'type'    => 'editor',
            'default' => '<p>Hi {user_name},</p><p>🎂 Happy Birthday! We just added <strong>{points} {points_label}</strong> to your account.</p><p>Balance: <strong>{total_points}</strong></p><p><a href="{rewards_url}">Treat yourself with rewards</a></p><p>- {site_name}</p>',
        ],
        'enable_earned_daily_login'                    => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'subject_earned_daily_login'                   => [
            'type'    => 'text',
            'default' => esc_html__( 'Daily Login Bonus: You earned {points} {points_label}', 'simple-points-and-rewards' ),
        ],
        'body_earned_daily_login'                      => [
            'type'    => 'editor',
            'default' => '<p>Hi {user_name},</p><p>Thanks for logging in today! You earned <strong>{points} {points_label}</strong>.</p><p>Balance: <strong>{total_points}</strong></p><p><a href="{rewards_url}">See rewards</a></p><p>- {site_name}</p>',
        ],
        'levels_enabled'                               => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'levels_starter_name'                          => [
            'type'    => 'text',
            'default' => esc_html__( 'Starter', 'simple-points-and-rewards' ),
        ],
        'levels_starter_badge_icon'                    => [
            'type'    => 'text',
            'default' => '⭐',
        ],
        'levels_starter_badge_type'                    => [
            'type'    => 'text',
            'default' => 'preset',
        ],
        'levels_starter_badge_text'                    => [
            'type'    => 'text',
            'default' => '',
        ],
        'levels_starter_badge_url'                     => [
            'type'    => 'text',
            'default' => '',
        ],
        'levels'                                       => [
            'type'    => 'array',
            'default' => [],
        ],
        'levels_points_type'                           => [
            'type'    => 'text',
            'default' => 'total_earned',
        ],
        'rewards'                                      => [
            'type'    => 'array',
            'default' => [],
        ],
        'terms_enabled'                                => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'terms_link_text'                              => [
            'type'    => 'text',
            'default' => '',
        ],
        'terms_show_dashboard'                         => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'terms_show_widget'                            => [
            'type'    => 'checkbox',
            'default' => true,
        ],
        'terms_content'                                => [
            'type'    => 'editor',
            'default' => '',
        ],
    ];
    // Always define PRO earn types in schema so options exist in free, but they remain disabled
    $fields['earn']['first_order'] = [
        'enabled'      => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'points'       => [
            'type'    => 'number',
            'default' => 200,
        ],
        'name'         => [
            'type'    => 'text',
            'default' => esc_html__( 'First Order Bonus', 'simple-points-and-rewards' ),
        ],
        'award_timing' => [
            'type'    => 'text',
            'default' => 'thankyou',
        ],
    ];
    $fields['earn']['nth_order'] = [
        'enabled'     => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'points'      => [
            'type'    => 'number',
            'default' => 500,
        ],
        'recurring'   => [
            'type'    => 'checkbox',
            'default' => false,
        ],
        'order_count' => [
            'type'    => 'number',
            'default' => 5,
        ],
        'name'        => [
            'type'    => 'text',
            'default' => esc_html__( 'Bonus after X Orders', 'simple-points-and-rewards' ),
        ],
    ];
    return $fields;
}

function spar_settings_default() {
    $fields = spar_get_option_fields();
    $defaults = [];
    foreach ( $fields as $key => $field_data ) {
        if ( $key === 'earn' ) {
            // Handle nested earn fields.
            foreach ( $field_data as $earn_key => $earn_data ) {
                foreach ( $earn_data as $subkey => $subfield ) {
                    $defaults['earn'][$earn_key][$subkey] = $subfield['default'];
                }
            }
        } else {
            // Handle direct fields.
            $defaults[$key] = $field_data['default'];
        }
    }
    return $defaults;
}

/**
 * Get all settings with defaults applied
 *
 * @param string $option The option key to retrieve
 * 
 * @return mixed The option value or default
 */
function spar_get_options(  $option  ) {
    $defaults = spar_settings_default();
    $options = get_option( 'spar_options', $defaults );
    // Merge with defaults to ensure all keys exist (prevents undefined index notices).
    $options = array_merge( $defaults, $options );
    // Safely return option or default when present
    return $options[$option] ?? $defaults[$option] ?? null;
}

/**
 * Get a grouped option with defaults applied
 *
 * @param string $group The option group (e.g. 'earn')
 * @param string $option The option key within the group (e.g. 'signup')
 * 
 * @return mixed The option value or default
 */
function spar_get_option(  $group, $option  ) {
    $defaults = spar_settings_default();
    $options = get_option( 'spar_options', $defaults );
    // Merge with defaults to ensure all keys exist.
    $options = array_merge( $defaults, $options );
    if ( $group === 'earn' ) {
        // Ensure earn options are properly merged.
        $earn_current = ( isset( $options['earn'][$option] ) && is_array( $options['earn'][$option] ) ? $options['earn'][$option] : [] );
        $earn_default = ( isset( $defaults['earn'][$option] ) && is_array( $defaults['earn'][$option] ) ? $defaults['earn'][$option] : null );
        if ( is_array( $earn_default ) ) {
            $earn_current = array_merge( $earn_default, $earn_current );
        }
        // Return merged earn settings if available, else fall back to default or null.
        return ( !empty( $earn_current ) ? $earn_current : $earn_default ?? null );
    }
    // For backward compatibility, check if the option exists at the top level.
    return $options[$option] ?? $defaults[$option] ?? null;
}

/**
 * Get a single option value by key
 *
 * @param string $option_key The option key to retrieve
 * @return mixed The option value or default
 */
function spar_get_single_option(  $option_key  ) {
    $defaults = spar_settings_default();
    $options = get_option( 'spar_options', $defaults );
    // Merge with defaults to ensure all keys exist
    $options = wp_parse_args( $options, $defaults );
    return ( isset( $options[$option_key] ) ? $options[$option_key] : (( isset( $defaults[$option_key] ) ? $defaults[$option_key] : null )) );
}

/**
 * Check whether reward vouchers are enabled globally.
 */
function spar_rewards_vouchers_enabled() {
    $value = spar_get_single_option( 'rewards_vouchers_enabled' );
    $enabled = ( null === $value ? true : (bool) $value );
    return (bool) apply_filters( 'spar_rewards_vouchers_enabled', $enabled );
}

/**
 * Update a single option value
 *
 * @param string $option_key The option key to update
 * @param mixed $value The new value
 * @return bool True on success, false on failure
 */
function spar_update_single_option(  $option_key, $value  ) {
    $options = get_option( 'spar_options', [] );
    $fields = spar_get_option_fields();
    // Get field type for sanitization.
    $field_type = ( isset( $fields[$option_key]['type'] ) ? $fields[$option_key]['type'] : 'text' );
    $default = ( isset( $fields[$option_key]['default'] ) ? $fields[$option_key]['default'] : '' );
    // Sanitize the value according to its declared type.
    if ( 'rewards_widget_icon' === $option_key ) {
        $options[$option_key] = sanitize_text_field( wp_unslash( $value ) );
    } elseif ( 'rewards_widget_icon_custom_url' === $option_key ) {
        $options[$option_key] = esc_url_raw( wp_unslash( $value ) );
    } elseif ( 'rewards_widget_icon_custom_text' === $option_key ) {
        $options[$option_key] = sanitize_text_field( wp_unslash( $value ) );
    } else {
        $options[$option_key] = spar_sanitize_field_value( $value, $field_type, $default );
    }
    return update_option( 'spar_options', $options );
}

// Removed per request: AJAX handler for updating single options (now relying on form submit only)
/**
 * Enqueue admin scripts for settings page
 */
function spar_enqueue_admin_settings_scripts(  $hook  ) {
    // Only load on our settings page: slug is 'spar-settings'.
    // Be tolerant across WP hook formats and direct page loads.
    $is_settings_page = false !== strpos( (string) $hook, 'spar-settings' );
    if ( !$is_settings_page ) {
        $page = ( isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '' );
        $is_settings_page = 'spar-settings' === $page;
    }
    if ( !$is_settings_page ) {
        return;
    }
    // Check if the settings.js file exists, if not create inline script
    // Correct path to settings script
    $settings_js_path = plugin_dir_path( __FILE__ ) . '../../../assets/js/admin-settings.js';
    $handle = 'spar-admin-settings-fallback';
    if ( file_exists( $settings_js_path ) ) {
        $handle = 'spar-admin-settings';
        wp_enqueue_script(
            $handle,
            plugin_dir_url( __FILE__ ) . '../../../assets/js/admin-settings.js',
            array('jquery'),
            '1.0.0',
            true
        );
    }
    // Always add localized script data for AJAX and UI strings on our script handle.
    wp_localize_script( $handle, 'sparAjax', array(
        'ajaxurl' => admin_url( 'admin-ajax.php' ),
        'nonce'   => wp_create_nonce( 'spar_settings_nonce' ),
        'strings' => array(
            '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' ),
        ),
    ) );
    // Media library for image uploads in settings.
    wp_enqueue_media();
    // If the external settings script is missing, load a small fallback file instead of inline JS.
    if ( !file_exists( $settings_js_path ) ) {
        wp_enqueue_script(
            'spar-admin-settings-fallback',
            plugin_dir_url( __FILE__ ) . '../../../assets/js/settings-fallback.js',
            array('jquery'),
            '1.0.0',
            true
        );
    }
}

add_action( 'admin_enqueue_scripts', 'spar_enqueue_admin_settings_scripts' );
if ( !function_exists( 'spar_get_product_points_location_options' ) ) {
    /**
     * Retrieve the available display locations for single product points output.
     *
     * @return array<string, array{label:string, hook:string, priority:int}>
     */
    function spar_get_product_points_location_options() {
        $locations = [
            'before_summary'             => [
                'label'    => esc_html__( 'Before product summary', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_before_single_product_summary',
                'priority' => 15,
            ],
            'summary_before_price'       => [
                'label'    => esc_html__( 'Product summary - before price', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_single_product_summary',
                'priority' => 9,
            ],
            'summary_after_price'        => [
                'label'    => esc_html__( 'Product summary - after price (default)', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_single_product_summary',
                'priority' => 25,
            ],
            'summary_before_add_to_cart' => [
                'label'    => esc_html__( 'Product summary - before add to cart button', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_single_product_summary',
                'priority' => 29,
            ],
            'summary_after_add_to_cart'  => [
                'label'    => esc_html__( 'Product summary - after add to cart button', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_single_product_summary',
                'priority' => 35,
            ],
            'after_summary'              => [
                'label'    => esc_html__( 'After product summary', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_after_single_product_summary',
                'priority' => 5,
            ],
            'after_product'              => [
                'label'    => esc_html__( 'After entire product', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_after_single_product',
                'priority' => 5,
            ],
        ];
        /**
         * Allow developers to filter the available product points locations.
         *
         * @param array $locations List of locations keyed by identifier.
         */
        return apply_filters( 'spar_product_points_location_options', $locations );
    }

}
function spar_sanitize_field_value(  $value, $type, $default  ) {
    // Basic coercion and sanitization by field type.
    // Anything not matched falls back to a safe text value.
    switch ( $type ) {
        case 'checkbox':
            return !empty( $value );
        case 'number':
            return absint( $value );
        case 'float':
            return floatval( $value );
        case 'text':
            // Remove slashes that WordPress might have added
            $value = wp_unslash( $value );
            $value = sanitize_text_field( $value );
            return ( '' !== $value ? $value : $default );
        case 'textarea':
            // Remove slashes that WordPress might have added
            $value = wp_unslash( $value );
            $value = sanitize_textarea_field( $value );
            return ( '' !== $value ? $value : $default );
        case 'editor':
            // Remove slashes that WordPress might have added
            $value = wp_unslash( $value );
            $value = wp_kses_post( $value );
            return ( '' !== $value ? $value : $default );
        case 'select':
            $value = sanitize_text_field( $value );
            return ( '' !== $value ? $value : $default );
        case 'color':
            // Validate hex color
            $color = sanitize_text_field( $value );
            if ( preg_match( '/^#[a-f0-9]{6}$/i', $color ) ) {
                return $color;
            }
            return $default;
        case 'array':
            return ( is_array( $value ) ? $value : $default );
        default:
            $value = sanitize_text_field( $value );
            return ( '' !== $value ? $value : $default );
    }
}

function spar_process_settings_post(  $options  ) {
    // Verify nonce to ensure only legitimate settings submissions are processed.
    $nonce = ( isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '' );
    if ( !wp_verify_nonce( $nonce, 'spar_save_settings_action' ) ) {
        // Nonce invalid or missing; return options unchanged.
        return $options;
    }
    $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 ) {
                foreach ( $earn_data as $subkey => $subfield ) {
                    $post_key = $earn_key . '_' . $subkey;
                    // Special handling for order_fixed tiers (nested arrays)
                    if ( $earn_key === 'order_fixed' && $subkey === 'tiers' ) {
                        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw array sanitized below per item
                        $raw_tiers = ( isset( $_POST[$post_key] ) ? wp_unslash( $_POST[$post_key] ) : [] );
                        $sanitized_tiers = [];
                        if ( is_array( $raw_tiers ) ) {
                            foreach ( $raw_tiers as $tier ) {
                                if ( !is_array( $tier ) ) {
                                    continue;
                                }
                                $points = ( isset( $tier['points'] ) ? absint( $tier['points'] ) : 0 );
                                $default_threshold = ( isset( $tier['default_threshold'] ) ? floatval( $tier['default_threshold'] ) : 0.0 );
                                $currencies = [];
                                // Premium multi-currency thresholds
                                if ( isset( $tier['currencies'] ) && is_array( $tier['currencies'] ) ) {
                                    foreach ( $tier['currencies'] as $row ) {
                                        if ( !is_array( $row ) ) {
                                            continue;
                                        }
                                        $code = ( isset( $row['currency'] ) ? strtoupper( sanitize_text_field( $row['currency'] ) ) : '' );
                                        $thr = ( isset( $row['threshold'] ) ? floatval( $row['threshold'] ) : 0.0 );
                                        if ( $code !== '' && $thr > 0 ) {
                                            $currencies[] = [
                                                'currency'  => $code,
                                                'threshold' => $thr,
                                            ];
                                        }
                                    }
                                }
                                // Only keep tiers that have points and a valid default threshold
                                if ( $points > 0 && $default_threshold > 0 ) {
                                    $sanitized_tiers[] = [
                                        'points'            => $points,
                                        'default_threshold' => $default_threshold,
                                        'currencies'        => $currencies,
                                    ];
                                }
                            }
                        }
                        $options['earn'][$earn_key][$subkey] = $sanitized_tiers;
                        // empty array if none
                        continue;
                        // handled, skip generic processing
                    }
                    // Special handling for spin_wheel prizes (nested arrays)
                    if ( $earn_key === 'spin_wheel' && $subkey === 'prizes' ) {
                        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw array sanitized below per item
                        $raw_prizes = ( isset( $_POST[$post_key] ) ? wp_unslash( $_POST[$post_key] ) : [] );
                        $sanitized_prizes = [];
                        $allowed_prize_types = ['points', 'voucher', 'product'];
                        if ( is_array( $raw_prizes ) ) {
                            $prize_count = 0;
                            foreach ( $raw_prizes as $prize ) {
                                if ( !is_array( $prize ) || $prize_count >= 10 ) {
                                    continue;
                                }
                                $type = ( isset( $prize['type'] ) && in_array( $prize['type'], $allowed_prize_types, true ) ? $prize['type'] : 'points' );
                                $value = ( isset( $prize['value'] ) ? floatval( $prize['value'] ) : 0 );
                                $probability = ( isset( $prize['probability'] ) ? floatval( $prize['probability'] ) : 0 );
                                $label = ( isset( $prize['label'] ) ? sanitize_text_field( $prize['label'] ) : '' );
                                $product_id = ( isset( $prize['product_id'] ) ? absint( $prize['product_id'] ) : 0 );
                                if ( $probability > 0 ) {
                                    $sanitized_prizes[] = [
                                        'type'        => $type,
                                        'value'       => $value,
                                        'probability' => min( $probability, 100 ),
                                        'label'       => $label,
                                        'product_id'  => $product_id,
                                    ];
                                    $prize_count++;
                                }
                            }
                        }
                        $options['earn'][$earn_key][$subkey] = $sanitized_prizes;
                        continue;
                    }
                    // Special handling for currency_rates: sanitize nested rows and drop empty entries
                    if ( $earn_key === 'order' && $subkey === 'currency_rates' ) {
                        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw array sanitized below per item
                        $raw_rates = ( isset( $_POST[$post_key] ) ? wp_unslash( $_POST[$post_key] ) : [] );
                        $sanitized = [];
                        if ( is_array( $raw_rates ) ) {
                            foreach ( $raw_rates as $row ) {
                                if ( !is_array( $row ) ) {
                                    continue;
                                }
                                $currency = ( isset( $row['currency'] ) ? sanitize_text_field( $row['currency'] ) : '' );
                                $points = ( isset( $row['points'] ) ? floatval( $row['points'] ) : 0.0 );
                                $amount = ( isset( $row['amount'] ) ? floatval( $row['amount'] ) : 0.0 );
                                // Only keep valid rows: have a currency and positive amount
                                if ( !empty( $currency ) && $amount > 0 ) {
                                    $sanitized[] = [
                                        'currency' => strtoupper( $currency ),
                                        'points'   => $points,
                                        'amount'   => $amount,
                                    ];
                                }
                            }
                        }
                        $options['earn'][$earn_key][$subkey] = $sanitized;
                        // empty array if nothing valid
                    } else {
                        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw input is sanitized immediately via spar_sanitize_field_value based on field definition.
                        $value = ( isset( $_POST[$post_key] ) ? wp_unslash( $_POST[$post_key] ) : '' );
                        $options['earn'][$earn_key][$subkey] = spar_sanitize_field_value( $value, $subfield['type'], $subfield['default'] );
                    }
                }
            }
        } elseif ( 'levels_starter_badge_icon' === $key ) {
            $raw_icon = ( isset( $_POST['levels_starter_badge_icon'] ) ? sanitize_text_field( wp_unslash( $_POST['levels_starter_badge_icon'] ) ) : '' );
            $raw_url = ( isset( $_POST['levels_starter_badge_url'] ) ? esc_url_raw( wp_unslash( $_POST['levels_starter_badge_url'] ) ) : '' );
            $raw_text = ( isset( $_POST['levels_starter_badge_text'] ) ? sanitize_text_field( wp_unslash( $_POST['levels_starter_badge_text'] ) ) : '' );
            $raw_type = ( isset( $_POST['levels_starter_badge_type'] ) ? sanitize_text_field( wp_unslash( $_POST['levels_starter_badge_type'] ) ) : '' );
            $allowed_types = ['preset', 'custom_url', 'custom_text'];
            $badge_type = ( in_array( $raw_type, $allowed_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 ) ) {
            // Processed alongside levels_starter_badge_icon above.
            continue;
        } elseif ( 'rewards_widget_icon' === $key ) {
            $icon_value = ( isset( $_POST['rewards_widget_icon'] ) ? sanitize_text_field( wp_unslash( $_POST['rewards_widget_icon'] ) ) : '' );
            $icon_url = ( isset( $_POST['rewards_widget_icon_custom_url'] ) ? esc_url_raw( wp_unslash( $_POST['rewards_widget_icon_custom_url'] ) ) : '' );
            $icon_text = ( isset( $_POST['rewards_widget_icon_custom_text'] ) ? sanitize_text_field( wp_unslash( $_POST['rewards_widget_icon_custom_text'] ) ) : '' );
            if ( 'custom_url' === $icon_value ) {
                $icon_text = '';
            } elseif ( 'custom_text' === $icon_value ) {
                $icon_url = '';
            } else {
                $icon_url = '';
                $icon_text = '';
            }
            $options['rewards_widget_icon'] = $icon_value;
            $options['rewards_widget_icon_custom_url'] = $icon_url;
            $options['rewards_widget_icon_custom_text'] = $icon_text;
            continue;
        } elseif ( in_array( $key, ['rewards_widget_icon_custom_url', 'rewards_widget_icon_custom_text'], true ) ) {
            // Processed alongside rewards_widget_icon above.
            continue;
        } elseif ( $key === 'earn_ways_order' ) {
            $allowed_ways = [
                'signup',
                'first_order',
                'nth_order',
                'order_fixed',
                'order',
                'review',
                'birthday',
                'daily_login',
                'referral',
                'spin_wheel'
            ];
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized per item below
            $raw_order = ( isset( $_POST['earn_ways_order'] ) ? wp_unslash( $_POST['earn_ways_order'] ) : [] );
            $sanitized_order = [];
            if ( is_array( $raw_order ) ) {
                foreach ( $raw_order as $item ) {
                    $item = sanitize_key( $item );
                    if ( in_array( $item, $allowed_ways, true ) && !in_array( $item, $sanitized_order, true ) ) {
                        $sanitized_order[] = $item;
                    }
                }
            }
            // Append any allowed ways not present in the submitted order
            foreach ( $allowed_ways as $way ) {
                if ( !in_array( $way, $sanitized_order, true ) ) {
                    $sanitized_order[] = $way;
                }
            }
            $options['earn_ways_order'] = $sanitized_order;
            continue;
        } elseif ( $key === 'earn_conditional_rules' ) {
            // Handle earn conditional rules array.
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw array is sanitized per-field in the loop below.
            $raw_rules = ( isset( $_POST['earn_conditional_rules'] ) ? wp_unslash( $_POST['earn_conditional_rules'] ) : [] );
            $sanitized_rules = [];
            $allowed_types = [
                'product',
                'product_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;
                    }
                    $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'
                    ] ) );
                    $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 );
                    }
                    $san = [
                        'id'               => ( isset( $rule['id'] ) ? sanitize_text_field( (string) $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,
                    ];
                    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':
                                $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_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_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',
                            '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];
                            }
                        }
                        $sanitized_rules[] = $san;
                    }
                }
            }
            $options['earn_conditional_rules'] = array_values( $sanitized_rules );
            continue;
        } elseif ( $key === 'levels' ) {
            // Handle levels array specially with detailed processing.
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw array is sanitized per-field in the loop below.
            $levels = ( isset( $_POST['levels'] ) ? wp_unslash( $_POST['levels'] ) : [] );
            $sanitized_levels = [];
            if ( !empty( $levels ) && is_array( $levels ) ) {
                foreach ( $levels as $level_data ) {
                    // Ensure we have an array of 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 = '';
                    }
                    $email_subject_raw = ( isset( $level_data['level_up_email_subject'] ) ? wp_unslash( $level_data['level_up_email_subject'] ) : '' );
                    $email_body_raw = ( isset( $level_data['level_up_email_body'] ) ? wp_unslash( $level_data['level_up_email_body'] ) : '' );
                    $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 );
                    $enabled_normalized = false;
                    if ( true === $enabled_raw || 1 === $enabled_raw ) {
                        $enabled_normalized = true;
                    } elseif ( is_string( $enabled_raw ) ) {
                        $enabled_normalized = in_array( strtolower( $enabled_raw ), [
                            '1',
                            'true',
                            'yes',
                            'on'
                        ], true );
                    }
                    // 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'     => $enabled_normalized,
                        'level_up_email_subject'     => sanitize_text_field( $email_subject_raw ),
                        'level_up_email_body'        => wp_kses_post( $email_body_raw ),
                    ];
                    // Only add levels with valid data
                    if ( !empty( $sanitized_level['name'] ) && $sanitized_level['required_points'] > 0 ) {
                        $sanitized_levels[] = $sanitized_level;
                    }
                }
            }
            $options['levels'] = $sanitized_levels;
        } elseif ( $key === 'rewards' ) {
            // Handle rewards array specially.
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw array is sanitized per-field in the loop below.
            $rewards = ( isset( $_POST['rewards'] ) ? wp_unslash( $_POST['rewards'] ) : [] );
            $sanitized_rewards = [];
            if ( !empty( $rewards ) && is_array( $rewards ) ) {
                foreach ( $rewards as $reward_data ) {
                    // Ensure we have an array of 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 ),
                    ];
                    // Custom reward fields
                    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 ) ) {
                            $sanitized_reward['developer_id'] = $developer_id;
                        }
                    }
                    // Only add rewards with valid data
                    if ( !empty( $sanitized_reward['name'] ) && $sanitized_reward['points'] > 0 ) {
                        $sanitized_rewards[] = $sanitized_reward;
                    }
                }
            }
            $options['rewards'] = $sanitized_rewards;
        } elseif ( 'rewards_widget_icon' === $key ) {
            // Allow empty selection for "No Icon".
            $value = ( isset( $_POST[$key] ) ? wp_unslash( $_POST[$key] ) : '' );
            $options[$key] = sanitize_text_field( $value );
        } else {
            // Handle direct fields.
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Raw input is sanitized via spar_sanitize_field_value using field type definition.
            $value = ( isset( $_POST[$key] ) ? wp_unslash( $_POST[$key] ) : '' );
            $options[$key] = spar_sanitize_field_value( $value, $field_data['type'], $field_data['default'] );
        }
    }
    // Independent handling: each section has its own refund/cancel deduction toggle.
    $options['earn']['order']['deduct_on_refund'] = !empty( $_POST['order_deduct_on_refund'] );
    $options['earn']['order_fixed']['deduct_on_refund'] = !empty( $_POST['order_fixed_deduct_on_refund'] );
    if ( isset( $options['earn']['review']['enabled'] ) ) {
        $options['earn']['review']['enabled'] = false;
    }
    if ( isset( $options['earn']['birthday']['enabled'] ) ) {
        $options['earn']['birthday']['enabled'] = false;
    }
    return $options;
}

/**
 * Generate a unique reward ID
 */
function spar_generate_reward_id() {
    return 'reward_' . wp_generate_password( 8, false, false );
}

/**
 * Get all active rewards from settings
 */
function spar_get_rewards() {
    if ( !spar_rewards_vouchers_enabled() ) {
        return [];
    }
    $options = get_option( 'spar_options', [] );
    $rewards = $options['rewards'] ?? [];
    // Filter only active rewards and check expiry.
    $filtered = array_filter( $rewards, function ( $reward ) {
        if ( ($reward['status'] ?? 'active') !== 'active' ) {
            return false;
        }
        // Filter out incomplete rewards (drafts)
        if ( empty( $reward['name'] ) || (int) ($reward['points'] ?? 0) <= 0 ) {
            return false;
        }
        return true;
    } );
    return $filtered;
}

/**
 * Get a specific reward by ID
 */
function spar_get_reward_by_id(  $reward_id  ) {
    $options = get_option( 'spar_options', [] );
    $rewards = $options['rewards'] ?? [];
    foreach ( $rewards as $reward ) {
        if ( $reward['id'] === $reward_id ) {
            return $reward;
        }
    }
    return null;
}

/**
 * Check if user can redeem a reward
 */
function spar_can_user_redeem_reward(  $user_id, $reward  ) {
    if ( !$reward || empty( $reward['name'] ) || (int) ($reward['points'] ?? 0) <= 0 ) {
        return false;
    }
    $user_points = spar_get_user_points( $user_id );
    return $user_points >= $reward['points'];
}

if ( !function_exists( 'spar_get_product_loop_points_location_options' ) ) {
    /**
     * Retrieve available display locations for catalog/loop product points output.
     *
     * @return array<string, array{label:string, hook:string, priority:int}>
     */
    function spar_get_product_loop_points_location_options() {
        $locations = [
            'loop_before_item'  => [
                'label'    => esc_html__( 'Catalog item - before product', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_before_shop_loop_item',
                'priority' => 12,
            ],
            'loop_before_title' => [
                'label'    => esc_html__( 'Catalog item - before title', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_before_shop_loop_item_title',
                'priority' => 8,
            ],
            'loop_after_title'  => [
                'label'    => esc_html__( 'Catalog item - after title (default)', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_after_shop_loop_item_title',
                'priority' => 12,
            ],
            'loop_after_item'   => [
                'label'    => esc_html__( 'Catalog item - after product', 'simple-points-and-rewards' ),
                'hook'     => 'woocommerce_after_shop_loop_item',
                'priority' => 15,
            ],
        ];
        /**
         * Allow developers to filter the available product loop points locations.
         *
         * @param array $locations List of locations keyed by identifier.
         */
        return apply_filters( 'spar_product_loop_points_location_options', $locations );
    }

}