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

trait EEP_Monetization_Trait {
    private function get_boost_orders_table_name() {
        global $wpdb;
        return (string) $wpdb->prefix . self::BOOST_ORDERS_TABLE;
    }

    private function get_ticket_orders_table_name() {
        global $wpdb;
        return (string) $wpdb->prefix . self::TICKET_ORDERS_TABLE;
    }

    private function ticket_orders_table_exists($refresh = false) {
        static $exists = null;
        if (!$refresh && $exists !== null) {
            return (bool) $exists;
        }

        global $wpdb;
        $table = $this->get_ticket_orders_table_name();
        $found = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        $exists = ($found === $table);
        return (bool) $exists;
    }


    public function create_boost_orders_table() {
        global $wpdb;
        $table = $this->get_boost_orders_table_name();
        $charset = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE {$table} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            parent_order_id bigint(20) unsigned NOT NULL DEFAULT 0,
            order_key char(36) NOT NULL,
            post_id bigint(20) unsigned NOT NULL,
            user_id bigint(20) unsigned NOT NULL,
            gateway varchar(20) NOT NULL DEFAULT '',
            order_status varchar(24) NOT NULL DEFAULT 'pending',
            currency varchar(8) NOT NULL DEFAULT 'USD',
            subtotal decimal(18,2) NOT NULL DEFAULT 0.00,
            vat_amount decimal(18,2) NOT NULL DEFAULT 0.00,
            total_amount decimal(18,2) NOT NULL DEFAULT 0.00,
            duration_days smallint(5) unsigned NOT NULL DEFAULT 30,
            auto_renew tinyint(1) unsigned NOT NULL DEFAULT 0,
            is_renewal tinyint(1) unsigned NOT NULL DEFAULT 0,
            stacking_applied tinyint(1) unsigned NOT NULL DEFAULT 0,
            external_order_id varchar(191) NOT NULL DEFAULT '',
            external_payment_id varchar(191) NOT NULL DEFAULT '',
            customer_ref varchar(191) NOT NULL DEFAULT '',
            payment_method_ref varchar(191) NOT NULL DEFAULT '',
            payment_meta longtext NULL,
            renewal_attempts smallint(5) unsigned NOT NULL DEFAULT 0,
            max_renewal_attempts smallint(5) unsigned NOT NULL DEFAULT 3,
            next_renewal_at datetime NULL,
            reminder_sent_at datetime NULL,
            paid_at datetime NULL,
            expires_at datetime NULL,
            created_at datetime NOT NULL,
            updated_at datetime NOT NULL,
            PRIMARY KEY  (id),
            UNIQUE KEY order_key (order_key),
            KEY post_id (post_id),
            KEY user_id (user_id),
            KEY status_next (order_status, next_renewal_at),
            KEY expires_at (expires_at),
            KEY external_order_id (external_order_id(100)),
            KEY created_at (created_at)
        ) {$charset};";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
    }

    public function create_ticket_orders_table() {
        global $wpdb;
        $table = $this->get_ticket_orders_table_name();
        $charset = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE {$table} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            ticket_uid varchar(64) NOT NULL,
            post_id bigint(20) unsigned NOT NULL,
            user_id bigint(20) unsigned NOT NULL,
            rsvp_status varchar(20) NOT NULL DEFAULT 'yes',
            gateway varchar(20) NOT NULL DEFAULT '',
            gateway_label varchar(80) NOT NULL DEFAULT '',
            payment_method varchar(80) NOT NULL DEFAULT '',
            currency varchar(8) NOT NULL DEFAULT 'USD',
            ticket_price decimal(18,2) NOT NULL DEFAULT 0.00,
            ticket_total decimal(18,2) NOT NULL DEFAULT 0.00,
            payment_collected tinyint(1) unsigned NOT NULL DEFAULT 0,
            payment_status varchar(24) NOT NULL DEFAULT 'confirmed',
            external_order_id varchar(191) NOT NULL DEFAULT '',
            external_payment_id varchar(191) NOT NULL DEFAULT '',
            customer_ref varchar(191) NOT NULL DEFAULT '',
            payment_method_ref varchar(191) NOT NULL DEFAULT '',
            qr_payload longtext NULL,
            qr_image_url text NULL,
            purchase_ip varchar(64) NOT NULL DEFAULT '',
            purchase_user_agent varchar(255) NOT NULL DEFAULT '',
            purchased_at datetime NOT NULL,
            email_sent tinyint(1) unsigned NOT NULL DEFAULT 0,
            email_sent_at datetime NULL,
            payment_meta longtext NULL,
            created_at datetime NOT NULL,
            updated_at datetime NOT NULL,
            PRIMARY KEY  (id),
            UNIQUE KEY ticket_uid (ticket_uid),
            KEY post_id (post_id),
            KEY user_id (user_id),
            KEY post_user (post_id, user_id),
            KEY payment_status (payment_status),
            KEY purchased_at (purchased_at),
            KEY external_order_id (external_order_id(100)),
            KEY external_payment_id (external_payment_id(100))
        ) {$charset};";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
        $this->ticket_orders_table_exists(true);
    }


    public function register_monetization_settings() {
        register_setting('eep_settings_group', 'eep_boost_enabled', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_boost_duration_pricing', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_duration_pricing_option'],
            'default' => wp_json_encode([
                ['days' => 7, 'price' => 9.99],
                ['days' => 30, 'price' => 29.99],
                ['days' => 90, 'price' => 79.99],
            ]),
        ]);
        register_setting('eep_settings_group', 'eep_boost_type_pricing', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_type_pricing_option'],
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_boost_allow_stacking', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_boost_ranking_mode', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_ranking_mode_option'],
            'default' => 'simple',
        ]);
        register_setting('eep_settings_group', 'eep_boost_gateway_mode', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_gateway_mode_option'],
            'default' => 'test',
        ]);
        register_setting('eep_settings_group', 'eep_boost_test_free_mode', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_boost_stripe_enabled', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_boost_paypal_enabled', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_boost_stripe_test_secret_key', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_secret_with_fallback_stripe_test'],
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_boost_stripe_live_secret_key', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_secret_with_fallback_stripe_live'],
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_boost_paypal_test_client_id', [
            'type' => 'string',
            'sanitize_callback' => 'eep_sanitize_text',
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_boost_paypal_test_secret', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_secret_with_fallback_paypal_test'],
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_boost_paypal_live_client_id', [
            'type' => 'string',
            'sanitize_callback' => 'eep_sanitize_text',
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_boost_paypal_live_secret', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_secret_with_fallback_paypal_live'],
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_boost_currency', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_currency_option'],
            'default' => 'USD',
        ]);
        register_setting('eep_settings_group', 'eep_boost_vat_enabled', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_boost_vat_percent', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_decimal_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_boost_auto_renew_enabled', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '1',
        ]);
        register_setting('eep_settings_group', 'eep_boost_reminder_days', [
            'type' => 'integer',
            'sanitize_callback' => [$this, 'sanitize_boost_reminder_days_option'],
            'default' => 3,
        ]);
        register_setting('eep_settings_group', 'eep_boost_retry_limit', [
            'type' => 'integer',
            'sanitize_callback' => [$this, 'sanitize_boost_retry_limit_option'],
            'default' => 3,
        ]);
        register_setting('eep_settings_group', 'eep_boost_show_featured_badge', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '1',
        ]);
        register_setting('eep_settings_group', 'eep_boost_featured_badge_text', [
            'type' => 'string',
            'sanitize_callback' => 'eep_sanitize_text',
            'default' => __('Featured', 'nexdirectory'),
        ]);
        register_setting('eep_settings_group', 'eep_boost_featured_badge_image_url', [
            'type' => 'string',
            'sanitize_callback' => 'esc_url_raw',
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_boost_highlight_style', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '1',
        ]);
        register_setting('eep_settings_group', 'eep_boost_verified_badge_enabled', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_ticketing_enabled', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '1',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_gateway_mode', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_gateway_mode_option'],
            'default' => 'test',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_test_free_mode', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_stripe_enabled', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_paypal_enabled', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '0',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_stripe_test_secret_key', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_ticket_secret_with_fallback_stripe_test'],
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_stripe_live_secret_key', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_ticket_secret_with_fallback_stripe_live'],
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_paypal_test_client_id', [
            'type' => 'string',
            'sanitize_callback' => 'eep_sanitize_text',
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_paypal_test_secret', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_ticket_secret_with_fallback_paypal_test'],
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_paypal_live_client_id', [
            'type' => 'string',
            'sanitize_callback' => 'eep_sanitize_text',
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_paypal_live_secret', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_ticket_secret_with_fallback_paypal_live'],
            'default' => '',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_currency', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_boost_currency_option'],
            'default' => 'USD',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_send_confirmation_email', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '1',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_qr_enabled', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_checkbox_option'],
            'default' => '1',
        ]);
        register_setting('eep_settings_group', 'eep_ticket_promo_codes', [
            'type' => 'string',
            'sanitize_callback' => [$this, 'sanitize_ticket_promo_codes_option'],
            'default' => '',
        ]);
    }


    public function sanitize_boost_secret_with_fallback_stripe_test($value) {
        return $this->sanitize_secret_option_with_fallback($value, 'eep_boost_stripe_test_secret_key');
    }


    public function sanitize_boost_secret_with_fallback_stripe_live($value) {
        return $this->sanitize_secret_option_with_fallback($value, 'eep_boost_stripe_live_secret_key');
    }


    public function sanitize_boost_secret_with_fallback_paypal_test($value) {
        return $this->sanitize_secret_option_with_fallback($value, 'eep_boost_paypal_test_secret');
    }


    public function sanitize_boost_secret_with_fallback_paypal_live($value) {
        return $this->sanitize_secret_option_with_fallback($value, 'eep_boost_paypal_live_secret');
    }


    public function sanitize_ticket_secret_with_fallback_stripe_test($value) {
        return $this->sanitize_secret_option_with_fallback($value, 'eep_ticket_stripe_test_secret_key');
    }


    public function sanitize_ticket_secret_with_fallback_stripe_live($value) {
        return $this->sanitize_secret_option_with_fallback($value, 'eep_ticket_stripe_live_secret_key');
    }


    public function sanitize_ticket_secret_with_fallback_paypal_test($value) {
        return $this->sanitize_secret_option_with_fallback($value, 'eep_ticket_paypal_test_secret');
    }


    public function sanitize_ticket_secret_with_fallback_paypal_live($value) {
        return $this->sanitize_secret_option_with_fallback($value, 'eep_ticket_paypal_live_secret');
    }


    public function sanitize_boost_currency_option($value) {
        $value = strtoupper(sanitize_text_field((string) $value));
        if (!preg_match('/^[A-Z]{3}$/', $value)) {
            $value = 'USD';
        }
        return $value;
    }


    public function sanitize_boost_gateway_mode_option($value) {
        $value = sanitize_key((string) $value);
        return in_array($value, ['test', 'live'], true) ? $value : 'test';
    }


    public function sanitize_boost_ranking_mode_option($value) {
        $value = sanitize_key((string) $value);
        return in_array($value, ['simple', 'time_weighted'], true) ? $value : 'simple';
    }


    public function sanitize_boost_decimal_option($value) {
        if (!is_scalar($value)) {
            return '0';
        }
        $value = str_replace(',', '.', trim((string) $value));
        if (!is_numeric($value)) {
            return '0';
        }
        $numeric = max(0, min(1000, (float) $value));
        return (string) round($numeric, 2);
    }


    public function sanitize_boost_reminder_days_option($value) {
        $value = absint((int) $value);
        if ($value < 1) {
            $value = 1;
        }
        if ($value > 30) {
            $value = 30;
        }
        return $value;
    }


    public function sanitize_boost_retry_limit_option($value) {
        $value = absint((int) $value);
        if ($value < 1) {
            $value = 1;
        }
        if ($value > 10) {
            $value = 10;
        }
        return $value;
    }


    public function sanitize_ticket_promo_codes_option($value) {
        $lines = preg_split('/\r\n|\r|\n/', (string) $value);
        if (!is_array($lines)) {
            return '';
        }

        $normalized = [];
        foreach ($lines as $line) {
            $line = trim((string) $line);
            if ($line === '') {
                continue;
            }
            $parts = array_map('trim', explode('|', $line));
            if (count($parts) < 3) {
                continue;
            }

            $code = strtoupper(preg_replace('/[^A-Z0-9\-_]/', '', (string) $parts[0]));
            if ($code === '' || strlen($code) < 3 || strlen($code) > 40) {
                continue;
            }

            $type = sanitize_key((string) $parts[1]);
            if (!in_array($type, ['percent', 'fixed'], true)) {
                continue;
            }

            $raw_value = str_replace(',', '.', (string) $parts[2]);
            if (!is_numeric($raw_value)) {
                continue;
            }
            $discount_value = (float) $raw_value;
            if ($type === 'percent') {
                $discount_value = max(0, min(100, $discount_value));
                $formatted_value = rtrim(rtrim(number_format($discount_value, 2, '.', ''), '0'), '.');
                if ($formatted_value === '') {
                    $formatted_value = '0';
                }
            } else {
                $discount_value = max(0, min(1000000, $discount_value));
                $formatted_value = number_format($discount_value, 2, '.', '');
            }

            $normalized[$code] = $code . '|' . $type . '|' . $formatted_value;
        }

        if (empty($normalized)) {
            return '';
        }

        ksort($normalized);
        return implode("\n", array_values($normalized));
    }


    private function get_ticket_promo_codes_catalog() {
        static $catalog_cache = null;
        if (is_array($catalog_cache)) {
            return $catalog_cache;
        }

        $catalog_cache = [];
        $raw = trim((string) get_option('eep_ticket_promo_codes', ''));
        if ($raw === '') {
            return $catalog_cache;
        }

        $lines = preg_split('/\r\n|\r|\n/', $raw);
        if (!is_array($lines)) {
            return $catalog_cache;
        }
        foreach ($lines as $line) {
            $line = trim((string) $line);
            if ($line === '') {
                continue;
            }
            $parts = array_map('trim', explode('|', $line));
            if (count($parts) < 3) {
                continue;
            }
            $code = strtoupper(preg_replace('/[^A-Z0-9\-_]/', '', (string) $parts[0]));
            $type = sanitize_key((string) $parts[1]);
            $value = str_replace(',', '.', (string) $parts[2]);
            if ($code === '' || !in_array($type, ['percent', 'fixed'], true) || !is_numeric($value)) {
                continue;
            }
            $catalog_cache[$code] = [
                'code' => $code,
                'type' => $type,
                'value' => (float) $value,
            ];
        }

        return $catalog_cache;
    }


    private function get_ticket_wizard_url($return_url, array $args = []) {
        $return_url = esc_url_raw((string) $return_url);
        if ($return_url === '') {
            $return_url = esc_url_raw($this->get_ticket_return_url());
        }
        $defaults = [
            'eep_ticket_wizard' => '1',
            'eep_ticket_step' => 'payment',
        ];
        return eep_add_query_arg(array_merge($defaults, $args), $return_url);
    }

    private function get_ticket_checkout_resume_url($return_url, array $context = []) {
        $args = [
            'eep_ticket_resume' => '1',
        ];
        $resume_gateway = sanitize_key((string) ($context['gateway'] ?? ''));
        if (in_array($resume_gateway, ['stripe', 'paypal', 'test_free'], true)) {
            $args['eep_ticket_gateway'] = $resume_gateway;
        }

        $context_key = $this->get_ticket_checkout_context_key((string) ($context['context_key'] ?? ''));
        $is_guest_checkout = (string) ($context['guest_checkout'] ?? '0') === '1';
        $guest_callback_token = sanitize_text_field((string) ($context['guest_callback_token'] ?? ''));
        if ($is_guest_checkout && $context_key !== '' && $guest_callback_token !== '') {
            $args['eep_ticket_ctx'] = $context_key;
            $args['eep_ticket_token'] = $guest_callback_token;
        }

        return $this->get_ticket_wizard_url($return_url, $args);
    }

    private function send_ticket_checkout_resume_email(array $context = [], $return_url = '') {
        $context_key = $this->get_ticket_checkout_context_key((string) ($context['context_key'] ?? ''));
        if ($context_key === '') {
            return false;
        }
        if ((string) ($context['is_paid'] ?? '0') !== '1') {
            return false;
        }
        if (sanitize_key((string) ($context['gateway'] ?? '')) === 'test_free') {
            return false;
        }
        if ((string) ($context['recovery_email_sent'] ?? '0') === '1') {
            return true;
        }

        $buyer_email = sanitize_email((string) ($context['buyer_email'] ?? ''));
        if (!is_email($buyer_email)) {
            $user_id = absint((int) ($context['user_id'] ?? 0));
            if ($user_id > 0) {
                $user = get_userdata($user_id);
                if ($user instanceof WP_User && is_email((string) $user->user_email)) {
                    $buyer_email = sanitize_email((string) $user->user_email);
                }
            }
        }
        if (!is_email($buyer_email)) {
            return false;
        }

        $post_id = absint((int) ($context['post_id'] ?? 0));
        $event_title = sanitize_text_field((string) ($context['event_title'] ?? get_the_title($post_id)));
        if ($event_title === '') {
            $event_title = __('Event', 'nexdirectory');
        }

        $platform_name = sanitize_text_field((string) get_option('eep_company_brand_name', ''));
        if ($platform_name === '') {
            $platform_name = sanitize_text_field((string) get_option('blogname', get_bloginfo('name')));
        }
        if ($platform_name === '') {
            $platform_name = __('nexdirectory', 'nexdirectory');
        }

        $resume_url = $this->get_ticket_checkout_resume_url(
            $return_url !== '' ? $return_url : $this->get_ticket_return_url($post_id),
            $context
        );
        $resume_url = esc_url_raw((string) $resume_url);
        if ($resume_url === '') {
            return false;
        }

        $currency = strtoupper(sanitize_text_field((string) ($context['currency'] ?? $this->get_ticket_currency())));
        $amount = round(max(0, (float) ($context['amount'] ?? 0)), 2);
        $price_label = sprintf('%1$s %2$s', $currency, number_format($amount, 2));
        $promo_code = strtoupper(sanitize_text_field((string) ($context['promo_code'] ?? '')));
        $promo_discount = round(max(0, (float) ($context['promo_discount'] ?? 0)), 2);

        $promo_line = '';
        if ($promo_code !== '' && $promo_discount > 0) {
            $promo_line = sprintf(
                '<p style="margin:0 0 10px 0;"><strong>%1$s</strong> %2$s (%3$s %4$s)</p>',
                esc_html__('Promo applied:', 'nexdirectory'),
                esc_html($promo_code),
                esc_html($currency),
                esc_html(number_format($promo_discount, 2))
            );
        }

        $subject = sprintf(
            /* translators: %s: event title */
            __('Your ticket checkout link: %s', 'nexdirectory'),
            $event_title
        );
        $custom_body_html = wp_kses_post(sprintf(
            '<p>%1$s</p><p><strong>%2$s</strong> %3$s</p>%4$s<p><a href="%5$s" style="display:inline-block;padding:10px 14px;background:#111827;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;">%6$s</a></p><p>%7$s</p>',
            esc_html(sprintf(__('Keep this secure checkout link from %s in case you get interrupted.', 'nexdirectory'), $platform_name)),
            esc_html__('Ticket total:', 'nexdirectory'),
            esc_html($price_label),
            $promo_line,
            esc_url($resume_url),
            esc_html__('Resume checkout', 'nexdirectory'),
            esc_html__('If this was not you, you can ignore this email.', 'nexdirectory')
        ));
        $fallback_text = sprintf(
            "Secure checkout link from %1\$s.\nEvent: %2\$s\nTicket total: %3\$s\n\nResume checkout:\n%4\$s\n\nIf this was not you, you can ignore this email.",
            $platform_name,
            $event_title,
            $price_label,
            $resume_url
        );

        return (bool) $this->send_platform_email($buyer_email, $subject, '', [
            'custom_body_html' => $custom_body_html,
            'fallback_text' => $fallback_text,
        ]);
    }


    private function resolve_ticket_promo_discount($promo_code, $base_amount) {
        $base_amount = round(max(0, (float) $base_amount), 2);
        $promo_code = strtoupper(trim((string) $promo_code));
        $promo_code = preg_replace('/[^A-Z0-9\-_]/', '', $promo_code);
        if ($promo_code === '') {
            return [
                'valid' => false,
                'code' => '',
                'type' => '',
                'value' => 0.0,
                'discount' => 0.0,
                'final' => $base_amount,
            ];
        }

        $catalog = $this->get_ticket_promo_codes_catalog();
        $rule = isset($catalog[$promo_code]) && is_array($catalog[$promo_code]) ? $catalog[$promo_code] : [];
        if (empty($rule)) {
            return [
                'valid' => false,
                'code' => $promo_code,
                'type' => '',
                'value' => 0.0,
                'discount' => 0.0,
                'final' => $base_amount,
            ];
        }

        $type = sanitize_key((string) ($rule['type'] ?? ''));
        $value = (float) ($rule['value'] ?? 0);
        $discount = 0.0;
        if ($type === 'percent') {
            $discount = round(($base_amount * max(0, min(100, $value))) / 100, 2);
        } elseif ($type === 'fixed') {
            $discount = round(max(0, $value), 2);
        }
        $discount = min($base_amount, $discount);
        $final = round(max(0, $base_amount - $discount), 2);

        return [
            'valid' => true,
            'code' => (string) ($rule['code'] ?? $promo_code),
            'type' => $type,
            'value' => $value,
            'discount' => $discount,
            'final' => $final,
        ];
    }


    public function sanitize_boost_duration_pricing_option($value) {
        $raw = is_scalar($value) ? trim((string) $value) : '';
        if ($raw === '') {
            return wp_json_encode([
                ['days' => 7, 'price' => 9.99],
                ['days' => 30, 'price' => 29.99],
                ['days' => 90, 'price' => 79.99],
            ]);
        }

        $rows = [];
        $decoded = json_decode($raw, true);
        if (is_array($decoded)) {
            foreach ($decoded as $item) {
                if (!is_array($item)) {
                    continue;
                }
                $days = absint((int) ($item['days'] ?? 0));
                $price = isset($item['price']) && is_numeric($item['price']) ? (float) $item['price'] : 0.0;
                if ($days < 1 || $days > 3650 || $price <= 0) {
                    continue;
                }
                $rows[$days] = ['days' => $days, 'price' => round($price, 2)];
            }
        }
        if (empty($rows)) {
            $lines = preg_split('/\r\n|\r|\n/', $raw);
            if (!is_array($lines)) {
                $lines = [];
            }
            foreach ($lines as $line) {
                $line = trim((string) $line);
                if ($line === '') {
                    continue;
                }
                if (!preg_match('/^(\d{1,4})\s*[:=\-|,]\s*([0-9]+(?:\.[0-9]{1,2})?)$/', $line, $matches)) {
                    continue;
                }
                $days = absint((int) ($matches[1] ?? 0));
                $price = isset($matches[2]) ? (float) $matches[2] : 0.0;
                if ($days < 1 || $days > 3650 || $price <= 0) {
                    continue;
                }
                $rows[$days] = ['days' => $days, 'price' => round($price, 2)];
            }
        }
        if (empty($rows)) {
            return (string) get_option('eep_boost_duration_pricing', wp_json_encode([
                ['days' => 7, 'price' => 9.99],
                ['days' => 30, 'price' => 29.99],
                ['days' => 90, 'price' => 79.99],
            ]));
        }
        ksort($rows);
        return wp_json_encode(array_values($rows));
    }


    public function sanitize_boost_type_pricing_option($value) {
        $raw = is_scalar($value) ? trim((string) $value) : '';
        if ($raw === '') {
            return '';
        }
        $lines = preg_split('/\r\n|\r|\n/', $raw);
        if (!is_array($lines)) {
            $lines = [];
        }
        $out = [];
        foreach ($lines as $line) {
            $line = trim((string) $line);
            if ($line === '') {
                continue;
            }
            $parts = explode('|', $line, 2);
            if (count($parts) !== 2) {
                continue;
            }
            $type_slug = sanitize_title((string) $parts[0]);
            if ($type_slug === '') {
                continue;
            }
            $durations = [];
            $pairs = explode(',', (string) $parts[1]);
            foreach ($pairs as $pair) {
                $pair = trim((string) $pair);
                if ($pair === '' || !preg_match('/^(\d{1,4})\s*[:=]\s*([0-9]+(?:\.[0-9]{1,2})?)$/', $pair, $matches)) {
                    continue;
                }
                $days = absint((int) ($matches[1] ?? 0));
                $price = isset($matches[2]) ? (float) $matches[2] : 0.0;
                if ($days < 1 || $days > 3650 || $price <= 0) {
                    continue;
                }
                $durations[] = $days . ':' . round($price, 2);
            }
            if (!empty($durations)) {
                $out[] = $type_slug . '|' . implode(',', $durations);
            }
        }
        return implode("\n", $out);
    }


    private function is_boost_enabled() {
        return (string) get_option('eep_boost_enabled', '0') === '1';
    }


    private function is_boost_gateway_enabled($gateway) {
        $gateway = sanitize_key((string) $gateway);
        if ($gateway === 'stripe') {
            return (string) get_option('eep_boost_stripe_enabled', '0') === '1';
        }
        if ($gateway === 'paypal') {
            return (string) get_option('eep_boost_paypal_enabled', '0') === '1';
        }
        return false;
    }


    private function get_boost_gateway_mode() {
        $mode = sanitize_key((string) get_option('eep_boost_gateway_mode', 'test'));
        return in_array($mode, ['test', 'live'], true) ? $mode : 'test';
    }


    private function boost_test_free_mode_enabled() {
        return (string) get_option('eep_boost_test_free_mode', '0') === '1';
    }


    private function get_boost_currency() {
        return $this->sanitize_boost_currency_option((string) get_option('eep_boost_currency', 'USD'));
    }


    private function is_ticketing_enabled() {
        return (string) get_option('eep_ticketing_enabled', '1') === '1';
    }

    private function ticket_qr_enabled() {
        return (string) get_option('eep_ticket_qr_enabled', '1') === '1';
    }

    private function is_ticket_sales_enabled_for_post($post_id) {
        $post_id = absint((int) $post_id);
        if ($post_id <= 0) {
            return false;
        }

        $platform_mode = sanitize_key((string) get_option('eep_platform_mode', 'user_generated'));
        $single_org_profile = $platform_mode === 'single_organisation';
        if (!$single_org_profile && method_exists($this, 'resolve_profile_key_for_post')) {
            $resolved_profile_key = sanitize_key((string) $this->resolve_profile_key_for_post($post_id));
            $single_org_profile = $resolved_profile_key === 'events_single_organisation';
        }
        if ($single_org_profile) {
            return true;
        }

        $ticket_sales_raw = (string) get_post_meta($post_id, 'eep_ticket_sales_enabled', true);
        return $ticket_sales_raw === '1';
    }

    private function is_ticket_checkout_enabled_for_post($post_id) {
        $post_id = absint((int) $post_id);
        if ($post_id <= 0) {
            return false;
        }
        if ($this->is_ticketing_enabled()) {
            return true;
        }
        return $this->is_ticket_sales_enabled_for_post($post_id);
    }


    private function is_ticket_gateway_enabled($gateway) {
        $gateway = sanitize_key((string) $gateway);
        if ($gateway === 'stripe') {
            return (string) get_option('eep_ticket_stripe_enabled', '0') === '1';
        }
        if ($gateway === 'paypal') {
            return (string) get_option('eep_ticket_paypal_enabled', '0') === '1';
        }
        return false;
    }


    private function get_ticket_gateway_mode() {
        $mode = sanitize_key((string) get_option('eep_ticket_gateway_mode', 'test'));
        return in_array($mode, ['test', 'live'], true) ? $mode : 'test';
    }


    private function ticket_test_free_mode_enabled() {
        return (string) get_option('eep_ticket_test_free_mode', '0') === '1';
    }


    private function get_ticket_currency() {
        return $this->sanitize_boost_currency_option((string) get_option('eep_ticket_currency', 'USD'));
    }


    private function get_boost_vat_percent() {
        if ((string) get_option('eep_boost_vat_enabled', '0') !== '1') {
            return 0.0;
        }
        return max(0.0, min(1000.0, (float) get_option('eep_boost_vat_percent', '0')));
    }


    private function boost_auto_renew_enabled() {
        return (string) get_option('eep_boost_auto_renew_enabled', '1') === '1';
    }


    private function boost_allow_stacking() {
        return (string) get_option('eep_boost_allow_stacking', '0') === '1';
    }


    private function get_boost_ranking_mode() {
        return $this->sanitize_boost_ranking_mode_option((string) get_option('eep_boost_ranking_mode', 'simple'));
    }


    private function get_boost_duration_pricing() {
        $raw = (string) get_option('eep_boost_duration_pricing', '');
        $decoded = json_decode($raw, true);
        $rows = [];
        if (is_array($decoded)) {
            foreach ($decoded as $item) {
                if (!is_array($item)) {
                    continue;
                }
                $days = absint((int) ($item['days'] ?? 0));
                $price = isset($item['price']) && is_numeric($item['price']) ? (float) $item['price'] : 0.0;
                if ($days < 1 || $price <= 0) {
                    continue;
                }
                $rows[$days] = round($price, 2);
            }
        }
        if (empty($rows)) {
            $rows = [
                7 => 9.99,
                30 => 29.99,
                90 => 79.99,
            ];
        }
        ksort($rows);
        return $rows;
    }


    private function get_boost_type_pricing() {
        $raw = (string) get_option('eep_boost_type_pricing', '');
        if ($raw === '') {
            return [];
        }
        $map = [];
        $lines = preg_split('/\r\n|\r|\n/', $raw);
        if (!is_array($lines)) {
            $lines = [];
        }
        foreach ($lines as $line) {
            $line = trim((string) $line);
            if ($line === '') {
                continue;
            }
            $parts = explode('|', $line, 2);
            if (count($parts) !== 2) {
                continue;
            }
            $slug = sanitize_title((string) $parts[0]);
            if ($slug === '') {
                continue;
            }
            $pairs = explode(',', (string) $parts[1]);
            foreach ($pairs as $pair) {
                $pair = trim((string) $pair);
                if ($pair === '' || !preg_match('/^(\d{1,4})\s*[:=]\s*([0-9]+(?:\.[0-9]{1,2})?)$/', $pair, $matches)) {
                    continue;
                }
                $days = absint((int) ($matches[1] ?? 0));
                $price = isset($matches[2]) ? (float) $matches[2] : 0.0;
                if ($days < 1 || $price <= 0) {
                    continue;
                }
                if (!isset($map[$slug])) {
                    $map[$slug] = [];
                }
                $map[$slug][$days] = round($price, 2);
            }
            if (isset($map[$slug])) {
                ksort($map[$slug]);
            }
        }
        return $map;
    }


    private function resolve_boost_price_for_listing($post_id, $duration_days) {
        $post_id = absint((int) $post_id);
        $duration_days = absint((int) $duration_days);
        if ($duration_days <= 0) {
            return 0.0;
        }

        $type_pricing = $this->get_boost_type_pricing();
        if (!empty($type_pricing)) {
            $slugs = wp_get_post_terms($post_id, self::TAX_TYPE, ['fields' => 'slugs']);
            if (!is_wp_error($slugs) && is_array($slugs)) {
                foreach ($slugs as $slug) {
                    $slug = sanitize_title((string) $slug);
                    if ($slug !== '' && isset($type_pricing[$slug][$duration_days])) {
                        return (float) $type_pricing[$slug][$duration_days];
                    }
                }
            }
        }

        $default_pricing = $this->get_boost_duration_pricing();
        return isset($default_pricing[$duration_days]) ? (float) $default_pricing[$duration_days] : 0.0;
    }


    private function get_default_boost_duration_days() {
        $pricing = $this->get_boost_duration_pricing();
        $days = array_keys($pricing);
        return !empty($days) ? (int) $days[0] : 30;
    }


    private function get_listing_boost_state($post_id) {
        $post_id = absint((int) $post_id);
        $expires_raw = (string) get_post_meta($post_id, 'eep_boost_expires_gmt', true);
        $expires_ts = $expires_raw !== '' ? strtotime($expires_raw . ' UTC') : false;
        $is_active = (string) get_post_meta($post_id, 'eep_boost_active', true) === '1'
            && $expires_ts !== false
            && $expires_ts > time();

        return [
            'active' => $is_active,
            'expires_gmt' => $expires_raw,
            'expires_ts' => $expires_ts ? (int) $expires_ts : 0,
            'expires_label' => $expires_ts ? wp_date(get_option('date_format') . ' ' . get_option('time_format'), (int) $expires_ts) : '',
            'auto_renew' => (string) get_post_meta($post_id, 'eep_boost_auto_renew', true) === '1',
            'gateway' => sanitize_key((string) get_post_meta($post_id, 'eep_boost_gateway', true)),
            'duration_days' => absint((int) get_post_meta($post_id, 'eep_boost_duration_days', true)),
        ];
    }


    private function get_listing_boost_priority_score($post_id) {
        $state = $this->get_listing_boost_state($post_id);
        if (empty($state['active'])) {
            return 0.0;
        }
        $mode = $this->get_boost_ranking_mode();
        $expires_ts = isset($state['expires_ts']) ? (int) $state['expires_ts'] : 0;
        if ($mode !== 'time_weighted' || $expires_ts <= 0) {
            return 10.0;
        }
        $remaining_seconds = max(0, $expires_ts - time());
        $remaining_days = $remaining_seconds / DAY_IN_SECONDS;
        return round(10.0 + $remaining_days, 2);
    }


    private function is_listing_boosted($post_id) {
        $state = $this->get_listing_boost_state($post_id);
        return !empty($state['active']);
    }


    private function get_boost_badge_config() {
        return [
            'show' => (string) get_option('eep_boost_show_featured_badge', '1') === '1',
            'text' => sanitize_text_field((string) get_option('eep_boost_featured_badge_text', __('Featured', 'nexdirectory'))),
            'image' => esc_url((string) get_option('eep_boost_featured_badge_image_url', '')),
            'highlight' => (string) get_option('eep_boost_highlight_style', '1') === '1',
            'verified_badge' => (string) get_option('eep_boost_verified_badge_enabled', '0') === '1',
        ];
    }


    private function create_boost_order_record(array $data = []) {
        global $wpdb;
        $table = $this->get_boost_orders_table_name();
        $now = current_time('mysql', true);
        $order_key = wp_generate_uuid4();
        $defaults = [
            'parent_order_id' => 0,
            'order_key' => $order_key,
            'post_id' => 0,
            'user_id' => 0,
            'gateway' => '',
            'order_status' => 'pending',
            'currency' => $this->get_boost_currency(),
            'subtotal' => 0.0,
            'vat_amount' => 0.0,
            'total_amount' => 0.0,
            'duration_days' => $this->get_default_boost_duration_days(),
            'auto_renew' => 0,
            'is_renewal' => 0,
            'stacking_applied' => 0,
            'external_order_id' => '',
            'external_payment_id' => '',
            'customer_ref' => '',
            'payment_method_ref' => '',
            'payment_meta' => '',
            'renewal_attempts' => 0,
            'max_renewal_attempts' => max(1, (int) get_option('eep_boost_retry_limit', 3)),
            'next_renewal_at' => null,
            'reminder_sent_at' => null,
            'paid_at' => null,
            'expires_at' => null,
            'created_at' => $now,
            'updated_at' => $now,
        ];
        $row = array_merge($defaults, $data);
        $result = $wpdb->insert(
            $table,
            $row,
            [
                '%d', '%s', '%d', '%d', '%s', '%s', '%s',
                '%f', '%f', '%f', '%d', '%d', '%d', '%d',
                '%s', '%s', '%s', '%s', '%s',
                '%d', '%d',
                '%s', '%s', '%s', '%s',
                '%s', '%s',
            ]
        ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

        if (!$result) {
            $this->append_runtime_log('error', 'Could not create boost order', [
                'error' => (string) $wpdb->last_error,
                'post_id' => (int) ($row['post_id'] ?? 0),
            ]);
            return null;
        }
        $row['id'] = (int) $wpdb->insert_id;
        return $row;
    }


    private function get_boost_order_by_id($order_id) {
        $order_id = absint((int) $order_id);
        if ($order_id <= 0) {
            return null;
        }
        global $wpdb;
        $table = $this->get_boost_orders_table_name();
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$table} WHERE id = %d LIMIT 1",
            $order_id
        ), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        return is_array($row) ? $row : null;
    }


    private function get_boost_order_history_for_user($user_id, $limit = 120) {
        $user_id = absint((int) $user_id);
        $limit = max(1, min(600, (int) $limit));
        if ($user_id <= 0) {
            return [];
        }

        global $wpdb;
        $table = $this->get_boost_orders_table_name();
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, post_id, order_status, gateway, currency, total_amount, duration_days, auto_renew, is_renewal, created_at, expires_at
             FROM {$table}
             WHERE user_id = %d
             ORDER BY created_at DESC
             LIMIT %d",
            $user_id,
            $limit
        ), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        if (!is_array($rows) || empty($rows)) {
            return [];
        }

        $status_labels = [
            'pending' => __('Pending', 'nexdirectory'),
            'paid' => __('Paid', 'nexdirectory'),
            'active' => __('Active', 'nexdirectory'),
            'renewed' => __('Renewed', 'nexdirectory'),
            'renewal_failed' => __('Renewal Failed', 'nexdirectory'),
            'cancelled' => __('Cancelled', 'nexdirectory'),
            'failed' => __('Failed', 'nexdirectory'),
            'expired' => __('Expired', 'nexdirectory'),
        ];

        $history = [];
        foreach ($rows as $row) {
            $post_id = absint((int) ($row['post_id'] ?? 0));
            if ($post_id <= 0 || get_post_type($post_id) !== self::POST_TYPE) {
                continue;
            }
            if ((int) get_post_field('post_author', $post_id) !== $user_id) {
                continue;
            }
            $created_ts = strtotime((string) ($row['created_at'] ?? ''));
            $expires_ts = strtotime((string) ($row['expires_at'] ?? ''));
            $status = sanitize_key((string) ($row['order_status'] ?? 'pending'));
            if ($status === '') {
                $status = 'pending';
            }
            $status_label = isset($status_labels[$status]) ? (string) $status_labels[$status] : ucwords(str_replace('_', ' ', $status));
            $gateway = sanitize_key((string) ($row['gateway'] ?? ''));
            $gateway_labels = [
                'stripe' => 'STRIPE',
                'paypal' => 'PAYPAL',
                'test_free' => __('Free Test', 'nexdirectory'),
            ];
            if (!isset($gateway_labels[$gateway])) {
                $gateway = '';
            }
            $history[] = [
                'order_id' => absint((int) ($row['id'] ?? 0)),
                'post_id' => $post_id,
                'title' => sanitize_text_field((string) get_the_title($post_id)),
                'post_url' => (string) get_permalink($post_id),
                'status' => $status,
                'status_label' => sanitize_text_field((string) $status_label),
                'gateway' => $gateway,
                'gateway_label' => $gateway !== '' ? (string) $gateway_labels[$gateway] : __('N/A', 'nexdirectory'),
                'currency' => sanitize_text_field((string) ($row['currency'] ?? $this->get_boost_currency())),
                'total_amount' => round((float) ($row['total_amount'] ?? 0), 2),
                'duration_days' => absint((int) ($row['duration_days'] ?? 0)),
                'auto_renew' => (int) ($row['auto_renew'] ?? 0) === 1,
                'is_renewal' => (int) ($row['is_renewal'] ?? 0) === 1,
                'created_label' => $created_ts ? wp_date(get_option('date_format') . ' ' . get_option('time_format'), (int) $created_ts) : '',
                'expires_label' => $expires_ts ? wp_date(get_option('date_format') . ' ' . get_option('time_format'), (int) $expires_ts) : '',
            ];
        }

        return $history;
    }


    private function update_boost_order($order_id, array $fields = []) {
        $order_id = absint((int) $order_id);
        if ($order_id <= 0 || empty($fields)) {
            return false;
        }
        $allowed = [
            'parent_order_id' => '%d',
            'order_status' => '%s',
            'external_order_id' => '%s',
            'external_payment_id' => '%s',
            'customer_ref' => '%s',
            'payment_method_ref' => '%s',
            'payment_meta' => '%s',
            'renewal_attempts' => '%d',
            'max_renewal_attempts' => '%d',
            'next_renewal_at' => '%s',
            'reminder_sent_at' => '%s',
            'paid_at' => '%s',
            'expires_at' => '%s',
            'auto_renew' => '%d',
            'is_renewal' => '%d',
            'stacking_applied' => '%d',
            'updated_at' => '%s',
        ];
        $data = [];
        $formats = [];
        foreach ($fields as $key => $value) {
            if (!isset($allowed[$key])) {
                continue;
            }
            $data[$key] = $value;
            $formats[] = $allowed[$key];
        }
        if (empty($data)) {
            return false;
        }
        $data['updated_at'] = current_time('mysql', true);
        $formats[] = '%s';

        global $wpdb;
        $table = $this->get_boost_orders_table_name();
        $result = $wpdb->update(
            $table,
            $data,
            ['id' => $order_id],
            $formats,
            ['%d']
        ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        return $result !== false;
    }


    private function format_boost_order_amount_for_gateway($amount) {
        return number_format((float) $amount, 2, '.', '');
    }


    private function get_boost_return_url() {
        $ui_labels = method_exists($this, 'get_site_profile_ui_labels') ? (array) $this->get_site_profile_ui_labels() : [];
        $profile_key = sanitize_key((string) ($ui_labels['profile_key'] ?? 'events'));
        $primary_route = $this->is_boost_enabled() ? 'boost' : 'my_items';
        $primary_option = $this->is_boost_enabled() ? 'eep_page_boost_posts_url' : 'eep_page_my_events_url';
        $url = $this->get_platform_page_url(
            $primary_option,
            $this->get_profile_route_slugs($primary_route, $profile_key),
            $this->get_profile_route_default_url($primary_route, $profile_key)
        );
        if ($url === '' && $primary_route !== 'my_items') {
            $url = $this->get_platform_page_url(
                'eep_page_my_events_url',
                $this->get_profile_route_slugs('my_items', $profile_key),
                $this->get_profile_route_default_url('my_items', $profile_key)
            );
        }
        if ($url === '') {
            $url = home_url('/');
        }
        return $url;
    }


    public function handle_boost_checkout() {
        if (!is_user_logged_in()) {
            wp_die(__('Login required.', 'nexdirectory'));
        }
        $user_id = get_current_user_id();
        $post_id = absint((int) wp_unslash($_POST['post_id'] ?? 0));
        if ($post_id <= 0 || get_post_type($post_id) !== self::POST_TYPE) {
            wp_die(__('Invalid listing selected.', 'nexdirectory'));
        }
        if (!current_user_can('edit_post', $post_id) && (int) get_post_field('post_author', $post_id) !== $user_id && !current_user_can('manage_options')) {
            wp_die(__('Not allowed.', 'nexdirectory'));
        }
        if (!isset($_POST['eep_boost_checkout_nonce']) || !wp_verify_nonce(
            eep_sanitize_text(wp_unslash($_POST['eep_boost_checkout_nonce'])),
            'eep_boost_checkout_' . $post_id
        )) {
            wp_die(__('Invalid request.', 'nexdirectory'));
        }
        if (!$this->is_boost_enabled()) {
            $this->redirect_with_message($this->get_boost_return_url(), __('Boosting is currently disabled by the site administrator.', 'nexdirectory'), 'error');
        }

        $test_free_mode = $this->boost_test_free_mode_enabled();
        $gateway = sanitize_key((string) wp_unslash($_POST['gateway'] ?? ''));
        if ($test_free_mode) {
            $gateway = 'test_free';
        } else {
            if ($gateway === '') {
                $gateway = $this->is_boost_gateway_enabled('stripe') ? 'stripe' : ($this->is_boost_gateway_enabled('paypal') ? 'paypal' : '');
            }
            if (!in_array($gateway, ['stripe', 'paypal'], true) || !$this->is_boost_gateway_enabled($gateway)) {
                $this->redirect_with_message($this->get_boost_return_url(), __('Selected payment gateway is not available.', 'nexdirectory'), 'error');
            }
        }
        $duration_days = absint((int) wp_unslash($_POST['duration_days'] ?? 0));
        $selected_price = $this->resolve_boost_price_for_listing($post_id, $duration_days);
        if ($selected_price <= 0) {
            $this->redirect_with_message($this->get_boost_return_url(), __('Invalid boost duration selected.', 'nexdirectory'), 'error');
        }
        $price = $test_free_mode ? 0.0 : $selected_price;
        $vat_percent = $test_free_mode ? 0.0 : $this->get_boost_vat_percent();
        $vat_amount = $vat_percent > 0 ? round(($price * $vat_percent) / 100, 2) : 0.0;
        $total_amount = round($price + $vat_amount, 2);
        $auto_renew = !$test_free_mode && $this->boost_auto_renew_enabled() && (string) wp_unslash($_POST['auto_renew'] ?? '0') === '1';

        $order = $this->create_boost_order_record([
            'post_id' => $post_id,
            'user_id' => $user_id,
            'gateway' => $gateway,
            'currency' => $this->get_boost_currency(),
            'subtotal' => $price,
            'vat_amount' => $vat_amount,
            'total_amount' => $total_amount,
            'duration_days' => $duration_days,
            'auto_renew' => $auto_renew ? 1 : 0,
            'stacking_applied' => $this->boost_allow_stacking() ? 1 : 0,
            'max_renewal_attempts' => max(1, (int) get_option('eep_boost_retry_limit', 3)),
        ]);

        if (!is_array($order) || empty($order['id'])) {
            $this->redirect_with_message($this->get_boost_return_url(), __('Could not create checkout order. Please try again.', 'nexdirectory'), 'error');
        }

        if ($test_free_mode) {
            $activation = $this->activate_boost_from_order((int) $order['id'], [
                'gateway' => 'test_free',
                'external_order_id' => 'test-free-' . (int) $order['id'],
                'external_payment_id' => '',
                'customer_ref' => '',
                'payment_method_ref' => '',
                'raw' => [
                    'mode' => 'test_free',
                    'activated_by' => (int) $user_id,
                    'activated_at' => current_time('mysql', true),
                ],
            ]);
            if (is_wp_error($activation)) {
                $this->update_boost_order((int) $order['id'], ['order_status' => 'failed']);
                $this->redirect_with_message($this->get_boost_return_url(), (string) $activation->get_error_message(), 'error');
            }
            $this->redirect_with_message($this->get_boost_return_url(), __('Boost activated in free test mode.', 'nexdirectory'));
        }

        $redirect_url = '';
        if ($gateway === 'stripe') {
            $redirect_url = $this->start_stripe_boost_checkout($order, $this->get_boost_return_url());
        } elseif ($gateway === 'paypal') {
            $redirect_url = $this->start_paypal_boost_checkout($order, $this->get_boost_return_url());
        }

        if (is_wp_error($redirect_url)) {
            $this->update_boost_order((int) $order['id'], ['order_status' => 'failed']);
            $this->redirect_with_message($this->get_boost_return_url(), (string) $redirect_url->get_error_message(), 'error');
        }
        if (!is_string($redirect_url) || $redirect_url === '') {
            $this->update_boost_order((int) $order['id'], ['order_status' => 'failed']);
            $this->redirect_with_message($this->get_boost_return_url(), __('Payment checkout could not be initialized.', 'nexdirectory'), 'error');
        }
        $this->safe_redirect_or_fallback($redirect_url);
    }


    private function get_ticket_return_url($post_id = 0) {
        $post_id = absint((int) $post_id);
        if ($post_id > 0) {
            $permalink = get_permalink($post_id);
            if (is_string($permalink) && $permalink !== '') {
                return (string) $permalink;
            }
        }

        $events_url = $this->get_platform_page_url(
            'eep_page_events_url',
            $this->get_profile_route_slugs('events'),
            $this->get_profile_route_default_url('events')
        );
        if ($events_url !== '') {
            return (string) $events_url;
        }

        return home_url('/');
    }

    private function get_ticket_gateway_label($gateway) {
        $gateway = sanitize_key((string) $gateway);
        $labels = [
            'stripe' => 'Stripe',
            'paypal' => 'PayPal',
            'test_free' => __('Free Reservation', 'nexdirectory'),
        ];
        if (isset($labels[$gateway])) {
            return (string) $labels[$gateway];
        }
        if ($gateway === '') {
            return __('N/A', 'nexdirectory');
        }
        return ucwords(str_replace('_', ' ', $gateway));
    }

    private function get_ticket_payment_method_label(array $capture = [], $gateway = '') {
        $label = sanitize_text_field((string) ($capture['payment_method_label'] ?? ''));
        if ($label !== '') {
            return $label;
        }
        $gateway = sanitize_key((string) $gateway);
        if ($gateway === 'test_free') {
            return __('Free Reservation', 'nexdirectory');
        }
        return $this->get_ticket_gateway_label($gateway);
    }

    private function get_current_request_ip() {
        return sanitize_text_field((string) ($_SERVER['REMOTE_ADDR'] ?? ''));
    }

    private function get_current_request_user_agent() {
        $ua = sanitize_text_field((string) ($_SERVER['HTTP_USER_AGENT'] ?? ''));
        return substr($ua, 0, 255);
    }

    private function generate_ticket_uid($post_id, $user_id) {
        $post_part = strtoupper(base_convert((string) max(1, absint((int) $post_id)), 10, 36));
        $user_part = strtoupper(base_convert((string) max(1, absint((int) $user_id)), 10, 36));
        $random_part = strtoupper(substr(str_replace('-', '', wp_generate_uuid4()), 0, 12));
        return sanitize_text_field((string) ('TKT-' . $post_part . '-' . $user_part . '-' . $random_part));
    }

    private function get_ticket_verification_url($ticket_uid, $post_id) {
        $ticket_uid = sanitize_text_field((string) $ticket_uid);
        $post_id = absint((int) $post_id);
        return eep_add_query_arg([
            'eep_ticket_ref' => $ticket_uid,
        ], $this->get_ticket_return_url($post_id));
    }

    private function build_ticket_qr_payload(array $ticket_payload = []) {
        $payload = [
            'ticket_id' => sanitize_text_field((string) ($ticket_payload['ticket_uid'] ?? '')),
            'post_id' => absint((int) ($ticket_payload['post_id'] ?? 0)),
            'user_id' => absint((int) ($ticket_payload['user_id'] ?? 0)),
            'status' => sanitize_key((string) ($ticket_payload['status'] ?? 'confirmed')),
            'currency' => sanitize_text_field((string) ($ticket_payload['currency'] ?? '')),
            'amount' => round((float) ($ticket_payload['amount'] ?? 0), 2),
            'gateway' => sanitize_key((string) ($ticket_payload['gateway'] ?? '')),
            'payment_method' => sanitize_text_field((string) ($ticket_payload['payment_method'] ?? '')),
            'purchased_at' => sanitize_text_field((string) ($ticket_payload['purchased_at'] ?? current_time('mysql', true))),
            'verify_url' => esc_url_raw((string) ($ticket_payload['verify_url'] ?? '')),
        ];
        $json = wp_json_encode($payload);
        if (!is_string($json) || $json === '') {
            return implode('|', [
                (string) ($payload['ticket_id'] ?? ''),
                (string) ($payload['post_id'] ?? '0'),
                (string) ($payload['user_id'] ?? '0'),
                (string) ($payload['status'] ?? ''),
                (string) ($payload['currency'] ?? ''),
                (string) ($payload['amount'] ?? 0),
                (string) ($payload['gateway'] ?? ''),
                (string) ($payload['payment_method'] ?? ''),
                (string) ($payload['purchased_at'] ?? ''),
                (string) ($payload['verify_url'] ?? ''),
            ]);
        }
        return $json;
    }

    private function build_ticket_qr_image_url($qr_payload) {
        $qr_payload = (string) $qr_payload;
        if ($qr_payload === '') {
            return '';
        }
        return esc_url_raw('https://api.qrserver.com/v1/create-qr-code/?size=320x320&margin=12&data=' . rawurlencode($qr_payload));
    }

    private function get_ticket_order_qr_payload_data(array $ticket_order = []) {
        $qr_payload_raw = trim((string) ($ticket_order['qr_payload'] ?? ''));
        if ($qr_payload_raw === '') {
            return [];
        }

        $payload_url = esc_url_raw($qr_payload_raw);
        if ($payload_url !== '' && wp_http_validate_url($payload_url)) {
            return [
                'verify_url' => $payload_url,
            ];
        }

        $payload = json_decode($qr_payload_raw, true);
        if (!is_array($payload)) {
            return [];
        }

        return $payload;
    }

    private function get_ticket_order_verification_url(array $ticket_order = []) {
        $payload = $this->get_ticket_order_qr_payload_data($ticket_order);
        $verify_url = esc_url_raw((string) ($payload['verify_url'] ?? ''));
        if ($verify_url !== '') {
            return $verify_url;
        }

        $ticket_uid = sanitize_text_field((string) ($ticket_order['ticket_uid'] ?? ''));
        $post_id = absint((int) ($ticket_order['post_id'] ?? 0));
        if ($ticket_uid === '' || $post_id <= 0) {
            return '';
        }

        return esc_url_raw((string) $this->get_ticket_verification_url($ticket_uid, $post_id));
    }

    private function format_ticket_purchase_label($purchased_at) {
        $purchased_at = trim((string) $purchased_at);
        if ($purchased_at === '') {
            return '';
        }

        $timestamp = strtotime($purchased_at);
        if (!$timestamp) {
            return '';
        }

        return wp_date(get_option('date_format') . ' ' . get_option('time_format'), $timestamp);
    }

    private function create_ticket_order_record(array $context = [], array $capture = [], $status = 'yes') {
        $post_id = absint((int) ($context['post_id'] ?? 0));
        $user_id = absint((int) ($context['user_id'] ?? 0));
        if ($post_id <= 0 || $user_id <= 0) {
            return [];
        }
        if (!$this->ticket_orders_table_exists()) {
            $this->create_ticket_orders_table();
            if (!$this->ticket_orders_table_exists(true)) {
                return [];
            }
        }

        global $wpdb;
        $table = $this->get_ticket_orders_table_name();
        $status = sanitize_key((string) $status);
        if (!in_array($status, ['yes', 'waitlist'], true)) {
            $status = 'yes';
        }
        $amount = round(max(0, (float) ($context['amount'] ?? 0)), 2);
        $currency = strtoupper(sanitize_text_field((string) ($context['currency'] ?? $this->get_ticket_currency())));
        $gateway = sanitize_key((string) ($capture['gateway'] ?? ($context['gateway'] ?? '')));
        if ($gateway === '') {
            $gateway = $amount > 0 ? 'unknown' : 'test_free';
        }
        $gateway_label = $this->get_ticket_gateway_label($gateway);
        $payment_method = $this->get_ticket_payment_method_label($capture, $gateway);
        $purchase_status = $status === 'waitlist' ? 'waitlist' : 'confirmed';
        $payment_collected = ($amount > 0 && $gateway !== 'test_free') ? 1 : 0;
        $purchased_at = current_time('mysql', true);
        $purchase_ip = sanitize_text_field((string) ($context['purchase_ip'] ?? $this->get_current_request_ip()));
        $purchase_user_agent = substr(sanitize_text_field((string) ($context['purchase_user_agent'] ?? $this->get_current_request_user_agent())), 0, 255);

        $payment_meta_raw = $capture['raw'] ?? [];
        if (!is_array($payment_meta_raw)) {
            $payment_meta_raw = [];
        }
        $payment_meta_raw['context_key'] = sanitize_text_field((string) ($context['context_key'] ?? ''));
        $payment_meta_raw['captured_at'] = $purchased_at;
        $promo_code = strtoupper(sanitize_text_field((string) ($context['promo_code'] ?? '')));
        $promo_discount = round(max(0, (float) ($context['promo_discount'] ?? 0)), 2);
        $amount_before_discount = round(max(0, (float) ($context['amount_before_discount'] ?? $amount)), 2);
        if ($promo_code !== '' && $promo_discount > 0) {
            $payment_meta_raw['promo_code'] = $promo_code;
            $payment_meta_raw['promo_discount'] = $promo_discount;
            $payment_meta_raw['amount_before_discount'] = $amount_before_discount;
        }
        $payment_meta = wp_json_encode($payment_meta_raw);
        if (!is_string($payment_meta)) {
            $payment_meta = '';
        }

        $inserted_row = [];
        for ($attempt = 0; $attempt < 4; $attempt++) {
            $ticket_uid = $this->generate_ticket_uid($post_id, $user_id);
            $verify_url = $this->get_ticket_verification_url($ticket_uid, $post_id);
            $legacy_qr_payload = $this->build_ticket_qr_payload([
                'ticket_uid' => $ticket_uid,
                'post_id' => $post_id,
                'user_id' => $user_id,
                'status' => $purchase_status,
                'currency' => $currency,
                'amount' => $amount,
                'gateway' => $gateway,
                'payment_method' => $payment_method,
                'purchased_at' => $purchased_at,
                'verify_url' => $verify_url,
            ]);
            $qr_payload = '';
            $qr_image_url = '';
            if ($this->ticket_qr_enabled()) {
                // QR scanners should open a direct verification page URL (not raw JSON payload).
                $qr_payload = $verify_url !== '' ? $verify_url : $legacy_qr_payload;
                $qr_image_url = $this->build_ticket_qr_image_url($qr_payload);
            }

            $row = [
                'ticket_uid' => $ticket_uid,
                'post_id' => $post_id,
                'user_id' => $user_id,
                'rsvp_status' => $status,
                'gateway' => $gateway,
                'gateway_label' => $gateway_label,
                'payment_method' => $payment_method,
                'currency' => $currency,
                'ticket_price' => $amount,
                'ticket_total' => $amount,
                'payment_collected' => $payment_collected,
                'payment_status' => $purchase_status,
                'external_order_id' => sanitize_text_field((string) ($capture['external_order_id'] ?? '')),
                'external_payment_id' => sanitize_text_field((string) ($capture['external_payment_id'] ?? '')),
                'customer_ref' => sanitize_text_field((string) ($capture['customer_ref'] ?? '')),
                'payment_method_ref' => sanitize_text_field((string) ($capture['payment_method_ref'] ?? '')),
                'qr_payload' => $qr_payload,
                'qr_image_url' => esc_url_raw($qr_image_url),
                'purchase_ip' => $purchase_ip,
                'purchase_user_agent' => $purchase_user_agent,
                'purchased_at' => $purchased_at,
                'email_sent' => 0,
                'email_sent_at' => '',
                'payment_meta' => $payment_meta,
                'created_at' => $purchased_at,
                'updated_at' => $purchased_at,
            ];

            $inserted = $wpdb->insert(
                $table,
                $row,
                [
                    '%s', '%d', '%d', '%s', '%s', '%s', '%s', '%s',
                    '%f', '%f', '%d', '%s',
                    '%s', '%s', '%s', '%s',
                    '%s', '%s', '%s', '%s', '%s',
                    '%d', '%s', '%s', '%s',
                ]
            ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

            if ($inserted !== false) {
                $row['id'] = (int) $wpdb->insert_id;
                $inserted_row = $row;
                break;
            }

            $last_error = (string) $wpdb->last_error;
            if ($last_error === '' || stripos($last_error, 'ticket_uid') === false) {
                $this->append_runtime_log('error', 'Ticket order insert failed', [
                    'post_id' => (string) $post_id,
                    'user_id' => (string) $user_id,
                    'gateway' => $gateway,
                    'error' => $last_error,
                ]);
                break;
            }
        }

        return is_array($inserted_row) ? $inserted_row : [];
    }

    private function mark_ticket_order_email_status($ticket_order_id, $was_sent = false) {
        $ticket_order_id = absint((int) $ticket_order_id);
        if ($ticket_order_id <= 0) {
            return false;
        }
        if (!$this->ticket_orders_table_exists()) {
            return false;
        }
        global $wpdb;
        $table = $this->get_ticket_orders_table_name();
        $data = [
            'email_sent' => $was_sent ? 1 : 0,
            'updated_at' => current_time('mysql', true),
        ];
        $formats = ['%d', '%s'];
        if ($was_sent) {
            $data['email_sent_at'] = current_time('mysql', true);
            $formats[] = '%s';
        }
        $updated = $wpdb->update(
            $table,
            $data,
            ['id' => $ticket_order_id],
            $formats,
            ['%d']
        ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        return $updated !== false;
    }

    private function get_latest_ticket_orders_by_post_for_user($user_id, array $post_ids = []) {
        $user_id = absint((int) $user_id);
        if ($user_id <= 0) {
            return [];
        }
        if (!$this->ticket_orders_table_exists()) {
            return [];
        }
        $post_ids = array_values(array_unique(array_filter(array_map('absint', $post_ids))));
        if (empty($post_ids)) {
            return [];
        }

        global $wpdb;
        $table = $this->get_ticket_orders_table_name();
        $placeholders = implode(', ', array_fill(0, count($post_ids), '%d'));
        $query = "SELECT *
            FROM {$table}
            WHERE user_id = %d AND post_id IN ({$placeholders})
            ORDER BY purchased_at DESC, id DESC
            LIMIT 500";
        $params = array_merge([$user_id], $post_ids);
        $prepared = $wpdb->prepare($query, ...$params);
        $rows = $wpdb->get_results($prepared, ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        if (!is_array($rows) || empty($rows)) {
            return [];
        }

        $map = [];
        foreach ($rows as $row) {
            $post_id = absint((int) ($row['post_id'] ?? 0));
            if ($post_id <= 0 || isset($map[$post_id])) {
                continue;
            }
            $map[$post_id] = $row;
        }
        return $map;
    }

    private function get_latest_ticket_orders_by_user_for_post($post_id, array $user_ids = []) {
        $post_id = absint((int) $post_id);
        if ($post_id <= 0) {
            return [];
        }
        if (!$this->ticket_orders_table_exists()) {
            return [];
        }
        $user_ids = array_values(array_unique(array_filter(array_map('absint', $user_ids))));
        if (empty($user_ids)) {
            return [];
        }

        global $wpdb;
        $table = $this->get_ticket_orders_table_name();
        $placeholders = implode(', ', array_fill(0, count($user_ids), '%d'));
        $query = "SELECT *
            FROM {$table}
            WHERE post_id = %d AND user_id IN ({$placeholders})
            ORDER BY purchased_at DESC, id DESC
            LIMIT 2000";
        $params = array_merge([$post_id], $user_ids);
        $prepared = $wpdb->prepare($query, ...$params);
        $rows = $wpdb->get_results($prepared, ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        if (!is_array($rows) || empty($rows)) {
            return [];
        }

        $map = [];
        foreach ($rows as $row) {
            $user_id = absint((int) ($row['user_id'] ?? 0));
            if ($user_id <= 0 || isset($map[$user_id])) {
                continue;
            }
            $map[$user_id] = $row;
        }
        return $map;
    }

    private function ensure_ticket_attendee_status($post_id, $user_id) {
        $post_id = absint((int) $post_id);
        $user_id = absint((int) $user_id);
        if ($post_id <= 0 || $user_id <= 0) {
            return new WP_Error('eep_ticket_attendee_invalid', __('Ticket attendee record is invalid.', 'nexdirectory'));
        }

        global $wpdb;
        $table = $this->get_rsvp_table_name();
        $existing_status = sanitize_key((string) $wpdb->get_var($wpdb->prepare(
            "SELECT status FROM {$table} WHERE post_id = %d AND user_id = %d LIMIT 1",
            $post_id,
            $user_id
        )));
        if (in_array($existing_status, ['yes', 'waitlist'], true)) {
            return $existing_status;
        }

        $result = $this->process_rsvp_action($post_id, 'join', $user_id);
        if (is_wp_error($result)) {
            return $result;
        }
        $new_status = sanitize_key((string) ($result['status'] ?? 'yes'));
        if (!in_array($new_status, ['yes', 'waitlist'], true)) {
            $new_status = 'yes';
        }
        return $new_status;
    }


    private function parse_event_ticket_price_amount($post_id) {
        $post_id = absint((int) $post_id);
        if ($post_id <= 0) {
            return 0.0;
        }

        $ticket_price_raw = trim((string) get_post_meta($post_id, 'eep_ticket_price', true));
        if ($ticket_price_raw === '') {
            return 0.0;
        }

        $normalized_ticket_price = str_replace(',', '.', $ticket_price_raw);
        if (is_numeric($normalized_ticket_price)) {
            return max(0.0, (float) $normalized_ticket_price);
        }
        if (preg_match('/\d+(?:\.\d{1,2})?/', $normalized_ticket_price, $ticket_price_match)) {
            return max(0.0, (float) ($ticket_price_match[0] ?? 0));
        }

        return 0.0;
    }


    private function get_ticket_checkout_context_key($context_key) {
        $context_key = strtolower(trim((string) $context_key));
        if (!preg_match('/^[a-z0-9\-]{8,64}$/', $context_key)) {
            return '';
        }
        return $context_key;
    }


    private function get_ticket_checkout_context_transient_key($context_key) {
        $context_key = $this->get_ticket_checkout_context_key($context_key);
        if ($context_key === '') {
            return '';
        }
        return 'eep_ticket_checkout_' . $context_key;
    }


    private function get_ticket_checkout_context($context_key) {
        $transient_key = $this->get_ticket_checkout_context_transient_key($context_key);
        if ($transient_key === '') {
            return [];
        }
        $context = get_transient($transient_key);
        return is_array($context) ? $context : [];
    }


    private function set_ticket_checkout_context($context_key, array $context, $ttl = 7200) {
        $transient_key = $this->get_ticket_checkout_context_transient_key($context_key);
        if ($transient_key === '') {
            return false;
        }
        return set_transient($transient_key, $context, max(300, absint((int) $ttl)));
    }


    private function delete_ticket_checkout_context($context_key) {
        $transient_key = $this->get_ticket_checkout_context_transient_key($context_key);
        if ($transient_key === '') {
            return false;
        }
        return delete_transient($transient_key);
    }


    public function handle_ticket_checkout() {
        $post_id = absint((int) wp_unslash($_POST['post_id'] ?? 0));
        if ($post_id <= 0 || get_post_type($post_id) !== self::POST_TYPE) {
            wp_die(__('Invalid listing selected.', 'nexdirectory'));
        }
        if ((string) get_post_status($post_id) !== 'publish') {
            $this->redirect_with_message($this->get_ticket_return_url($post_id), __('This listing is not available for ticket checkout.', 'nexdirectory'), 'error');
        }
        if (!isset($_POST['eep_ticket_checkout_nonce']) || !wp_verify_nonce(
            eep_sanitize_text(wp_unslash($_POST['eep_ticket_checkout_nonce'])),
            'eep_ticket_checkout_' . $post_id
        )) {
            wp_die(__('Invalid request.', 'nexdirectory'));
        }

        $return_url = $this->get_ticket_return_url($post_id);
        if (!$this->is_ticket_checkout_enabled_for_post($post_id)) {
            $this->redirect_with_message($return_url, __('Ticketing is currently disabled by the site administrator.', 'nexdirectory'), 'error');
        }
        if (!$this->is_ticket_sales_enabled_for_post($post_id)) {
            $this->redirect_with_message($return_url, __('Ticket sales are not enabled for this listing.', 'nexdirectory'), 'error');
        }

        $ticket_price_amount = $this->parse_event_ticket_price_amount($post_id);
        $ticket_base_amount = round(max(0, (float) $ticket_price_amount), 2);
        $promo_code_raw = strtoupper(trim((string) wp_unslash($_POST['promo_code'] ?? '')));
        $promo_code_raw = preg_replace('/[^A-Z0-9\-_]/', '', $promo_code_raw);
        $promo_resolution = [
            'valid' => false,
            'code' => '',
            'type' => '',
            'value' => 0.0,
            'discount' => 0.0,
            'final' => $ticket_base_amount,
        ];
        if ($promo_code_raw !== '' && $ticket_base_amount > 0) {
            $promo_resolution = $this->resolve_ticket_promo_discount($promo_code_raw, $ticket_base_amount);
            if (empty($promo_resolution['valid'])) {
                $promo_error_url = $this->get_ticket_wizard_url($return_url, [
                    'eep_ticket_promo' => $promo_code_raw,
                ]);
                $this->redirect_with_message($promo_error_url, __('Promo code is invalid or expired. Please try another code.', 'nexdirectory'), 'error');
            }
        }
        $ticket_price_amount = round(max(0, (float) ($promo_resolution['final'] ?? $ticket_base_amount)), 2);
        $ticket_is_paid = $ticket_price_amount > 0;
        $gateway = sanitize_key((string) wp_unslash($_POST['gateway'] ?? ''));
        $ticket_test_free_mode = $this->ticket_test_free_mode_enabled();
        $ticket_gateway_stripe_enabled = $this->is_ticket_gateway_enabled('stripe');
        $ticket_gateway_paypal_enabled = $this->is_ticket_gateway_enabled('paypal');

        if (!$ticket_is_paid || $ticket_test_free_mode) {
            $gateway = 'test_free';
        } else {
            if ($gateway === '') {
                $gateway = $ticket_gateway_stripe_enabled ? 'stripe' : ($ticket_gateway_paypal_enabled ? 'paypal' : '');
            }
            if (!in_array($gateway, ['stripe', 'paypal'], true)) {
                $this->redirect_with_message($return_url, __('No valid payment gateway is configured for ticket checkout.', 'nexdirectory'), 'error');
            }
            if (($gateway === 'stripe' && !$ticket_gateway_stripe_enabled) || ($gateway === 'paypal' && !$ticket_gateway_paypal_enabled)) {
                $this->redirect_with_message($return_url, __('Selected payment gateway is not available.', 'nexdirectory'), 'error');
            }
        }

        $context = [
            'context_key' => '',
            'post_id' => $post_id,
            'user_id' => 0,
            'gateway' => $gateway,
            'currency' => $this->get_ticket_currency(),
            'amount' => round((float) $ticket_price_amount, 2),
            'amount_before_discount' => $ticket_base_amount,
            'is_paid' => $ticket_is_paid ? 1 : 0,
            'created_at' => time(),
            'event_title' => sanitize_text_field((string) get_the_title($post_id)),
            'purchase_ip' => $this->get_current_request_ip(),
            'purchase_user_agent' => $this->get_current_request_user_agent(),
            'buyer_email' => '',
            'guest_checkout' => '0',
            'promo_code' => !empty($promo_resolution['valid']) ? sanitize_text_field((string) ($promo_resolution['code'] ?? '')) : '',
            'promo_type' => !empty($promo_resolution['valid']) ? sanitize_key((string) ($promo_resolution['type'] ?? '')) : '',
            'promo_value' => !empty($promo_resolution['valid']) ? round(max(0, (float) ($promo_resolution['value'] ?? 0)), 2) : 0.0,
            'promo_discount' => !empty($promo_resolution['valid']) ? round(max(0, (float) ($promo_resolution['discount'] ?? 0)), 2) : 0.0,
            'recovery_email_sent' => '0',
        ];

        if (!is_user_logged_in()) {
            $guest_resume_requested = (string) wp_unslash($_POST['guest_resume'] ?? '0') === '1';
            if ($guest_resume_requested) {
                $resume_context_key = $this->get_ticket_checkout_context_key(wp_unslash($_POST['guest_ctx'] ?? ''));
                $resume_context = $this->get_ticket_checkout_context($resume_context_key);
                $resume_token = sanitize_text_field((string) wp_unslash($_POST['guest_token'] ?? ''));
                if ($resume_context_key === '' || empty($resume_context)) {
                    $this->redirect_with_message($return_url, __('Ticket checkout session expired. Please restart checkout.', 'nexdirectory'), 'error');
                }

                $resume_post_id = absint((int) ($resume_context['post_id'] ?? 0));
                $stored_resume_token = sanitize_text_field((string) ($resume_context['guest_callback_token'] ?? ''));
                if (
                    $resume_post_id !== $post_id
                    || (string) ($resume_context['guest_checkout'] ?? '0') !== '1'
                    || (string) ($resume_context['guest_verified'] ?? '0') !== '1'
                    || $resume_token === ''
                    || $stored_resume_token === ''
                    || !hash_equals($stored_resume_token, $resume_token)
                ) {
                    $this->redirect_with_message($return_url, __('Guest checkout verification is invalid or expired. Please start again.', 'nexdirectory'), 'error');
                }

                $resume_base_amount = round(max(0, (float) ($resume_context['amount_before_discount'] ?? $this->parse_event_ticket_price_amount($post_id))), 2);
                $resume_promo_code = strtoupper(trim((string) wp_unslash($_POST['promo_code'] ?? '')));
                $resume_promo_code = preg_replace('/[^A-Z0-9\-_]/', '', $resume_promo_code);
                $resume_promo_resolution = [
                    'valid' => false,
                    'code' => '',
                    'type' => '',
                    'value' => 0.0,
                    'discount' => 0.0,
                    'final' => $resume_base_amount,
                ];
                if ($resume_promo_code !== '' && $resume_base_amount > 0) {
                    $resume_promo_resolution = $this->resolve_ticket_promo_discount($resume_promo_code, $resume_base_amount);
                    if (empty($resume_promo_resolution['valid'])) {
                        $retry_url = $this->get_ticket_checkout_resume_url($return_url, $resume_context);
                        $retry_url = eep_add_query_arg(['eep_ticket_promo' => $resume_promo_code], $retry_url);
                        $this->redirect_with_message($retry_url, __('Promo code is invalid or expired. Please try another code.', 'nexdirectory'), 'error');
                    }
                }
                $resume_context['amount_before_discount'] = $resume_base_amount;
                $resume_context['amount'] = round(max(0, (float) ($resume_promo_resolution['final'] ?? $resume_base_amount)), 2);
                $resume_context['promo_code'] = !empty($resume_promo_resolution['valid']) ? sanitize_text_field((string) ($resume_promo_resolution['code'] ?? '')) : '';
                $resume_context['promo_type'] = !empty($resume_promo_resolution['valid']) ? sanitize_key((string) ($resume_promo_resolution['type'] ?? '')) : '';
                $resume_context['promo_value'] = !empty($resume_promo_resolution['valid']) ? round(max(0, (float) ($resume_promo_resolution['value'] ?? 0)), 2) : 0.0;
                $resume_context['promo_discount'] = !empty($resume_promo_resolution['valid']) ? round(max(0, (float) ($resume_promo_resolution['discount'] ?? 0)), 2) : 0.0;
                $resume_context['is_paid'] = ((float) ($resume_context['amount'] ?? 0)) > 0 ? 1 : 0;

                $resume_gateway = sanitize_key((string) wp_unslash($_POST['gateway'] ?? ($resume_context['gateway'] ?? '')));
                $resume_is_paid = !empty($resume_context['is_paid']);
                if (!$resume_is_paid || $ticket_test_free_mode) {
                    $resume_gateway = 'test_free';
                } else {
                    if ($resume_gateway === '') {
                        $resume_gateway = $ticket_gateway_stripe_enabled ? 'stripe' : ($ticket_gateway_paypal_enabled ? 'paypal' : '');
                    }
                    if (!in_array($resume_gateway, ['stripe', 'paypal'], true)) {
                        $this->redirect_with_message($return_url, __('No valid payment gateway is configured for ticket checkout.', 'nexdirectory'), 'error');
                    }
                    if (($resume_gateway === 'stripe' && !$ticket_gateway_stripe_enabled) || ($resume_gateway === 'paypal' && !$ticket_gateway_paypal_enabled)) {
                        $this->redirect_with_message($return_url, __('Selected payment gateway is not available.', 'nexdirectory'), 'error');
                    }
                }

                $resume_context['gateway'] = $resume_gateway;
                $resume_context['created_at'] = time();
                $this->set_ticket_checkout_context($resume_context_key, $resume_context, 2 * HOUR_IN_SECONDS);

                if (!$resume_is_paid || $resume_gateway === 'test_free') {
                    $finalize = $this->finalize_ticket_registration($resume_context, [
                        'gateway' => $resume_gateway,
                        'external_order_id' => 'ticket-free-' . (string) $resume_context_key,
                        'external_payment_id' => '',
                        'customer_ref' => '',
                        'payment_method_ref' => '',
                        'payment_method_label' => __('Free Reservation', 'nexdirectory'),
                        'raw' => [
                            'mode' => $resume_gateway,
                            'created_at' => current_time('mysql', true),
                        ],
                    ]);
                    $this->delete_ticket_checkout_context($resume_context_key);
                    if (is_wp_error($finalize)) {
                        $this->redirect_with_message($return_url, (string) $finalize->get_error_message(), 'error');
                    }

                    $message = is_array($finalize) && !empty($finalize['message'])
                        ? sanitize_text_field((string) $finalize['message'])
                        : __('Ticket confirmed. A confirmation email has been sent.', 'nexdirectory');
                    $this->redirect_with_message($return_url, $message, 'success');
                }

                $resume_redirect_url = '';
                if ($resume_is_paid && $resume_gateway !== 'test_free') {
                    if ($this->send_ticket_checkout_resume_email($resume_context, $return_url)) {
                        $resume_context['recovery_email_sent'] = '1';
                        $this->set_ticket_checkout_context($resume_context_key, $resume_context, 2 * HOUR_IN_SECONDS);
                    }
                }
                if ($resume_gateway === 'stripe') {
                    $resume_redirect_url = $this->start_stripe_ticket_checkout($resume_context, $resume_context_key, $return_url);
                } elseif ($resume_gateway === 'paypal') {
                    $resume_redirect_url = $this->start_paypal_ticket_checkout($resume_context, $resume_context_key, $return_url);
                }

                if (is_wp_error($resume_redirect_url)) {
                    $this->append_runtime_log('warning', 'Guest ticket checkout initialization failed', [
                        'post_id' => $post_id,
                        'context' => (string) $resume_context_key,
                        'gateway' => $resume_gateway,
                        'error' => (string) $resume_redirect_url->get_error_message(),
                    ]);
                    $resume_retry_url = $this->get_ticket_checkout_resume_url($return_url, $resume_context);
                    $resume_retry_promo = strtoupper(sanitize_text_field((string) ($resume_context['promo_code'] ?? '')));
                    if ($resume_retry_promo !== '') {
                        $resume_retry_url = eep_add_query_arg(['eep_ticket_promo' => $resume_retry_promo], $resume_retry_url);
                    }
                    $this->redirect_with_message($resume_retry_url, (string) $resume_redirect_url->get_error_message(), 'error');
                }
                if (!is_string($resume_redirect_url) || $resume_redirect_url === '') {
                    $resume_retry_url = $this->get_ticket_checkout_resume_url($return_url, $resume_context);
                    $resume_retry_promo = strtoupper(sanitize_text_field((string) ($resume_context['promo_code'] ?? '')));
                    if ($resume_retry_promo !== '') {
                        $resume_retry_url = eep_add_query_arg(['eep_ticket_promo' => $resume_retry_promo], $resume_retry_url);
                    }
                    $this->redirect_with_message($resume_retry_url, __('Ticket checkout could not be initialized.', 'nexdirectory'), 'error');
                }

                $this->safe_redirect_or_fallback($resume_redirect_url);
            }

            $guest_checkout_requested = (string) wp_unslash($_POST['guest_checkout'] ?? '0') === '1';
            if (!$guest_checkout_requested) {
                $this->redirect_with_message(
                    $this->get_login_signup_url($return_url),
                    __('Please sign in or choose guest checkout to continue.', 'nexdirectory'),
                    'error'
                );
            }

            $guest_email = sanitize_email((string) wp_unslash($_POST['guest_email'] ?? ''));
            if (!is_email($guest_email)) {
                $this->redirect_with_message($return_url, __('Enter a valid email address for guest checkout.', 'nexdirectory'), 'error');
            }

            $context_key = wp_generate_uuid4();
            $guest_verification_token = wp_generate_password(32, false, false);
            $context['context_key'] = (string) $context_key;
            $context['guest_checkout'] = '1';
            $context['guest_email'] = $guest_email;
            $context['guest_name'] = sanitize_text_field((string) wp_unslash($_POST['guest_name'] ?? ''));
            $context['guest_verification_token'] = $guest_verification_token;
            $context['buyer_email'] = $guest_email;

            $this->set_ticket_checkout_context($context_key, $context, 2 * DAY_IN_SECONDS);
            $sent = $this->send_guest_ticket_verification_email($context, $return_url);
            if (!$sent) {
                $this->delete_ticket_checkout_context($context_key);
                $this->redirect_with_message($return_url, __('Could not send the verification email. Please try again.', 'nexdirectory'), 'error');
            }

            $this->redirect_with_message(
                $return_url,
                __('Check your email to verify and continue ticket checkout.', 'nexdirectory'),
                'success'
            );
        }

        $user_id = get_current_user_id();
        $user = get_userdata($user_id);
        if ($user instanceof WP_User && is_email((string) $user->user_email)) {
            $context['buyer_email'] = sanitize_email((string) $user->user_email);
        }

        $context_key = wp_generate_uuid4();
        $context['context_key'] = (string) $context_key;
        $context['user_id'] = $user_id;
        $this->set_ticket_checkout_context($context_key, $context, 2 * HOUR_IN_SECONDS);

        if (!$ticket_is_paid || $gateway === 'test_free') {
            $finalize = $this->finalize_ticket_registration($context, [
                'gateway' => $gateway,
                'external_order_id' => 'ticket-free-' . (string) $context_key,
                'external_payment_id' => '',
                'customer_ref' => '',
                'payment_method_ref' => '',
                'payment_method_label' => __('Free Reservation', 'nexdirectory'),
                'raw' => [
                    'mode' => $gateway,
                    'created_at' => current_time('mysql', true),
                ],
            ]);
            $this->delete_ticket_checkout_context($context_key);
            if (is_wp_error($finalize)) {
                $this->redirect_with_message($return_url, (string) $finalize->get_error_message(), 'error');
            }

            $message = is_array($finalize) && !empty($finalize['message'])
                ? sanitize_text_field((string) $finalize['message'])
                : __('Ticket confirmed. A confirmation email has been sent.', 'nexdirectory');
            $this->redirect_with_message($return_url, $message, 'success');
        }

        $redirect_url = '';
        if ($ticket_is_paid && $gateway !== 'test_free') {
            if ($this->send_ticket_checkout_resume_email($context, $return_url)) {
                $context['recovery_email_sent'] = '1';
                $this->set_ticket_checkout_context($context_key, $context, 2 * HOUR_IN_SECONDS);
            }
        }
        if ($gateway === 'stripe') {
            $redirect_url = $this->start_stripe_ticket_checkout($context, $context_key, $return_url);
        } elseif ($gateway === 'paypal') {
            $redirect_url = $this->start_paypal_ticket_checkout($context, $context_key, $return_url);
        }

        if (is_wp_error($redirect_url)) {
            $this->append_runtime_log('warning', 'Ticket checkout initialization failed', [
                'post_id' => $post_id,
                'user_id' => $user_id,
                'gateway' => $gateway,
                'error' => (string) $redirect_url->get_error_message(),
            ]);
            $retry_url = $this->get_ticket_checkout_resume_url($return_url, $context);
            $retry_promo = strtoupper(sanitize_text_field((string) ($context['promo_code'] ?? '')));
            if ($retry_promo !== '') {
                $retry_url = eep_add_query_arg(['eep_ticket_promo' => $retry_promo], $retry_url);
            }
            $this->redirect_with_message($retry_url, (string) $redirect_url->get_error_message(), 'error');
        }
        if (!is_string($redirect_url) || $redirect_url === '') {
            $retry_url = $this->get_ticket_checkout_resume_url($return_url, $context);
            $retry_promo = strtoupper(sanitize_text_field((string) ($context['promo_code'] ?? '')));
            if ($retry_promo !== '') {
                $retry_url = eep_add_query_arg(['eep_ticket_promo' => $retry_promo], $retry_url);
            }
            $this->redirect_with_message($retry_url, __('Ticket checkout could not be initialized.', 'nexdirectory'), 'error');
        }

        $this->safe_redirect_or_fallback($redirect_url);
    }


    private function send_guest_ticket_verification_email(array $context = [], $return_url = '') {
        $guest_email = sanitize_email((string) ($context['guest_email'] ?? ''));
        $context_key = sanitize_text_field((string) ($context['context_key'] ?? ''));
        $token = sanitize_text_field((string) ($context['guest_verification_token'] ?? ''));
        $post_id = absint((int) ($context['post_id'] ?? 0));
        if (!is_email($guest_email) || $context_key === '' || $token === '' || $post_id <= 0) {
            return false;
        }

        $event_title = sanitize_text_field((string) ($context['event_title'] ?? get_the_title($post_id)));
        if ($event_title === '') {
            $event_title = __('Event', 'nexdirectory');
        }
        $platform_name = sanitize_text_field((string) get_option('eep_company_brand_name', ''));
        if ($platform_name === '') {
            $platform_name = sanitize_text_field((string) get_option('blogname', get_bloginfo('name')));
        }
        if ($platform_name === '') {
            $platform_name = __('nexdirectory', 'nexdirectory');
        }

        $continue_url = eep_add_query_arg([
            'eep_ticket_guest_verify' => '1',
            'eep_ticket_ctx' => $context_key,
            'token' => $token,
        ], $return_url !== '' ? $return_url : $this->get_ticket_return_url($post_id));
        $continue_url = esc_url_raw((string) $continue_url);

        $subject = sprintf(
            /* translators: %s: event title */
            __('Confirm your email to continue ticket checkout: %s', 'nexdirectory'),
            $event_title
        );
        $custom_body_html = wp_kses_post(sprintf(
            '<p>%1$s</p><p>%2$s</p><p><a href="%3$s" style="display:inline-block;padding:10px 14px;background:#111827;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;">%4$s</a></p><p>%5$s</p>',
            esc_html(sprintf(__('You started a guest ticket checkout on %s.', 'nexdirectory'), $platform_name)),
            esc_html(sprintf(__('Event: %s', 'nexdirectory'), $event_title)),
            esc_url($continue_url),
            esc_html__('Verify email and continue', 'nexdirectory'),
            esc_html__('If you did not request this, you can ignore this email.', 'nexdirectory')
        ));
        $fallback = sprintf(
            "You started a guest ticket checkout on %1\$s.\nEvent: %2\$s\n\nVerify your email and continue:\n%3\$s\n\nIf you did not request this, ignore this email.",
            $platform_name,
            $event_title,
            $continue_url
        );

        return (bool) $this->send_platform_email($guest_email, $subject, '', [
            'custom_body_html' => $custom_body_html,
            'fallback_text' => $fallback,
        ]);
    }


    private function get_or_create_guest_ticket_user($guest_email, $guest_name = '') {
        $guest_email = sanitize_email((string) $guest_email);
        $guest_name = sanitize_text_field((string) $guest_name);
        if (!is_email($guest_email)) {
            return 0;
        }

        $existing_user = get_user_by('email', $guest_email);
        if ($existing_user instanceof WP_User) {
            return absint((int) $existing_user->ID);
        }

        $local_part = (string) strtok($guest_email, '@');
        $base_login = sanitize_user($local_part, true);
        if ($base_login === '') {
            $base_login = 'guest';
        }
        $base_login = strtolower(substr($base_login, 0, 40));
        $candidate_login = $base_login;
        $suffix = 1;
        while (username_exists($candidate_login) && $suffix < 200) {
            $candidate_login = substr($base_login, 0, 32) . $suffix;
            $suffix++;
        }
        if (username_exists($candidate_login)) {
            return 0;
        }

        $display_name = $guest_name !== '' ? $guest_name : ucwords(str_replace(['.', '_', '-'], ' ', $local_part));
        $display_name = trim((string) preg_replace('/\s+/u', ' ', $display_name));
        if ($display_name === '') {
            $display_name = $candidate_login;
        }

        $created = wp_insert_user([
            'user_login' => $candidate_login,
            'user_pass' => wp_generate_password(24, true, true),
            'user_email' => $guest_email,
            'display_name' => $display_name,
            'nickname' => $display_name,
            'role' => 'subscriber',
        ]);
        if (is_wp_error($created)) {
            return 0;
        }

        return absint((int) $created);
    }


    private function handle_guest_ticket_verification_return() {
        if (empty($_GET['eep_ticket_guest_verify']) || empty($_GET['eep_ticket_ctx']) || empty($_GET['token'])) {
            return false;
        }

        $context_key = $this->get_ticket_checkout_context_key(wp_unslash($_GET['eep_ticket_ctx']));
        $context = $this->get_ticket_checkout_context($context_key);
        $return_url = $this->get_ticket_return_url(absint((int) ($context['post_id'] ?? 0)));
        if ($context_key === '' || empty($context)) {
            $this->redirect_with_message($this->get_ticket_return_url(), __('Ticket verification link is invalid or expired.', 'nexdirectory'), 'error');
        }

        $token = sanitize_text_field((string) wp_unslash($_GET['token']));
        $stored_token = sanitize_text_field((string) ($context['guest_verification_token'] ?? ''));
        if (
            (string) ($context['guest_checkout'] ?? '0') !== '1'
            || $token === ''
            || $stored_token === ''
            || !hash_equals($stored_token, $token)
        ) {
            $this->redirect_with_message($return_url, __('Ticket verification link is invalid or expired.', 'nexdirectory'), 'error');
        }

        $guest_email = sanitize_email((string) ($context['guest_email'] ?? ''));
        if (!is_email($guest_email)) {
            $this->delete_ticket_checkout_context($context_key);
            $this->redirect_with_message($return_url, __('Guest checkout email is missing. Please start again.', 'nexdirectory'), 'error');
        }

        $guest_user_id = $this->get_or_create_guest_ticket_user($guest_email, (string) ($context['guest_name'] ?? ''));
        if ($guest_user_id <= 0) {
            $this->delete_ticket_checkout_context($context_key);
            $this->redirect_with_message($return_url, __('Could not create a guest ticket account. Please try again.', 'nexdirectory'), 'error');
        }
        update_user_meta($guest_user_id, 'eep_email_verified', '1');

        $context['user_id'] = $guest_user_id;
        $context['buyer_email'] = $guest_email;
        $context['guest_verification_token'] = '';
        $context['guest_callback_token'] = sanitize_text_field((string) ($context['guest_callback_token'] ?? ''));
        if ($context['guest_callback_token'] === '') {
            $context['guest_callback_token'] = wp_generate_password(24, false, false);
        }
        $context['guest_verified'] = '1';
        $context['created_at'] = time();
        $this->set_ticket_checkout_context($context_key, $context, 2 * HOUR_IN_SECONDS);

        $wizard_url = eep_add_query_arg([
            'eep_ticket_wizard' => '1',
            'eep_ticket_step' => 'payment',
            'eep_ticket_ctx' => (string) $context_key,
            'eep_ticket_token' => (string) $context['guest_callback_token'],
        ], $return_url);
        $this->redirect_with_message($wizard_url, __('Email verified. Continue checkout in the ticket popup.', 'nexdirectory'), 'success');
        return true;
    }


    private function start_stripe_ticket_checkout(array $context, $context_key, $return_url) {
        $secret_key = $this->get_active_ticket_stripe_secret_key();
        if ($secret_key === '') {
            return new WP_Error('eep_ticket_stripe_missing_key', __('Stripe secret key is missing for ticket checkout.', 'nexdirectory'));
        }

        $post_id = absint((int) ($context['post_id'] ?? 0));
        $user_id = absint((int) ($context['user_id'] ?? 0));
        $amount_cents = max(1, (int) round(((float) ($context['amount'] ?? 0)) * 100));
        $currency = strtolower(sanitize_text_field((string) ($context['currency'] ?? $this->get_ticket_currency())));
        $event_title = sanitize_text_field((string) ($context['event_title'] ?? get_the_title($post_id)));
        if ($event_title === '') {
            $event_title = __('Event', 'nexdirectory');
        }
        $guest_callback_token = sanitize_text_field((string) ($context['guest_callback_token'] ?? ''));
        $is_guest_checkout = (string) ($context['guest_checkout'] ?? '0') === '1' && $guest_callback_token !== '';
        $buyer_email = sanitize_email((string) ($context['buyer_email'] ?? ''));

        $success_args = [
            'eep_ticket_gateway' => 'stripe',
            'eep_ticket_success' => '1',
            'eep_ticket_ctx' => (string) $context_key,
            'session_id' => '{CHECKOUT_SESSION_ID}',
        ];
        $cancel_args = [
            'eep_ticket_gateway' => 'stripe',
            'eep_ticket_cancel' => '1',
            'eep_ticket_ctx' => (string) $context_key,
        ];
        if ($is_guest_checkout) {
            $success_args['eep_ticket_guest'] = '1';
            $success_args['eep_ticket_token'] = $guest_callback_token;
            $cancel_args['eep_ticket_guest'] = '1';
            $cancel_args['eep_ticket_token'] = $guest_callback_token;
        }
        $success_url = eep_add_query_arg($success_args, $return_url);
        $cancel_url = eep_add_query_arg($cancel_args, $return_url);

        $request_body = [
            'mode' => 'payment',
            'success_url' => $success_url,
            'cancel_url' => $cancel_url,
            'line_items[0][quantity]' => '1',
            'line_items[0][price_data][currency]' => $currency,
            'line_items[0][price_data][unit_amount]' => (string) $amount_cents,
            'line_items[0][price_data][product_data][name]' => sprintf(
                /* translators: %s: event title */
                __('Ticket: %s', 'nexdirectory'),
                $event_title
            ),
            'metadata[context_key]' => (string) $context_key,
            'metadata[post_id]' => (string) $post_id,
            'metadata[user_id]' => (string) $user_id,
            'payment_intent_data[metadata][context_key]' => (string) $context_key,
            'payment_intent_data[metadata][post_id]' => (string) $post_id,
            'payment_intent_data[metadata][user_id]' => (string) $user_id,
        ];
        $promo_code = strtoupper(sanitize_text_field((string) ($context['promo_code'] ?? '')));
        $promo_discount = round(max(0, (float) ($context['promo_discount'] ?? 0)), 2);
        if ($promo_code !== '') {
            $request_body['metadata[promo_code]'] = $promo_code;
            $request_body['payment_intent_data[metadata][promo_code]'] = $promo_code;
        }
        if ($promo_discount > 0) {
            $request_body['metadata[promo_discount]'] = number_format($promo_discount, 2, '.', '');
            $request_body['payment_intent_data[metadata][promo_discount]'] = number_format($promo_discount, 2, '.', '');
        }
        if ($buyer_email !== '') {
            $request_body['customer_email'] = $buyer_email;
        }

        $response = wp_remote_post('https://api.stripe.com/v1/checkout/sessions', [
            'timeout' => 25,
            'headers' => [
                'Authorization' => 'Bearer ' . $secret_key,
                'Content-Type' => 'application/x-www-form-urlencoded',
            ],
            'body' => $request_body,
        ]);
        if (is_wp_error($response)) {
            return new WP_Error('eep_ticket_stripe_request_failed', __('Stripe ticket checkout request failed.', 'nexdirectory'));
        }

        $status = (int) wp_remote_retrieve_response_code($response);
        $payload = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($payload)) {
            $message = __('Stripe ticket checkout could not be created.', 'nexdirectory');
            if (is_array($payload) && !empty($payload['error']['message'])) {
                $message = sanitize_text_field((string) $payload['error']['message']);
            }
            return new WP_Error('eep_ticket_stripe_checkout_error', $message);
        }

        $session_id = sanitize_text_field((string) ($payload['id'] ?? ''));
        $url = esc_url_raw((string) ($payload['url'] ?? ''));
        if ($session_id !== '') {
            $context['external_order_id'] = $session_id;
            $this->set_ticket_checkout_context($context_key, $context, 2 * HOUR_IN_SECONDS);
        }
        if ($url === '') {
            return new WP_Error('eep_ticket_stripe_missing_url', __('Stripe ticket checkout URL is missing from API response.', 'nexdirectory'));
        }

        return $url;
    }


    private function start_paypal_ticket_checkout(array $context, $context_key, $return_url) {
        $auth = $this->get_ticket_paypal_access_token();
        if (is_wp_error($auth)) {
            return $auth;
        }

        $post_id = absint((int) ($context['post_id'] ?? 0));
        $amount = $this->format_boost_order_amount_for_gateway((float) ($context['amount'] ?? 0));
        $currency = sanitize_text_field((string) ($context['currency'] ?? $this->get_ticket_currency()));
        $event_title = sanitize_text_field((string) ($context['event_title'] ?? get_the_title($post_id)));
        if ($event_title === '') {
            $event_title = __('Event', 'nexdirectory');
        }
        $promo_code = strtoupper(sanitize_text_field((string) ($context['promo_code'] ?? '')));
        $promo_discount = round(max(0, (float) ($context['promo_discount'] ?? 0)), 2);
        $description = sprintf(
            /* translators: %s: event title */
            __('Ticket: %s', 'nexdirectory'),
            $event_title
        );
        if ($promo_code !== '' && $promo_discount > 0) {
            $description .= ' [' . $promo_code . ']';
        }
        $guest_callback_token = sanitize_text_field((string) ($context['guest_callback_token'] ?? ''));
        $is_guest_checkout = (string) ($context['guest_checkout'] ?? '0') === '1' && $guest_callback_token !== '';

        $success_args = [
            'eep_ticket_gateway' => 'paypal',
            'eep_ticket_success' => '1',
            'eep_ticket_ctx' => (string) $context_key,
        ];
        $cancel_args = [
            'eep_ticket_gateway' => 'paypal',
            'eep_ticket_cancel' => '1',
            'eep_ticket_ctx' => (string) $context_key,
        ];
        if ($is_guest_checkout) {
            $success_args['eep_ticket_guest'] = '1';
            $success_args['eep_ticket_token'] = $guest_callback_token;
            $cancel_args['eep_ticket_guest'] = '1';
            $cancel_args['eep_ticket_token'] = $guest_callback_token;
        }
        $success_url = eep_add_query_arg($success_args, $return_url);
        $cancel_url = eep_add_query_arg($cancel_args, $return_url);

        $payload = [
            'intent' => 'CAPTURE',
            'purchase_units' => [
                [
                    'reference_id' => 'ticket-' . (string) $context_key,
                    'custom_id' => (string) $context_key,
                    'description' => $description,
                    'amount' => [
                        'currency_code' => $currency,
                        'value' => $amount,
                    ],
                ],
            ],
            'application_context' => [
                'return_url' => $success_url,
                'cancel_url' => $cancel_url,
                'user_action' => 'PAY_NOW',
            ],
        ];

        $response = wp_remote_post($auth['base'] . '/v2/checkout/orders', [
            'timeout' => 25,
            'headers' => [
                'Authorization' => 'Bearer ' . $auth['token'],
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ],
            'body' => wp_json_encode($payload),
        ]);
        if (is_wp_error($response)) {
            return new WP_Error('eep_ticket_paypal_order_failed', __('Could not create PayPal ticket checkout.', 'nexdirectory'));
        }

        $status = (int) wp_remote_retrieve_response_code($response);
        $body = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($body)) {
            return new WP_Error('eep_ticket_paypal_order_error', __('PayPal ticket checkout returned an unexpected response.', 'nexdirectory'));
        }

        $paypal_order_id = sanitize_text_field((string) ($body['id'] ?? ''));
        $approve_url = '';
        $links = is_array($body['links'] ?? null) ? (array) $body['links'] : [];
        foreach ($links as $link) {
            if (!is_array($link)) {
                continue;
            }
            if (sanitize_key((string) ($link['rel'] ?? '')) === 'approve') {
                $approve_url = esc_url_raw((string) ($link['href'] ?? ''));
                break;
            }
        }

        if ($paypal_order_id !== '') {
            $context['external_order_id'] = $paypal_order_id;
            $this->set_ticket_checkout_context($context_key, $context, 2 * HOUR_IN_SECONDS);
        }

        if ($approve_url === '') {
            return new WP_Error('eep_ticket_paypal_approve_missing', __('PayPal approval link was missing for ticket checkout.', 'nexdirectory'));
        }

        return $approve_url;
    }


    public function handle_ticket_reference_lookup() {
        if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
            return;
        }
        if (empty($_GET['eep_ticket_ref'])) {
            return;
        }

        $ticket_uid = sanitize_text_field((string) wp_unslash($_GET['eep_ticket_ref']));
        $fallback_url = eep_remove_query_arg('eep_ticket_ref', $this->current_url());
        if ($fallback_url === '') {
            $fallback_url = $this->get_ticket_return_url();
        }
        $brand_name = method_exists($this, 'get_white_label_brand_name')
            ? sanitize_text_field((string) $this->get_white_label_brand_name())
            : '';
        if ($brand_name === '') {
            $brand_name = sanitize_text_field((string) get_option('blogname', get_bloginfo('name')));
        }
        if ($brand_name === '') {
            $brand_name = __('nexdirectory', 'nexdirectory');
        }

        $accent_color = sanitize_hex_color((string) get_option('eep_style_accent_color', '#FF5E14')) ?: '#FF5E14';
        $accent_hover_color = sanitize_hex_color((string) get_option('eep_style_accent_hover_color', '#E04F0D')) ?: '#E04F0D';
        $on_primary_color = sanitize_hex_color((string) get_option('eep_style_on_primary_color', '#FFFFFF')) ?: '#FFFFFF';
        $logo_url = esc_url_raw((string) get_option('eep_frontend_sticky_nav_logo_url', ''));

        $verification_context = [
            'brand_name' => $brand_name,
            'brand_logo_url' => $logo_url,
            'accent_color' => $accent_color,
            'accent_hover_color' => $accent_hover_color,
            'on_primary_color' => $on_primary_color,
            'fallback_url' => esc_url_raw((string) $fallback_url),
            'support_email' => sanitize_email((string) get_option('admin_email', '')),
            'scanned_at_label' => wp_date(
                sprintf('%s %s', get_option('date_format', 'M j, Y'), get_option('time_format', 'g:i a')),
                current_time('timestamp')
            ),
            'ticket_uid' => $ticket_uid,
            'verification_state' => 'error',
            'verification_state_label' => __('Not Verified', 'nexdirectory'),
            'verification_title' => __('Ticket could not be verified', 'nexdirectory'),
            'verification_message' => __('Please confirm this QR code and try scanning again.', 'nexdirectory'),
            'status_label' => __('Unknown', 'nexdirectory'),
            'amount_label' => __('N/A', 'nexdirectory'),
            'purchased_label' => __('N/A', 'nexdirectory'),
            'event_title' => __('Event not found', 'nexdirectory'),
            'event_url' => esc_url_raw((string) $fallback_url),
            'event_date_label' => __('N/A', 'nexdirectory'),
            'event_location_label' => __('N/A', 'nexdirectory'),
            'attendee_label' => __('N/A', 'nexdirectory'),
            'payment_method_label' => __('N/A', 'nexdirectory'),
            'gateway_label' => __('N/A', 'nexdirectory'),
            'rsvp_status_label' => __('N/A', 'nexdirectory'),
            'external_order_id' => '',
            'external_payment_id' => '',
            'qr_image_url' => '',
            'qr_payload_pretty' => '',
            'ticket_order_id' => 0,
            'show_sensitive_details' => false,
        ];

        if ($ticket_uid === '') {
            $verification_context['verification_message'] = __('Ticket code is missing or invalid.', 'nexdirectory');
            if ($this->render_ticket_verification_page($verification_context)) {
                exit;
            }
            $this->redirect_with_message($fallback_url, __('Ticket code is missing or invalid.', 'nexdirectory'), 'error');
        }

        if (!$this->ticket_orders_table_exists()) {
            $verification_context['verification_title'] = __('Ticket verification unavailable', 'nexdirectory');
            $verification_context['verification_message'] = __('Ticket verification is unavailable right now. Please try again shortly.', 'nexdirectory');
            if ($this->render_ticket_verification_page($verification_context)) {
                exit;
            }
            $this->redirect_with_message($fallback_url, __('Ticket verification is unavailable right now.', 'nexdirectory'), 'error');
        }

        global $wpdb;
        $table = $this->get_ticket_orders_table_name();
        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT id, ticket_uid, post_id, user_id, rsvp_status, gateway, gateway_label, payment_method, currency, ticket_price, ticket_total, payment_status, external_order_id, external_payment_id, qr_payload, qr_image_url, purchased_at, created_at, updated_at FROM {$table} WHERE ticket_uid = %s LIMIT 1",
                $ticket_uid
            ),
            ARRAY_A
        ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

        if (!is_array($row) || empty($row)) {
            $verification_context['verification_message'] = __('No ticket record was found for this QR code. Please contact support if needed.', 'nexdirectory');
            if ($this->render_ticket_verification_page($verification_context)) {
                exit;
            }
            $this->redirect_with_message($fallback_url, __('Ticket could not be verified. Please contact support.', 'nexdirectory'), 'error');
        }

        $post_id = absint((int) ($row['post_id'] ?? 0));
        $event_url = $post_id > 0 ? (string) get_permalink($post_id) : '';
        if ($event_url === '') {
            $event_url = $fallback_url;
        }
        $event_title = $post_id > 0 ? sanitize_text_field((string) get_the_title($post_id)) : '';
        if ($event_title === '') {
            $event_title = __('Event', 'nexdirectory');
        }

        $current_user_id = is_user_logged_in() ? absint((int) get_current_user_id()) : 0;
        $ticket_owner_id = absint((int) ($row['user_id'] ?? 0));
        $can_view_sensitive_details = false;
        if ($current_user_id > 0) {
            if ($ticket_owner_id > 0 && $ticket_owner_id === $current_user_id) {
                $can_view_sensitive_details = true;
            } elseif ($this->user_can_moderate_events($current_user_id)) {
                $can_view_sensitive_details = true;
            } elseif (
                $post_id > 0
                && ($this->user_can_edit_event($post_id, $current_user_id) || $this->can_manage_event_team($post_id, $current_user_id))
            ) {
                $can_view_sensitive_details = true;
            }
        }

        $payment_status = sanitize_key((string) ($row['payment_status'] ?? 'confirmed'));
        $status_map = [
            'confirmed' => __('Confirmed', 'nexdirectory'),
            'completed' => __('Completed', 'nexdirectory'),
            'paid' => __('Paid', 'nexdirectory'),
            'waitlist' => __('Waitlist', 'nexdirectory'),
            'refunded' => __('Refunded', 'nexdirectory'),
            'failed' => __('Failed', 'nexdirectory'),
            'cancelled' => __('Cancelled', 'nexdirectory'),
        ];
        $status_label = (string) ($status_map[$payment_status] ?? ($payment_status !== '' ? ucwords(str_replace('_', ' ', $payment_status)) : __('Confirmed', 'nexdirectory')));

        $verification_state = 'success';
        $verification_state_label = __('Verified', 'nexdirectory');
        $verification_title = __('Ticket verified', 'nexdirectory');
        $verification_message = __('This ticket is valid and has been matched to an active order.', 'nexdirectory');
        if (in_array($payment_status, ['waitlist'], true)) {
            $verification_state = 'warning';
            $verification_state_label = __('Attention', 'nexdirectory');
            $verification_title = __('Ticket requires attention', 'nexdirectory');
            $verification_message = __('This ticket is on the waitlist. Admission depends on organizer approval.', 'nexdirectory');
        } elseif (in_array($payment_status, ['failed', 'refunded', 'cancelled'], true)) {
            $verification_state = 'error';
            $verification_state_label = __('Not Valid', 'nexdirectory');
            $verification_title = __('Ticket not valid for entry', 'nexdirectory');
            $verification_message = __('This ticket is not currently eligible for entry. Please contact support if needed.', 'nexdirectory');
        }

        $ticket_total = round(max(0, (float) ($row['ticket_total'] ?? 0)), 2);
        $currency = strtoupper(sanitize_text_field((string) ($row['currency'] ?? '')));
        $amount_label = $ticket_total > 0 ? trim($currency . ' ' . number_format($ticket_total, 2, '.', '')) : __('Free', 'nexdirectory');
        if ($ticket_total > 0 && $currency === '') {
            $amount_label = number_format($ticket_total, 2, '.', '');
        }
        $purchased_label = $this->format_ticket_purchase_label((string) ($row['purchased_at'] ?? ''));
        if ($purchased_label === '') {
            $purchased_label = __('N/A', 'nexdirectory');
        }

        $event_date_label = __('N/A', 'nexdirectory');
        $start_date_raw = sanitize_text_field((string) get_post_meta($post_id, 'eep_start_date', true));
        $start_time_raw = sanitize_text_field((string) get_post_meta($post_id, 'eep_start_time', true));
        if ($start_date_raw !== '') {
            $start_timestamp = strtotime(trim($start_date_raw . ' ' . $start_time_raw));
            if ($start_timestamp !== false && $start_timestamp > 0) {
                $event_date_label = wp_date(
                    sprintf('%s %s', get_option('date_format', 'M j, Y'), $start_time_raw !== '' ? get_option('time_format', 'g:i a') : ''),
                    $start_timestamp
                );
            } else {
                $event_date_label = $start_date_raw . ($start_time_raw !== '' ? ' ' . $start_time_raw : '');
            }
        }

        $event_location_label = trim(implode(', ', array_filter([
            sanitize_text_field((string) get_post_meta($post_id, 'eep_venue', true)),
            sanitize_text_field((string) get_post_meta($post_id, 'eep_city', true)),
            sanitize_text_field((string) get_post_meta($post_id, 'eep_country', true)),
        ])));
        if ($event_location_label === '') {
            $event_location_label = sanitize_text_field((string) get_post_meta($post_id, 'eep_address', true));
        }
        if ($event_location_label === '') {
            $event_location_label = __('N/A', 'nexdirectory');
        }

        $user_id = absint((int) ($row['user_id'] ?? 0));
        $attendee_email = '';
        $attendee_name = '';
        if ($user_id > 0) {
            $user = get_userdata($user_id);
            if ($user instanceof WP_User) {
                $attendee_email = sanitize_email((string) ($user->user_email ?? ''));
                $attendee_name = sanitize_text_field((string) $user->display_name);
            }
        }
        if ($attendee_name === '' && $attendee_email !== '') {
            $attendee_name = $attendee_email;
        }
        if ($attendee_name === '') {
            $attendee_name = __('Guest', 'nexdirectory');
        }
        if ($attendee_email !== '') {
            $parts = explode('@', $attendee_email);
            $local = (string) ($parts[0] ?? '');
            $domain = (string) ($parts[1] ?? '');
            if ($local !== '' && $domain !== '') {
                $masked_local = strlen($local) <= 2 ? substr($local, 0, 1) . '*' : substr($local, 0, 2) . str_repeat('*', max(1, strlen($local) - 2));
                $attendee_name = trim($attendee_name . ' (' . $masked_local . '@' . $domain . ')');
            }
        }

        $gateway_label = sanitize_text_field((string) ($row['gateway_label'] ?? ''));
        if ($gateway_label === '') {
            $gateway_label = $this->get_ticket_gateway_label((string) ($row['gateway'] ?? ''));
        }
        if ($gateway_label === '') {
            $gateway_label = __('N/A', 'nexdirectory');
        }
        $payment_method_label = sanitize_text_field((string) ($row['payment_method'] ?? ''));
        if ($payment_method_label === '') {
            $payment_method_label = $gateway_label;
        }
        if ($payment_method_label === '') {
            $payment_method_label = __('N/A', 'nexdirectory');
        }

        $rsvp_status = sanitize_key((string) ($row['rsvp_status'] ?? 'yes'));
        $rsvp_status_map = [
            'yes' => __('Attending', 'nexdirectory'),
            'waitlist' => __('Waitlist', 'nexdirectory'),
            'no' => __('Not attending', 'nexdirectory'),
        ];
        $rsvp_status_label = (string) ($rsvp_status_map[$rsvp_status] ?? ($rsvp_status !== '' ? ucwords(str_replace('_', ' ', $rsvp_status)) : __('N/A', 'nexdirectory')));

        $qr_payload_raw = trim((string) ($row['qr_payload'] ?? ''));
        $qr_payload = $qr_payload_raw !== '' ? json_decode($qr_payload_raw, true) : null;
        $qr_payload_pretty = '';
        if (is_array($qr_payload) && !empty($qr_payload)) {
            $qr_payload_pretty = (string) wp_json_encode($qr_payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        }

        if (!$can_view_sensitive_details) {
            $amount_label = __('Hidden', 'nexdirectory');
            $purchased_label = __('Hidden', 'nexdirectory');
            $attendee_name = __('Private attendee', 'nexdirectory');
            $payment_method_label = __('Hidden', 'nexdirectory');
            $gateway_label = __('Hidden', 'nexdirectory');
            $rsvp_status_label = __('Hidden', 'nexdirectory');
        }

        $verification_context = array_merge($verification_context, [
            'ticket_order_id' => absint((int) ($row['id'] ?? 0)),
            'ticket_uid' => sanitize_text_field((string) ($row['ticket_uid'] ?? $ticket_uid)),
            'verification_state' => $verification_state,
            'verification_state_label' => $verification_state_label,
            'verification_title' => $verification_title,
            'verification_message' => $verification_message,
            'status_label' => $status_label,
            'amount_label' => $amount_label,
            'purchased_label' => $purchased_label,
            'event_title' => $event_title,
            'event_url' => esc_url_raw((string) $event_url),
            'event_date_label' => $event_date_label,
            'event_location_label' => $event_location_label,
            'attendee_label' => $attendee_name,
            'payment_method_label' => $payment_method_label,
            'gateway_label' => $gateway_label,
            'rsvp_status_label' => $rsvp_status_label,
            'external_order_id' => $can_view_sensitive_details ? sanitize_text_field((string) ($row['external_order_id'] ?? '')) : '',
            'external_payment_id' => $can_view_sensitive_details ? sanitize_text_field((string) ($row['external_payment_id'] ?? '')) : '',
            'qr_image_url' => $can_view_sensitive_details ? esc_url_raw((string) ($row['qr_image_url'] ?? '')) : '',
            'qr_payload_pretty' => $can_view_sensitive_details ? $qr_payload_pretty : '',
            'show_sensitive_details' => $can_view_sensitive_details,
        ]);

        if ($this->render_ticket_verification_page($verification_context)) {
            exit;
        }

        $notice_type = in_array($payment_status, ['failed', 'refunded', 'cancelled'], true) ? 'error' : 'success';
        $message = sprintf(
            __('Ticket %1$s verified: %2$s. Amount: %3$s. Purchased: %4$s.', 'nexdirectory'),
            sanitize_text_field((string) ($row['ticket_uid'] ?? $ticket_uid)),
            $status_label,
            $amount_label,
            $purchased_label
        );
        if ($notice_type === 'error') {
            $message = sprintf(
                __('Ticket %1$s status: %2$s. Please contact support if this is unexpected.', 'nexdirectory'),
                sanitize_text_field((string) ($row['ticket_uid'] ?? $ticket_uid)),
                $status_label
            );
        }
        $this->redirect_with_message($event_url, $message, $notice_type);
    }

    private function render_ticket_verification_page(array $context = []) {
        $template_path = plugin_dir_path(EEP_PLUGIN_FILE) . 'templates/frontend/ticket-verification.php';
        if (!is_readable($template_path)) {
            return false;
        }

        nocache_headers();
        if (!headers_sent()) {
            header('X-Robots-Tag: noindex, nofollow', true);
        }

        extract($context, EXTR_SKIP);
        ob_start();
        include $template_path;
        $markup = (string) ob_get_clean();
        if ($markup === '') {
            return false;
        }

        echo $markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        return true;
    }


    public function handle_ticket_payment_return() {
        if ($this->handle_guest_ticket_verification_return()) {
            return;
        }

        if (empty($_GET['eep_ticket_gateway']) || empty($_GET['eep_ticket_ctx'])) {
            return;
        }

        $gateway = sanitize_key((string) wp_unslash($_GET['eep_ticket_gateway']));
        if (!in_array($gateway, ['stripe', 'paypal'], true)) {
            return;
        }

        $context_key = $this->get_ticket_checkout_context_key(wp_unslash($_GET['eep_ticket_ctx']));
        $context = $this->get_ticket_checkout_context($context_key);
        if ($context_key === '' || empty($context)) {
            $this->redirect_with_message($this->get_ticket_return_url(), __('Ticket checkout session expired. Please try again.', 'nexdirectory'), 'error');
        }

        $post_id = absint((int) ($context['post_id'] ?? 0));
        $return_url = $this->get_ticket_return_url($post_id);
        $retry_url = $this->get_ticket_checkout_resume_url($return_url, $context);
        $retry_promo_code = strtoupper(sanitize_text_field((string) ($context['promo_code'] ?? '')));
        if ($retry_promo_code !== '') {
            $retry_url = eep_add_query_arg(['eep_ticket_promo' => $retry_promo_code], $retry_url);
        }
        $is_guest_checkout = (string) ($context['guest_checkout'] ?? '0') === '1';
        $context_user_id = absint((int) ($context['user_id'] ?? 0));
        if ($is_guest_checkout) {
            $context_guest_token = sanitize_text_field((string) ($context['guest_callback_token'] ?? ''));
            $request_guest_token = sanitize_text_field((string) wp_unslash($_GET['eep_ticket_token'] ?? ''));
            if (
                $context_user_id <= 0
                || $context_guest_token === ''
                || $request_guest_token === ''
                || !hash_equals($context_guest_token, $request_guest_token)
            ) {
                $this->redirect_with_message($return_url, __('You do not have permission to process this ticket checkout.', 'nexdirectory'), 'error');
            }
        } else {
            if (!is_user_logged_in()) {
                $this->safe_redirect_or_fallback(wp_login_url($this->current_url()));
            }
            $user_id = get_current_user_id();
            if ($context_user_id <= 0 || ($context_user_id !== $user_id && !current_user_can('manage_options'))) {
                $this->redirect_with_message($retry_url, __('You do not have permission to process this ticket checkout.', 'nexdirectory'), 'error');
            }
        }

        if (!empty($_GET['eep_ticket_cancel'])) {
            $context['created_at'] = time();
            $this->set_ticket_checkout_context($context_key, $context, 2 * HOUR_IN_SECONDS);
            $this->redirect_with_message($retry_url, __('Checkout cancelled. You can retry payment below.', 'nexdirectory'), 'warning');
        }

        $capture = null;
        if ($gateway === 'stripe') {
            $session_id = sanitize_text_field((string) wp_unslash($_GET['session_id'] ?? ''));
            if ($session_id === '') {
                $this->redirect_with_message($retry_url, __('Stripe ticket session is missing. Please retry checkout.', 'nexdirectory'), 'error');
            }
            $capture = $this->verify_and_capture_stripe_ticket_checkout($context, $session_id);
        } else {
            $paypal_token = sanitize_text_field((string) wp_unslash($_GET['token'] ?? ''));
            if ($paypal_token === '') {
                $this->redirect_with_message($retry_url, __('PayPal token is missing. Please retry checkout.', 'nexdirectory'), 'error');
            }
            $capture = $this->verify_and_capture_paypal_ticket_order($context, $paypal_token, $context_key);
        }

        if (is_wp_error($capture)) {
            $this->append_runtime_log('warning', 'Ticket payment verification failed', [
                'context' => (string) $context_key,
                'post_id' => $post_id,
                'user_id' => $context_user_id,
                'gateway' => $gateway,
                'error' => (string) $capture->get_error_message(),
            ]);
            $context['created_at'] = time();
            $this->set_ticket_checkout_context($context_key, $context, 2 * HOUR_IN_SECONDS);
            $this->redirect_with_message($retry_url, (string) $capture->get_error_message(), 'error');
        }

        $finalize = $this->finalize_ticket_registration($context, is_array($capture) ? $capture : []);
        if (is_wp_error($finalize)) {
            $context['created_at'] = time();
            $this->set_ticket_checkout_context($context_key, $context, 2 * HOUR_IN_SECONDS);
            $this->redirect_with_message($retry_url, (string) $finalize->get_error_message(), 'error');
        }

        $this->delete_ticket_checkout_context($context_key);
        $message = is_array($finalize) && !empty($finalize['message'])
            ? sanitize_text_field((string) $finalize['message'])
            : __('Ticket confirmed. A confirmation email has been sent.', 'nexdirectory');

        $success_args = [
            'eep_ticket_success_modal' => '1',
        ];
        if (!empty($finalize['ticket_uid'])) {
            $success_args['eep_ticket_uid'] = sanitize_text_field((string) $finalize['ticket_uid']);
        }
        $final_ticket_order = is_array($finalize['ticket_order'] ?? null) ? (array) $finalize['ticket_order'] : [];
        $verification_url = $this->get_ticket_order_verification_url($final_ticket_order);
        if ($verification_url !== '') {
            $success_args['eep_ticket_verify'] = esc_url_raw((string) $verification_url);
        }
        $promo_code = strtoupper(sanitize_text_field((string) ($context['promo_code'] ?? '')));
        $promo_discount = round(max(0, (float) ($context['promo_discount'] ?? 0)), 2);
        if ($promo_code !== '' && $promo_discount > 0) {
            $success_args['eep_ticket_promo'] = $promo_code;
            $success_args['eep_ticket_discount'] = number_format($promo_discount, 2, '.', '');
        }

        $success_url = eep_add_query_arg($success_args, $return_url);
        $this->redirect_with_message($success_url, $message, 'success');
    }


    private function verify_and_capture_stripe_ticket_checkout(array $context, $session_id) {
        $secret_key = $this->get_active_ticket_stripe_secret_key();
        if ($secret_key === '') {
            return new WP_Error('eep_ticket_stripe_missing_key', __('Stripe secret key is not configured for ticket checkout.', 'nexdirectory'));
        }

        $session_id = sanitize_text_field((string) $session_id);
        $url = 'https://api.stripe.com/v1/checkout/sessions/' . rawurlencode($session_id) . '?expand[]=payment_intent';
        $response = wp_remote_get($url, [
            'timeout' => 25,
            'headers' => [
                'Authorization' => 'Bearer ' . $secret_key,
                'Accept' => 'application/json',
            ],
        ]);
        if (is_wp_error($response)) {
            return new WP_Error('eep_ticket_stripe_verify_failed', __('Could not verify Stripe ticket payment.', 'nexdirectory'));
        }

        $status = (int) wp_remote_retrieve_response_code($response);
        $payload = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($payload)) {
            return new WP_Error('eep_ticket_stripe_verify_error', __('Stripe ticket payment verification failed.', 'nexdirectory'));
        }

        $payment_status = sanitize_key((string) ($payload['payment_status'] ?? ''));
        $session_status = sanitize_key((string) ($payload['status'] ?? ''));
        if ($payment_status !== 'paid' || $session_status !== 'complete') {
            return new WP_Error('eep_ticket_stripe_unpaid', __('Stripe payment is not marked as paid yet.', 'nexdirectory'));
        }

        $metadata = is_array($payload['metadata'] ?? null) ? (array) $payload['metadata'] : [];
        $expected_context_key = sanitize_text_field((string) ($context['context_key'] ?? ''));
        if (!empty($metadata['context_key']) && !hash_equals($expected_context_key, sanitize_text_field((string) $metadata['context_key']))) {
            return new WP_Error('eep_ticket_stripe_mismatch', __('Stripe ticket verification mismatch.', 'nexdirectory'));
        }
        if (!empty($metadata['post_id']) && absint((int) $metadata['post_id']) !== absint((int) ($context['post_id'] ?? 0))) {
            return new WP_Error('eep_ticket_stripe_post_mismatch', __('Stripe ticket listing mismatch.', 'nexdirectory'));
        }
        if (!empty($metadata['user_id']) && absint((int) $metadata['user_id']) !== absint((int) ($context['user_id'] ?? 0))) {
            return new WP_Error('eep_ticket_stripe_user_mismatch', __('Stripe ticket user mismatch.', 'nexdirectory'));
        }

        $expected_amount_cents = (int) round(max(0, (float) ($context['amount'] ?? 0)) * 100);
        $amount_total = absint((int) ($payload['amount_total'] ?? 0));
        if ($expected_amount_cents > 0 && $amount_total > 0 && $amount_total !== $expected_amount_cents) {
            return new WP_Error('eep_ticket_stripe_amount_mismatch', __('Stripe ticket payment amount mismatch.', 'nexdirectory'));
        }

        $payment_intent = $payload['payment_intent'] ?? [];
        if (is_string($payment_intent)) {
            $payment_intent = ['id' => $payment_intent];
        }
        if (!is_array($payment_intent)) {
            $payment_intent = [];
        }

        $payment_method_ref = sanitize_text_field((string) ($payment_intent['payment_method'] ?? ''));
        $payment_method_label = '';
        $payment_intent_details = is_array($payment_intent['payment_method_details'] ?? null) ? (array) $payment_intent['payment_method_details'] : [];
        $payment_method_type = sanitize_key((string) ($payment_intent_details['type'] ?? ''));
        if ($payment_method_type === '') {
            $payment_types = is_array($payload['payment_method_types'] ?? null) ? (array) $payload['payment_method_types'] : [];
            if (!empty($payment_types[0])) {
                $payment_method_type = sanitize_key((string) $payment_types[0]);
            }
        }
        if ($payment_method_type !== '') {
            $payment_method_label = ucwords(str_replace('_', ' ', $payment_method_type));
            if ($payment_method_type === 'card') {
                $card_data = is_array($payment_intent_details['card'] ?? null) ? (array) $payment_intent_details['card'] : [];
                $card_brand = strtoupper(sanitize_text_field((string) ($card_data['brand'] ?? '')));
                $card_last4 = preg_replace('/\D+/', '', (string) ($card_data['last4'] ?? ''));
                if ($card_brand !== '' && $card_last4 !== '') {
                    $payment_method_label = sprintf('%1$s %2$s', $card_brand, '•••• ' . substr($card_last4, -4));
                } elseif ($card_brand !== '') {
                    $payment_method_label = $card_brand;
                }
            }
        }
        if ($payment_method_label === '') {
            $payment_method_label = __('Stripe Checkout', 'nexdirectory');
        }

        return [
            'gateway' => 'stripe',
            'external_order_id' => sanitize_text_field((string) ($payload['id'] ?? $session_id)),
            'external_payment_id' => sanitize_text_field((string) ($payment_intent['id'] ?? '')),
            'customer_ref' => sanitize_text_field((string) ($payment_intent['customer'] ?? ($payload['customer'] ?? ''))),
            'payment_method_ref' => $payment_method_ref,
            'payment_method_label' => $payment_method_label,
            'raw' => $payload,
        ];
    }


    private function verify_and_capture_paypal_ticket_order(array $context, $paypal_token, $context_key) {
        $auth = $this->get_ticket_paypal_access_token();
        if (is_wp_error($auth)) {
            return $auth;
        }

        $paypal_token = sanitize_text_field((string) $paypal_token);
        $capture_url = $auth['base'] . '/v2/checkout/orders/' . rawurlencode($paypal_token) . '/capture';
        $response = wp_remote_post($capture_url, [
            'timeout' => 30,
            'headers' => [
                'Authorization' => 'Bearer ' . $auth['token'],
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ],
            'body' => '{}',
        ]);
        if (is_wp_error($response)) {
            return new WP_Error('eep_ticket_paypal_capture_failed', __('Could not capture PayPal ticket payment.', 'nexdirectory'));
        }

        $status = (int) wp_remote_retrieve_response_code($response);
        $payload = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($payload)) {
            return new WP_Error('eep_ticket_paypal_capture_error', __('PayPal ticket payment capture failed.', 'nexdirectory'));
        }

        $order_status = sanitize_key((string) ($payload['status'] ?? ''));
        if (!in_array($order_status, ['completed', 'approved'], true)) {
            return new WP_Error('eep_ticket_paypal_unpaid', __('PayPal ticket payment is not completed.', 'nexdirectory'));
        }

        $custom_id = '';
        $payment_id = '';
        $customer_ref = '';
        $captured_amount_value = '';
        $payment_method_ref = '';
        $payment_method_label = '';
        if (!empty($payload['purchase_units']) && is_array($payload['purchase_units'])) {
            $unit = $payload['purchase_units'][0] ?? [];
            if (is_array($unit)) {
                $custom_id = sanitize_text_field((string) ($unit['custom_id'] ?? ''));
                if (!empty($unit['payments']['captures'][0]['id'])) {
                    $payment_id = sanitize_text_field((string) $unit['payments']['captures'][0]['id']);
                }
                if (!empty($unit['payments']['captures'][0]['amount']['value'])) {
                    $captured_amount_value = sanitize_text_field((string) $unit['payments']['captures'][0]['amount']['value']);
                }
                if (!empty($unit['payments']['captures'][0]['payment_source'])) {
                    $payment_method_ref = sanitize_text_field((string) $unit['payments']['captures'][0]['payment_source']);
                }
            }
        }
        if ($custom_id === '' || !hash_equals((string) $context_key, $custom_id)) {
            return new WP_Error('eep_ticket_paypal_mismatch', __('PayPal ticket verification mismatch.', 'nexdirectory'));
        }
        if (!empty($payload['payer']['payer_id'])) {
            $customer_ref = sanitize_text_field((string) $payload['payer']['payer_id']);
        }

        $expected_amount = round(max(0, (float) ($context['amount'] ?? 0)), 2);
        if ($captured_amount_value !== '' && abs(((float) $captured_amount_value) - $expected_amount) > 0.01) {
            return new WP_Error('eep_ticket_paypal_amount_mismatch', __('PayPal ticket payment amount mismatch.', 'nexdirectory'));
        }

        $payment_source = is_array($payload['payment_source'] ?? null) ? (array) $payload['payment_source'] : [];
        if (!empty($payment_source)) {
            $source_type = '';
            foreach ($payment_source as $source_key => $_unused_source_value) {
                $source_type = sanitize_key((string) $source_key);
                break;
            }
            if ($source_type === 'card') {
                $card_data = is_array($payment_source['card'] ?? null) ? (array) $payment_source['card'] : [];
                $brand = strtoupper(sanitize_text_field((string) ($card_data['brand'] ?? '')));
                $last_digits = preg_replace('/\D+/', '', (string) ($card_data['last_digits'] ?? ''));
                if ($brand !== '' && $last_digits !== '') {
                    $payment_method_label = sprintf('%1$s %2$s', $brand, '•••• ' . substr($last_digits, -4));
                } elseif ($brand !== '') {
                    $payment_method_label = $brand;
                }
            } elseif ($source_type === 'paypal') {
                $paypal_data = is_array($payment_source['paypal'] ?? null) ? (array) $payment_source['paypal'] : [];
                $email = sanitize_email((string) ($paypal_data['email_address'] ?? ''));
                $payment_method_label = $email !== '' ? sprintf(__('PayPal (%s)', 'nexdirectory'), $email) : 'PayPal';
            } elseif ($source_type !== '') {
                $payment_method_label = ucwords(str_replace('_', ' ', $source_type));
            }
        }
        if ($payment_method_label === '') {
            $payment_method_label = 'PayPal';
        }
        if ($payment_method_ref === '') {
            $payment_method_ref = sanitize_text_field((string) ($payload['payer']['payer_id'] ?? ''));
        }

        return [
            'gateway' => 'paypal',
            'external_order_id' => sanitize_text_field((string) ($payload['id'] ?? $paypal_token)),
            'external_payment_id' => $payment_id,
            'customer_ref' => $customer_ref,
            'payment_method_ref' => $payment_method_ref,
            'payment_method_label' => $payment_method_label,
            'raw' => $payload,
        ];
    }


    private function finalize_ticket_registration(array $context, array $capture = []) {
        $post_id = absint((int) ($context['post_id'] ?? 0));
        $user_id = absint((int) ($context['user_id'] ?? 0));
        if ($post_id <= 0 || get_post_type($post_id) !== self::POST_TYPE) {
            return new WP_Error('eep_ticket_listing_invalid', __('Ticket listing is invalid.', 'nexdirectory'));
        }
        if ((string) get_post_status($post_id) !== 'publish') {
            return new WP_Error('eep_ticket_listing_unavailable', __('This listing is not currently available for tickets.', 'nexdirectory'));
        }
        $user = get_user_by('id', $user_id);
        if (!$user instanceof WP_User) {
            return new WP_Error('eep_ticket_user_invalid', __('Ticket purchaser account is invalid.', 'nexdirectory'));
        }

        $status_result = $this->ensure_ticket_attendee_status($post_id, $user_id);
        if (is_wp_error($status_result)) {
            return $status_result;
        }

        $status = sanitize_key((string) $status_result);
        if (!in_array($status, ['yes', 'waitlist'], true)) {
            $status = 'yes';
        }

        $amount = round(max(0, (float) ($context['amount'] ?? 0)), 2);
        $gateway = sanitize_key((string) ($capture['gateway'] ?? ($context['gateway'] ?? '')));

        if (method_exists($this, 'record_analytics_event')) {
            $this->record_analytics_event($post_id, 'ticket_checkout', [
                'context' => 'ticketing',
                'meta_key' => 'gateway',
                'meta_value' => $gateway,
                'value' => $amount,
                'owner_id' => (int) get_post_field('post_author', $post_id),
                'user_id' => $user_id,
            ]);
        }

        $ticket_order = $this->create_ticket_order_record($context, $capture, $status);
        if (empty($ticket_order)) {
            $this->append_runtime_log('warning', 'Ticket order record could not be created', [
                'post_id' => (string) $post_id,
                'user_id' => (string) $user_id,
                'gateway' => $gateway,
            ]);
            return new WP_Error('eep_ticket_record_failed', __('Ticket checkout was received but the ticket record could not be saved. Please contact support.', 'nexdirectory'));
        }

        if (method_exists($this, 'queue_admin_ticket_purchase_notice')) {
            $this->queue_admin_ticket_purchase_notice([
                'event_id' => $post_id,
                'event_title' => (string) get_the_title($post_id),
                'buyer_name' => sanitize_text_field((string) ($user->display_name ?: $user->user_login)),
                'buyer_email' => sanitize_email((string) ($user->user_email ?? ($context['buyer_email'] ?? ''))),
                'status' => $status,
                'ticket_uid' => sanitize_text_field((string) ($ticket_order['ticket_uid'] ?? '')),
                'order_id' => absint((int) ($ticket_order['id'] ?? 0)),
                'currency' => sanitize_text_field((string) ($ticket_order['currency'] ?? ($context['currency'] ?? ''))),
                'amount' => round(max(0, (float) ($ticket_order['ticket_total'] ?? $amount)), 2),
                'purchased_at' => sanitize_text_field((string) ($ticket_order['purchased_at'] ?? current_time('mysql', true))),
            ]);
        }

        $confirmation_sent = false;
        if ((string) get_option('eep_ticket_send_confirmation_email', '1') === '1') {
            $confirmation_sent = $this->send_ticket_confirmation_email($post_id, $user, $context, $capture, $status, $ticket_order);
        }
        if (!empty($ticket_order['id'])) {
            $this->mark_ticket_order_email_status(absint((int) $ticket_order['id']), $confirmation_sent);
        }

        $message = $status === 'waitlist'
            ? __('Ticket request received. You are currently on the waitlist.', 'nexdirectory')
            : __('Ticket confirmed.', 'nexdirectory');
        if ($confirmation_sent && $status !== 'waitlist') {
            $message = __('Ticket confirmed. Check your email for confirmation.', 'nexdirectory');
        }

        return [
            'status' => $status,
            'message' => $message,
            'confirmation_sent' => $confirmation_sent,
            'ticket_uid' => sanitize_text_field((string) ($ticket_order['ticket_uid'] ?? '')),
            'ticket_order_id' => absint((int) ($ticket_order['id'] ?? 0)),
            'ticket_order' => $ticket_order,
        ];
    }


    private function send_ticket_confirmation_email($post_id, WP_User $user, array $context = [], array $capture = [], $status = 'yes', array $ticket_order = []) {
        $post_id = absint((int) $post_id);
        if ($post_id <= 0 || !is_email((string) $user->user_email)) {
            return false;
        }

        $status = sanitize_key((string) $status);
        $status_label = $status === 'waitlist' ? __('Waitlist', 'nexdirectory') : __('Confirmed', 'nexdirectory');
        $currency = strtoupper(sanitize_text_field((string) ($ticket_order['currency'] ?? ($context['currency'] ?? $this->get_ticket_currency()))));
        $amount = round(max(0, (float) ($ticket_order['ticket_total'] ?? ($context['amount'] ?? 0))), 2);
        $gateway = sanitize_key((string) ($capture['gateway'] ?? ($context['gateway'] ?? '')));
        if ($gateway === '') {
            $gateway = sanitize_key((string) ($ticket_order['gateway'] ?? ''));
        }
        $gateway_label = sanitize_text_field((string) ($ticket_order['gateway_label'] ?? $this->get_ticket_gateway_label($gateway)));
        $payment_method = sanitize_text_field((string) ($ticket_order['payment_method'] ?? $this->get_ticket_payment_method_label($capture, $gateway)));
        $ticket_uid = sanitize_text_field((string) ($ticket_order['ticket_uid'] ?? ''));
        $ticket_verify_url = $this->get_ticket_order_verification_url($ticket_order);
        $ticket_qr_image_url = esc_url_raw((string) ($ticket_order['qr_image_url'] ?? ''));
        $ticket_purchase_date = $this->format_ticket_purchase_label((string) ($ticket_order['purchased_at'] ?? current_time('mysql', true)));
        $ticket_total_label = $amount > 0
            ? trim($currency . ' ' . number_format($amount, 2, '.', ''))
            : __('Free', 'nexdirectory');
        if ($amount > 0 && $currency === '') {
            $ticket_total_label = number_format($amount, 2, '.', '');
        }

        $subject_template = (string) get_option(
            'eep_email_subject_ticket_confirmation',
            $this->get_default_event_email_template_value('eep_email_subject_ticket_confirmation', __('Ticket confirmation: {event_title}', 'nexdirectory'))
        );
        $body_template = (string) get_option(
            'eep_email_body_ticket_confirmation',
            $this->get_default_event_email_template_value('eep_email_body_ticket_confirmation', '')
        );

        $event_url = (string) get_permalink($post_id);
        $event_title = (string) get_the_title($post_id);
        $extra_tokens = [
            '{ticket_price}' => number_format($amount, 2, '.', ''),
            '{ticket_currency}' => $currency,
            '{ticket_total}' => $ticket_total_label,
            '{ticket_gateway}' => $gateway_label,
            '{ticket_payment_method}' => $payment_method,
            '{ticket_status}' => $status_label,
            '{ticket_id}' => $ticket_uid,
            '{ticket_purchase_date}' => $ticket_purchase_date,
            '{ticket_verification_url}' => $ticket_verify_url,
            '{ticket_qr_image_url}' => $ticket_qr_image_url,
            '{attendee_name}' => sanitize_text_field((string) $user->display_name),
            '{attendee_email}' => sanitize_email((string) $user->user_email),
            '{site_name}' => sanitize_text_field((string) $this->get_white_label_brand_name()),
            '{event_url}' => $event_url,
        ];

        $subject = trim((string) $this->render_event_email_subject($subject_template, $post_id, $status_label, $extra_tokens));
        if ($subject === '') {
            $subject = sprintf(
                /* translators: %s: event title */
                __('Ticket confirmation: %s', 'nexdirectory'),
                $event_title
            );
        }

        $custom_body_html = $this->render_event_email_body($body_template, $post_id, $status_label, '', $extra_tokens);
        if ($custom_body_html === '') {
            $custom_body_html = $this->render_event_email_body(
                __("Hi {attendee_name},\n\nYour ticket status for \"{event_title}\" is {ticket_status}.\nTicket ID: {ticket_id}\nAmount: {ticket_total}\nPayment method: {ticket_payment_method}\nGateway: {ticket_gateway}\nPurchased: {ticket_purchase_date}\nDate: {event_date}\nView listing: {event_url}\nVerify ticket: {ticket_verification_url}\nQR image: {ticket_qr_image_url}\n\nThank you.", 'nexdirectory'),
                $post_id,
                $status_label,
                '',
                $extra_tokens
            );
        }
        if ($custom_body_html !== '') {
            $ticket_meta_html = '<hr style="margin:20px 0;border:none;border-top:1px solid #e5e7eb;">';
            $ticket_meta_html .= '<p><strong>' . esc_html__('Ticket ID:', 'nexdirectory') . '</strong> ' . esc_html($ticket_uid !== '' ? $ticket_uid : __('N/A', 'nexdirectory')) . '</p>';
            $ticket_meta_html .= '<p><strong>' . esc_html__('Payment Method:', 'nexdirectory') . '</strong> ' . esc_html($payment_method !== '' ? $payment_method : __('N/A', 'nexdirectory')) . '</p>';
            $ticket_meta_html .= '<p><strong>' . esc_html__('Total Paid:', 'nexdirectory') . '</strong> ' . esc_html($ticket_total_label) . '</p>';
            if ($ticket_purchase_date !== '') {
                $ticket_meta_html .= '<p><strong>' . esc_html__('Purchased:', 'nexdirectory') . '</strong> ' . esc_html($ticket_purchase_date) . '</p>';
            }
            if ($ticket_verify_url !== '') {
                $ticket_meta_html .= '<p><a href="' . esc_url($ticket_verify_url) . '" style="display:inline-block;padding:10px 14px;background:#111827;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;">' . esc_html__('Verify Ticket', 'nexdirectory') . '</a></p>';
            }
            if ($ticket_qr_image_url !== '') {
                $ticket_meta_html .= '<p><img src="' . esc_url($ticket_qr_image_url) . '" alt="' . esc_attr__('Ticket QR Code', 'nexdirectory') . '" width="220" height="220" style="display:block;max-width:220px;height:auto;border:1px solid #e5e7eb;border-radius:8px;"></p>';
            }
            $custom_body_html .= $ticket_meta_html;
        }

        $fallback_text = sprintf(
            "Hi %s,\n\nYour ticket status for \"%s\" is %s.\nTicket ID: %s\nAmount: %s\nPayment method: %s\nGateway: %s\nPurchased: %s\nDate: %s\nVerify ticket: %s\nQR image: %s\nListing: %s",
            (string) $user->display_name,
            $event_title,
            $status_label,
            $ticket_uid !== '' ? $ticket_uid : __('N/A', 'nexdirectory'),
            $ticket_total_label,
            $payment_method !== '' ? $payment_method : __('N/A', 'nexdirectory'),
            $gateway_label,
            $ticket_purchase_date !== '' ? $ticket_purchase_date : __('N/A', 'nexdirectory'),
            $this->format_event_date($post_id),
            $ticket_verify_url !== '' ? $ticket_verify_url : __('N/A', 'nexdirectory'),
            $ticket_qr_image_url !== '' ? $ticket_qr_image_url : __('N/A', 'nexdirectory'),
            $event_url
        );

        return (bool) $this->send_platform_email((string) $user->user_email, $subject, '', [
            'post_id' => $post_id,
            'event_title' => $event_title,
            'event_date' => $this->format_event_date($post_id),
            'event_url' => $event_url,
            'custom_body_html' => $custom_body_html,
            'fallback_text' => $fallback_text,
        ]);
    }


    private function get_active_stripe_secret_key() {
        $mode = $this->get_boost_gateway_mode();
        if ($mode === 'live') {
            return trim((string) get_option('eep_boost_stripe_live_secret_key', ''));
        }
        return trim((string) get_option('eep_boost_stripe_test_secret_key', ''));
    }


    private function get_active_ticket_stripe_secret_key() {
        $mode = $this->get_ticket_gateway_mode();
        if ($mode === 'live') {
            $key = trim((string) get_option('eep_ticket_stripe_live_secret_key', ''));
            if ($key === '') {
                $key = trim((string) get_option('eep_boost_stripe_live_secret_key', ''));
            }
            return $key;
        }
        $key = trim((string) get_option('eep_ticket_stripe_test_secret_key', ''));
        if ($key === '') {
            $key = trim((string) get_option('eep_boost_stripe_test_secret_key', ''));
        }
        return $key;
    }


    private function start_stripe_boost_checkout(array $order, $return_url) {
        $secret_key = $this->get_active_stripe_secret_key();
        if ($secret_key === '') {
            return new WP_Error('eep_boost_stripe_missing_key', __('Stripe secret key is missing for the selected mode.', 'nexdirectory'));
        }

        $order_id = absint((int) ($order['id'] ?? 0));
        $order_key = sanitize_text_field((string) ($order['order_key'] ?? ''));
        $post_id = absint((int) ($order['post_id'] ?? 0));
        $duration_days = absint((int) ($order['duration_days'] ?? 30));
        $currency = strtolower(sanitize_text_field((string) ($order['currency'] ?? $this->get_boost_currency())));
        $amount_cents = max(1, (int) round(((float) ($order['total_amount'] ?? 0)) * 100));
        $title = sanitize_text_field((string) get_the_title($post_id));
        if ($title === '') {
            $title = __('Listing', 'nexdirectory');
        }
        $success_url = eep_add_query_arg([
            'eep_boost_gateway' => 'stripe',
            'eep_boost_success' => '1',
            'eep_boost_order' => $order_id,
            'eep_boost_key' => rawurlencode($order_key),
            'session_id' => '{CHECKOUT_SESSION_ID}',
        ], $return_url);
        $cancel_url = eep_add_query_arg([
            'eep_boost_gateway' => 'stripe',
            'eep_boost_cancel' => '1',
            'eep_boost_order' => $order_id,
            'eep_boost_key' => rawurlencode($order_key),
        ], $return_url);

        $body = [
            'mode' => 'payment',
            'success_url' => $success_url,
            'cancel_url' => $cancel_url,
            'line_items[0][quantity]' => '1',
            'line_items[0][price_data][currency]' => $currency,
            'line_items[0][price_data][unit_amount]' => (string) $amount_cents,
            'line_items[0][price_data][product_data][name]' => sprintf(
                /* translators: 1: listing title, 2: days */
                __('%1$s Boost (%2$d days)', 'nexdirectory'),
                $title,
                $duration_days
            ),
            'metadata[order_id]' => (string) $order_id,
            'metadata[order_key]' => $order_key,
            'metadata[post_id]' => (string) $post_id,
            'payment_intent_data[metadata][order_id]' => (string) $order_id,
            'payment_intent_data[metadata][order_key]' => $order_key,
        ];
        if (!empty($order['auto_renew']) && $this->boost_auto_renew_enabled()) {
            $body['customer_creation'] = 'always';
            $body['payment_intent_data][setup_future_usage'] = 'off_session';
        }

        $response = wp_remote_post('https://api.stripe.com/v1/checkout/sessions', [
            'timeout' => 25,
            'headers' => [
                'Authorization' => 'Bearer ' . $secret_key,
                'Content-Type' => 'application/x-www-form-urlencoded',
            ],
            'body' => $body,
        ]);

        if (is_wp_error($response)) {
            return new WP_Error('eep_boost_stripe_request_failed', __('Stripe checkout request failed.', 'nexdirectory'));
        }
        $status = (int) wp_remote_retrieve_response_code($response);
        $payload = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($payload)) {
            $message = __('Stripe checkout could not be created.', 'nexdirectory');
            if (is_array($payload) && !empty($payload['error']['message'])) {
                $message = sanitize_text_field((string) $payload['error']['message']);
            }
            return new WP_Error('eep_boost_stripe_checkout_error', $message);
        }
        $session_id = sanitize_text_field((string) ($payload['id'] ?? ''));
        $url = esc_url_raw((string) ($payload['url'] ?? ''));
        $meta = [
            'checkout_session_id' => $session_id,
        ];
        $this->update_boost_order($order_id, [
            'external_order_id' => $session_id,
            'payment_meta' => wp_json_encode($meta),
        ]);
        if ($url === '') {
            return new WP_Error('eep_boost_stripe_missing_url', __('Stripe checkout URL is missing from API response.', 'nexdirectory'));
        }
        return $url;
    }


    private function get_paypal_api_credentials() {
        $mode = $this->get_boost_gateway_mode();
        if ($mode === 'live') {
            return [
                'client_id' => trim((string) get_option('eep_boost_paypal_live_client_id', '')),
                'secret' => trim((string) get_option('eep_boost_paypal_live_secret', '')),
                'base' => 'https://api-m.paypal.com',
            ];
        }
        return [
            'client_id' => trim((string) get_option('eep_boost_paypal_test_client_id', '')),
            'secret' => trim((string) get_option('eep_boost_paypal_test_secret', '')),
            'base' => 'https://api-m.sandbox.paypal.com',
        ];
    }


    private function get_ticket_paypal_api_credentials() {
        $mode = $this->get_ticket_gateway_mode();
        if ($mode === 'live') {
            $client_id = trim((string) get_option('eep_ticket_paypal_live_client_id', ''));
            $secret = trim((string) get_option('eep_ticket_paypal_live_secret', ''));
            if ($client_id === '') {
                $client_id = trim((string) get_option('eep_boost_paypal_live_client_id', ''));
            }
            if ($secret === '') {
                $secret = trim((string) get_option('eep_boost_paypal_live_secret', ''));
            }
            return [
                'client_id' => $client_id,
                'secret' => $secret,
                'base' => 'https://api-m.paypal.com',
            ];
        }
        $client_id = trim((string) get_option('eep_ticket_paypal_test_client_id', ''));
        $secret = trim((string) get_option('eep_ticket_paypal_test_secret', ''));
        if ($client_id === '') {
            $client_id = trim((string) get_option('eep_boost_paypal_test_client_id', ''));
        }
        if ($secret === '') {
            $secret = trim((string) get_option('eep_boost_paypal_test_secret', ''));
        }
        return [
            'client_id' => $client_id,
            'secret' => $secret,
            'base' => 'https://api-m.sandbox.paypal.com',
        ];
    }


    private function get_paypal_access_token() {
        $creds = $this->get_paypal_api_credentials();
        if ($creds['client_id'] === '' || $creds['secret'] === '') {
            return new WP_Error('eep_boost_paypal_missing', __('PayPal API credentials are missing for the selected mode.', 'nexdirectory'));
        }
        $response = wp_remote_post($creds['base'] . '/v1/oauth2/token', [
            'timeout' => 25,
            'headers' => [
                'Authorization' => 'Basic ' . base64_encode($creds['client_id'] . ':' . $creds['secret']), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
                'Content-Type' => 'application/x-www-form-urlencoded',
                'Accept' => 'application/json',
            ],
            'body' => [
                'grant_type' => 'client_credentials',
            ],
        ]);
        if (is_wp_error($response)) {
            return new WP_Error('eep_boost_paypal_token_error', __('Could not connect to PayPal.', 'nexdirectory'));
        }
        $status = (int) wp_remote_retrieve_response_code($response);
        $payload = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($payload) || empty($payload['access_token'])) {
            return new WP_Error('eep_boost_paypal_token_invalid', __('PayPal authentication failed.', 'nexdirectory'));
        }
        return [
            'token' => sanitize_text_field((string) $payload['access_token']),
            'base' => $creds['base'],
        ];
    }


    private function get_ticket_paypal_access_token() {
        $creds = $this->get_ticket_paypal_api_credentials();
        if ($creds['client_id'] === '' || $creds['secret'] === '') {
            return new WP_Error('eep_ticket_paypal_missing', __('PayPal API credentials are missing for ticket checkout.', 'nexdirectory'));
        }
        $response = wp_remote_post($creds['base'] . '/v1/oauth2/token', [
            'timeout' => 25,
            'headers' => [
                'Authorization' => 'Basic ' . base64_encode($creds['client_id'] . ':' . $creds['secret']), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
                'Content-Type' => 'application/x-www-form-urlencoded',
                'Accept' => 'application/json',
            ],
            'body' => [
                'grant_type' => 'client_credentials',
            ],
        ]);
        if (is_wp_error($response)) {
            return new WP_Error('eep_ticket_paypal_token_error', __('Could not connect to PayPal.', 'nexdirectory'));
        }
        $status = (int) wp_remote_retrieve_response_code($response);
        $payload = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($payload) || empty($payload['access_token'])) {
            return new WP_Error('eep_ticket_paypal_token_invalid', __('PayPal authentication failed.', 'nexdirectory'));
        }
        return [
            'token' => sanitize_text_field((string) $payload['access_token']),
            'base' => $creds['base'],
        ];
    }


    private function start_paypal_boost_checkout(array $order, $return_url) {
        $auth = $this->get_paypal_access_token();
        if (is_wp_error($auth)) {
            return $auth;
        }
        $order_id = absint((int) ($order['id'] ?? 0));
        $order_key = sanitize_text_field((string) ($order['order_key'] ?? ''));
        $post_id = absint((int) ($order['post_id'] ?? 0));
        $duration_days = absint((int) ($order['duration_days'] ?? 30));
        $currency = $this->get_boost_currency();
        $amount = $this->format_boost_order_amount_for_gateway((float) ($order['total_amount'] ?? 0));
        $title = sanitize_text_field((string) get_the_title($post_id));
        if ($title === '') {
            $title = __('Listing', 'nexdirectory');
        }
        $success_url = eep_add_query_arg([
            'eep_boost_gateway' => 'paypal',
            'eep_boost_success' => '1',
            'eep_boost_order' => $order_id,
            'eep_boost_key' => rawurlencode($order_key),
        ], $return_url);
        $cancel_url = eep_add_query_arg([
            'eep_boost_gateway' => 'paypal',
            'eep_boost_cancel' => '1',
            'eep_boost_order' => $order_id,
            'eep_boost_key' => rawurlencode($order_key),
        ], $return_url);

        $payload = [
            'intent' => 'CAPTURE',
            'purchase_units' => [
                [
                    'reference_id' => 'boost-' . $order_id,
                    'custom_id' => $order_key,
                    'description' => sprintf(
                        /* translators: 1: listing title, 2: days */
                        __('%1$s Boost (%2$d days)', 'nexdirectory'),
                        $title,
                        $duration_days
                    ),
                    'amount' => [
                        'currency_code' => $currency,
                        'value' => $amount,
                    ],
                ],
            ],
            'application_context' => [
                'return_url' => $success_url,
                'cancel_url' => $cancel_url,
                'user_action' => 'PAY_NOW',
            ],
        ];

        $response = wp_remote_post($auth['base'] . '/v2/checkout/orders', [
            'timeout' => 25,
            'headers' => [
                'Authorization' => 'Bearer ' . $auth['token'],
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ],
            'body' => wp_json_encode($payload),
        ]);
        if (is_wp_error($response)) {
            return new WP_Error('eep_boost_paypal_order_failed', __('Could not create PayPal checkout.', 'nexdirectory'));
        }
        $status = (int) wp_remote_retrieve_response_code($response);
        $body = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($body)) {
            return new WP_Error('eep_boost_paypal_order_error', __('PayPal checkout returned an unexpected response.', 'nexdirectory'));
        }

        $paypal_order_id = sanitize_text_field((string) ($body['id'] ?? ''));
        $approve_url = '';
        $links = is_array($body['links'] ?? null) ? (array) $body['links'] : [];
        foreach ($links as $link) {
            if (!is_array($link)) {
                continue;
            }
            if (sanitize_key((string) ($link['rel'] ?? '')) === 'approve') {
                $approve_url = esc_url_raw((string) ($link['href'] ?? ''));
                break;
            }
        }

        $this->update_boost_order($order_id, [
            'external_order_id' => $paypal_order_id,
            'payment_meta' => wp_json_encode([
                'paypal_order_id' => $paypal_order_id,
            ]),
        ]);

        if ($approve_url === '') {
            return new WP_Error('eep_boost_paypal_approve_missing', __('PayPal approval link was missing.', 'nexdirectory'));
        }
        return $approve_url;
    }


    public function handle_boost_payment_return() {
        if (empty($_GET['eep_boost_gateway']) || empty($_GET['eep_boost_order'])) {
            return;
        }

        $gateway = sanitize_key((string) wp_unslash($_GET['eep_boost_gateway']));
        if (!in_array($gateway, ['stripe', 'paypal'], true)) {
            return;
        }

        $order_id = absint((int) wp_unslash($_GET['eep_boost_order']));
        $order_key = sanitize_text_field((string) wp_unslash($_GET['eep_boost_key'] ?? ''));
        $order = $this->get_boost_order_by_id($order_id);
        if (!is_array($order) || empty($order['order_key']) || !hash_equals((string) $order['order_key'], $order_key)) {
            $this->redirect_with_message($this->get_boost_return_url(), __('Boost checkout could not be verified.', 'nexdirectory'), 'error');
        }

        if (!is_user_logged_in()) {
            $login_url = wp_login_url($this->current_url());
            $this->safe_redirect_or_fallback($login_url);
        }
        $user_id = get_current_user_id();
        if ((int) ($order['user_id'] ?? 0) !== $user_id && !current_user_can('manage_options')) {
            $this->redirect_with_message($this->get_boost_return_url(), __('You do not have permission to process this boost order.', 'nexdirectory'), 'error');
        }

        if (!empty($_GET['eep_boost_cancel'])) {
            $this->update_boost_order($order_id, ['order_status' => 'cancelled']);
            $this->redirect_with_message($this->get_boost_return_url(), __('Boost checkout cancelled.', 'nexdirectory'), 'error');
        }

        $current_status = sanitize_key((string) ($order['order_status'] ?? 'pending'));
        if (in_array($current_status, ['active', 'paid'], true)) {
            $this->redirect_with_message($this->get_boost_return_url(), __('Boost already activated for this listing.', 'nexdirectory'));
        }

        $capture = null;
        if ($gateway === 'stripe') {
            $session_id = sanitize_text_field((string) wp_unslash($_GET['session_id'] ?? ''));
            if ($session_id === '') {
                $this->redirect_with_message($this->get_boost_return_url(), __('Stripe session is missing.', 'nexdirectory'), 'error');
            }
            $capture = $this->verify_and_capture_stripe_checkout($order, $session_id);
        } else {
            $paypal_token = sanitize_text_field((string) wp_unslash($_GET['token'] ?? ''));
            if ($paypal_token === '') {
                $this->redirect_with_message($this->get_boost_return_url(), __('PayPal token is missing.', 'nexdirectory'), 'error');
            }
            $capture = $this->verify_and_capture_paypal_order($order, $paypal_token);
        }

        if (is_wp_error($capture)) {
            $this->update_boost_order($order_id, ['order_status' => 'failed']);
            $this->redirect_with_message($this->get_boost_return_url(), (string) $capture->get_error_message(), 'error');
        }

        $activation = $this->activate_boost_from_order($order_id, is_array($capture) ? $capture : []);
        if (is_wp_error($activation)) {
            $this->update_boost_order($order_id, ['order_status' => 'failed']);
            $this->redirect_with_message($this->get_boost_return_url(), (string) $activation->get_error_message(), 'error');
        }
        $this->redirect_with_message($this->get_boost_return_url(), __('Boost activated successfully.', 'nexdirectory'));
    }


    private function verify_and_capture_stripe_checkout(array $order, $session_id) {
        $secret_key = $this->get_active_stripe_secret_key();
        if ($secret_key === '') {
            return new WP_Error('eep_boost_stripe_missing_key', __('Stripe secret key is not configured.', 'nexdirectory'));
        }
        $session_id = sanitize_text_field((string) $session_id);
        $url = 'https://api.stripe.com/v1/checkout/sessions/' . rawurlencode($session_id) . '?expand[]=payment_intent';
        $response = wp_remote_get($url, [
            'timeout' => 25,
            'headers' => [
                'Authorization' => 'Bearer ' . $secret_key,
                'Accept' => 'application/json',
            ],
        ]);
        if (is_wp_error($response)) {
            return new WP_Error('eep_boost_stripe_verify_failed', __('Could not verify Stripe payment.', 'nexdirectory'));
        }
        $status = (int) wp_remote_retrieve_response_code($response);
        $payload = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($payload)) {
            return new WP_Error('eep_boost_stripe_verify_error', __('Stripe payment verification failed.', 'nexdirectory'));
        }
        $payment_status = sanitize_key((string) ($payload['payment_status'] ?? ''));
        $session_status = sanitize_key((string) ($payload['status'] ?? ''));
        if ($payment_status !== 'paid' || $session_status !== 'complete') {
            return new WP_Error('eep_boost_stripe_unpaid', __('Stripe payment is not marked as paid yet.', 'nexdirectory'));
        }

        $payment_intent = $payload['payment_intent'] ?? [];
        if (is_string($payment_intent)) {
            $payment_intent = ['id' => $payment_intent];
        }
        if (!is_array($payment_intent)) {
            $payment_intent = [];
        }
        $metadata = is_array($payload['metadata'] ?? null) ? (array) $payload['metadata'] : [];
        if (!empty($metadata['order_id']) && (int) $metadata['order_id'] !== (int) ($order['id'] ?? 0)) {
            return new WP_Error('eep_boost_stripe_mismatch', __('Stripe order verification mismatch.', 'nexdirectory'));
        }
        return [
            'gateway' => 'stripe',
            'external_order_id' => sanitize_text_field((string) ($payload['id'] ?? $session_id)),
            'external_payment_id' => sanitize_text_field((string) ($payment_intent['id'] ?? '')),
            'customer_ref' => sanitize_text_field((string) ($payment_intent['customer'] ?? ($payload['customer'] ?? ''))),
            'payment_method_ref' => sanitize_text_field((string) ($payment_intent['payment_method'] ?? '')),
            'raw' => $payload,
        ];
    }


    private function verify_and_capture_paypal_order(array $order, $paypal_token) {
        $auth = $this->get_paypal_access_token();
        if (is_wp_error($auth)) {
            return $auth;
        }
        $paypal_token = sanitize_text_field((string) $paypal_token);
        $capture_url = $auth['base'] . '/v2/checkout/orders/' . rawurlencode($paypal_token) . '/capture';
        $response = wp_remote_post($capture_url, [
            'timeout' => 30,
            'headers' => [
                'Authorization' => 'Bearer ' . $auth['token'],
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ],
            'body' => '{}',
        ]);
        if (is_wp_error($response)) {
            return new WP_Error('eep_boost_paypal_capture_failed', __('Could not capture PayPal payment.', 'nexdirectory'));
        }
        $status = (int) wp_remote_retrieve_response_code($response);
        $payload = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($payload)) {
            return new WP_Error('eep_boost_paypal_capture_error', __('PayPal payment capture failed.', 'nexdirectory'));
        }
        $order_status = sanitize_key((string) ($payload['status'] ?? ''));
        if (!in_array($order_status, ['completed', 'approved'], true)) {
            return new WP_Error('eep_boost_paypal_unpaid', __('PayPal payment is not completed.', 'nexdirectory'));
        }

        $custom_id = '';
        $payment_id = '';
        $customer_ref = '';
        if (!empty($payload['purchase_units']) && is_array($payload['purchase_units'])) {
            $unit = $payload['purchase_units'][0] ?? [];
            if (is_array($unit)) {
                $custom_id = sanitize_text_field((string) ($unit['custom_id'] ?? ''));
                if (!empty($unit['payments']['captures'][0]['id'])) {
                    $payment_id = sanitize_text_field((string) $unit['payments']['captures'][0]['id']);
                }
            }
        }
        if ($custom_id !== '' && !hash_equals((string) ($order['order_key'] ?? ''), $custom_id)) {
            return new WP_Error('eep_boost_paypal_mismatch', __('PayPal order verification mismatch.', 'nexdirectory'));
        }
        if (!empty($payload['payer']['payer_id'])) {
            $customer_ref = sanitize_text_field((string) $payload['payer']['payer_id']);
        }
        return [
            'gateway' => 'paypal',
            'external_order_id' => sanitize_text_field((string) ($payload['id'] ?? $paypal_token)),
            'external_payment_id' => $payment_id,
            'customer_ref' => $customer_ref,
            'payment_method_ref' => '',
            'raw' => $payload,
        ];
    }


    private function activate_boost_from_order($order_id, array $capture = []) {
        $order = $this->get_boost_order_by_id($order_id);
        if (!is_array($order)) {
            return new WP_Error('eep_boost_order_missing', __('Boost order not found.', 'nexdirectory'));
        }
        $post_id = absint((int) ($order['post_id'] ?? 0));
        if ($post_id <= 0 || get_post_type($post_id) !== self::POST_TYPE) {
            return new WP_Error('eep_boost_listing_missing', __('Listing for this boost order is invalid.', 'nexdirectory'));
        }

        $duration_days = max(1, absint((int) ($order['duration_days'] ?? 30)));
        $stacking = $this->boost_allow_stacking();
        $now = time();
        $existing_expiry_raw = (string) get_post_meta($post_id, 'eep_boost_expires_gmt', true);
        $existing_expiry_ts = $existing_expiry_raw !== '' ? strtotime($existing_expiry_raw . ' UTC') : false;
        $base_ts = $now;
        if ($stacking && $existing_expiry_ts && $existing_expiry_ts > $now) {
            $base_ts = (int) $existing_expiry_ts;
        }
        $expires_ts = $base_ts + ($duration_days * DAY_IN_SECONDS);
        $expires_gmt = gmdate('Y-m-d H:i:s', $expires_ts);
        $next_renewal = gmdate('Y-m-d H:i:s', max($now + HOUR_IN_SECONDS, $expires_ts - DAY_IN_SECONDS));
        $auto_renew = $this->boost_auto_renew_enabled() && (int) ($order['auto_renew'] ?? 0) === 1;

        update_post_meta($post_id, 'eep_boost_active', '1');
        update_post_meta($post_id, 'eep_boost_expires_gmt', $expires_gmt);
        update_post_meta($post_id, 'eep_boost_gateway', sanitize_key((string) ($order['gateway'] ?? '')));
        update_post_meta($post_id, 'eep_boost_duration_days', (string) $duration_days);
        update_post_meta($post_id, 'eep_boost_auto_renew', $auto_renew ? '1' : '0');
        if (!empty($capture['customer_ref'])) {
            update_post_meta($post_id, 'eep_boost_customer_ref', sanitize_text_field((string) $capture['customer_ref']));
        }
        if (!empty($capture['payment_method_ref'])) {
            update_post_meta($post_id, 'eep_boost_payment_method_ref', sanitize_text_field((string) $capture['payment_method_ref']));
        }

        $this->update_boost_order($order_id, [
            'order_status' => 'active',
            'external_order_id' => sanitize_text_field((string) ($capture['external_order_id'] ?? (string) ($order['external_order_id'] ?? ''))),
            'external_payment_id' => sanitize_text_field((string) ($capture['external_payment_id'] ?? (string) ($order['external_payment_id'] ?? ''))),
            'customer_ref' => sanitize_text_field((string) ($capture['customer_ref'] ?? (string) ($order['customer_ref'] ?? ''))),
            'payment_method_ref' => sanitize_text_field((string) ($capture['payment_method_ref'] ?? (string) ($order['payment_method_ref'] ?? ''))),
            'payment_meta' => wp_json_encode(is_array($capture['raw'] ?? null) ? (array) $capture['raw'] : []),
            'paid_at' => current_time('mysql', true),
            'expires_at' => $expires_gmt,
            'next_renewal_at' => $auto_renew ? $next_renewal : null,
            'renewal_attempts' => 0,
            'auto_renew' => $auto_renew ? 1 : 0,
            'stacking_applied' => $stacking ? 1 : 0,
        ]);

        if (method_exists($this, 'record_analytics_event')) {
            $this->record_analytics_event($post_id, 'boost_sale', [
                'context' => 'monetization',
                'meta_key' => 'gateway',
                'meta_value' => sanitize_key((string) ($order['gateway'] ?? '')),
                'value' => (float) ($order['total_amount'] ?? 0),
                'owner_id' => (int) get_post_field('post_author', $post_id),
                'user_id' => (int) ($order['user_id'] ?? 0),
            ]);
        }

        return true;
    }


    public function handle_toggle_boost_autorenew() {
        if (!is_user_logged_in()) {
            wp_die(__('Login required.', 'nexdirectory'));
        }
        $post_id = absint((int) wp_unslash($_POST['post_id'] ?? 0));
        if ($post_id <= 0 || get_post_type($post_id) !== self::POST_TYPE) {
            wp_die(__('Invalid listing selected.', 'nexdirectory'));
        }
        if (!isset($_POST['eep_boost_toggle_nonce']) || !wp_verify_nonce(
            eep_sanitize_text(wp_unslash($_POST['eep_boost_toggle_nonce'])),
            'eep_boost_toggle_' . $post_id
        )) {
            wp_die(__('Invalid request.', 'nexdirectory'));
        }
        $author_id = (int) get_post_field('post_author', $post_id);
        if ($author_id !== get_current_user_id() && !current_user_can('manage_options')) {
            wp_die(__('Not allowed.', 'nexdirectory'));
        }

        $enabled = (string) wp_unslash($_POST['auto_renew'] ?? '0') === '1' && $this->boost_auto_renew_enabled();
        update_post_meta($post_id, 'eep_boost_auto_renew', $enabled ? '1' : '0');

        global $wpdb;
        $table = $this->get_boost_orders_table_name();
        $wpdb->query($wpdb->prepare(
            "UPDATE {$table}
             SET auto_renew = %d, updated_at = %s
             WHERE post_id = %d
               AND order_status IN ('active','paid')
             ORDER BY id DESC",
            $enabled ? 1 : 0,
            current_time('mysql', true),
            $post_id
        )); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

        $message = $enabled
            ? __('Auto-renew enabled for this boost.', 'nexdirectory')
            : __('Auto-renew disabled for this boost.', 'nexdirectory');
        $this->redirect_with_message($this->get_boost_return_url(), $message);
    }


    public function handle_cancel_boost_subscription() {
        if (!is_user_logged_in()) {
            wp_die(__('Login required.', 'nexdirectory'));
        }
        $post_id = absint((int) wp_unslash($_POST['post_id'] ?? 0));
        if ($post_id <= 0 || get_post_type($post_id) !== self::POST_TYPE) {
            wp_die(__('Invalid listing selected.', 'nexdirectory'));
        }
        if (!isset($_POST['eep_boost_cancel_nonce']) || !wp_verify_nonce(
            eep_sanitize_text(wp_unslash($_POST['eep_boost_cancel_nonce'])),
            'eep_boost_cancel_' . $post_id
        )) {
            wp_die(__('Invalid request.', 'nexdirectory'));
        }
        $author_id = (int) get_post_field('post_author', $post_id);
        if ($author_id !== get_current_user_id() && !current_user_can('manage_options')) {
            wp_die(__('Not allowed.', 'nexdirectory'));
        }
        update_post_meta($post_id, 'eep_boost_auto_renew', '0');
        $this->redirect_with_message($this->get_boost_return_url(), __('Auto-renew cancelled. Current boost remains active until expiry.', 'nexdirectory'));
    }


    public function schedule_boost_renewals() {
        if ($this->should_use_action_scheduler()) {
            if (function_exists('as_unschedule_all_actions')) {
                as_unschedule_all_actions('eep_boost_renewal_cron', [], 'eep');
            }
            if (function_exists('as_schedule_recurring_action')) {
                as_schedule_recurring_action(time() + 480, HOUR_IN_SECONDS, 'eep_boost_renewal_cron', [], 'eep');
            }
            if (wp_next_scheduled('eep_boost_renewal_cron')) {
                wp_clear_scheduled_hook('eep_boost_renewal_cron');
            }
            return;
        }
        if (function_exists('as_unschedule_all_actions')) {
            as_unschedule_all_actions('eep_boost_renewal_cron', [], 'eep');
        }
        $scheduled = wp_get_scheduled_event('eep_boost_renewal_cron');
        // Migrate legacy custom schedule to WP core hourly once.
        if ($scheduled && (string) ($scheduled->schedule ?? '') !== 'hourly') {
            wp_clear_scheduled_hook('eep_boost_renewal_cron');
            $scheduled = false;
        }
        if (!$scheduled) {
            wp_schedule_event(time() + 480, 'hourly', 'eep_boost_renewal_cron');
        }
    }


    public function run_boost_renewal_cron() {
        if (!$this->is_boost_enabled()) {
            return;
        }
        $this->send_boost_expiry_reminders();
        $this->process_due_boost_renewals();
        $this->downgrade_expired_boosts();
    }


    private function send_boost_expiry_reminders() {
        $days_before = max(1, absint((int) get_option('eep_boost_reminder_days', 3)));
        $from = gmdate('Y-m-d H:i:s', time() + (($days_before * DAY_IN_SECONDS) - HOUR_IN_SECONDS));
        $to = gmdate('Y-m-d H:i:s', time() + (($days_before * DAY_IN_SECONDS) + HOUR_IN_SECONDS));
        global $wpdb;
        $table = $this->get_boost_orders_table_name();
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$table}
             WHERE order_status = 'active'
               AND auto_renew IN (0,1)
               AND reminder_sent_at IS NULL
               AND expires_at IS NOT NULL
               AND expires_at BETWEEN %s AND %s
             ORDER BY id DESC
             LIMIT 250",
            $from,
            $to
        ), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        if (!is_array($rows) || empty($rows)) {
            return;
        }
        foreach ($rows as $row) {
            $user_id = absint((int) ($row['user_id'] ?? 0));
            $post_id = absint((int) ($row['post_id'] ?? 0));
            $user = $user_id > 0 ? get_userdata($user_id) : null;
            $email = $user ? sanitize_email((string) $user->user_email) : '';
            if ($post_id <= 0 || $email === '') {
                $this->update_boost_order((int) ($row['id'] ?? 0), [
                    'reminder_sent_at' => current_time('mysql', true),
                ]);
                continue;
            }
            $title = sanitize_text_field((string) get_the_title($post_id));
            $link = esc_url_raw((string) get_permalink($post_id));
            $expires_at = sanitize_text_field((string) ($row['expires_at'] ?? ''));
            $subject = sprintf(
                /* translators: %s: listing title */
                __('Boost expiring soon: %s', 'nexdirectory'),
                $title
            );
            $message = sprintf(
                __("Your boosted listing \"%1\$s\" expires on %2\$s.\n\nReview: %3\$s\n\nYou can re-boost it from your dashboard.", 'nexdirectory'),
                $title,
                $expires_at !== '' ? wp_date(get_option('date_format') . ' ' . get_option('time_format'), strtotime($expires_at . ' UTC')) : __('soon', 'nexdirectory'),
                $link
            );
            $this->send_platform_email($email, $subject, '', [
                'fallback_text' => $message,
                'email_heading' => __('Boost Expiry Reminder', 'nexdirectory'),
            ]);
            $this->update_boost_order((int) ($row['id'] ?? 0), [
                'reminder_sent_at' => current_time('mysql', true),
            ]);
        }
    }


    private function process_due_boost_renewals() {
        if (!$this->boost_auto_renew_enabled()) {
            return;
        }
        $now_gmt = gmdate('Y-m-d H:i:s');
        global $wpdb;
        $table = $this->get_boost_orders_table_name();
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT *
             FROM {$table}
             WHERE order_status = 'active'
               AND auto_renew = 1
               AND next_renewal_at IS NOT NULL
               AND next_renewal_at <= %s
             ORDER BY next_renewal_at ASC
             LIMIT 100",
            $now_gmt
        ), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        if (!is_array($rows) || empty($rows)) {
            return;
        }

        foreach ($rows as $row) {
            $order_id = absint((int) ($row['id'] ?? 0));
            $post_id = absint((int) ($row['post_id'] ?? 0));
            if ($order_id <= 0 || $post_id <= 0) {
                continue;
            }
            $gateway = sanitize_key((string) ($row['gateway'] ?? ''));
            $attempts = absint((int) ($row['renewal_attempts'] ?? 0));
            $max_attempts = max(1, absint((int) ($row['max_renewal_attempts'] ?? get_option('eep_boost_retry_limit', 3))));

            if ($gateway !== 'stripe') {
                $attempts++;
                $fields = [
                    'order_status' => $attempts >= $max_attempts ? 'expired' : 'renewal_failed',
                    'renewal_attempts' => $attempts,
                    'next_renewal_at' => $attempts >= $max_attempts ? null : gmdate('Y-m-d H:i:s', time() + (12 * HOUR_IN_SECONDS)),
                    'auto_renew' => $attempts >= $max_attempts ? 0 : 1,
                ];
                $this->update_boost_order($order_id, $fields);
                if ($attempts >= $max_attempts) {
                    update_post_meta($post_id, 'eep_boost_auto_renew', '0');
                }
                continue;
            }

            $renewal = $this->attempt_stripe_auto_renew($row);
            if (is_wp_error($renewal)) {
                $attempts++;
                $this->update_boost_order($order_id, [
                    'order_status' => $attempts >= $max_attempts ? 'expired' : 'renewal_failed',
                    'renewal_attempts' => $attempts,
                    'next_renewal_at' => $attempts >= $max_attempts ? null : gmdate('Y-m-d H:i:s', time() + (12 * HOUR_IN_SECONDS)),
                    'auto_renew' => $attempts >= $max_attempts ? 0 : 1,
                ]);
                if ($attempts >= $max_attempts) {
                    update_post_meta($post_id, 'eep_boost_auto_renew', '0');
                }
                $this->append_runtime_log('warning', 'Boost auto-renew failed', [
                    'order_id' => $order_id,
                    'post_id' => $post_id,
                    'gateway' => $gateway,
                    'error' => (string) $renewal->get_error_message(),
                ]);
                continue;
            }

            $new_order = $this->create_boost_order_record([
                'parent_order_id' => $order_id,
                'post_id' => $post_id,
                'user_id' => absint((int) ($row['user_id'] ?? 0)),
                'gateway' => 'stripe',
                'order_status' => 'paid',
                'currency' => sanitize_text_field((string) ($row['currency'] ?? $this->get_boost_currency())),
                'subtotal' => (float) ($row['subtotal'] ?? 0),
                'vat_amount' => (float) ($row['vat_amount'] ?? 0),
                'total_amount' => (float) ($row['total_amount'] ?? 0),
                'duration_days' => absint((int) ($row['duration_days'] ?? 30)),
                'auto_renew' => 1,
                'is_renewal' => 1,
                'stacking_applied' => 1,
                'external_order_id' => sanitize_text_field((string) ($renewal['external_order_id'] ?? '')),
                'external_payment_id' => sanitize_text_field((string) ($renewal['external_payment_id'] ?? '')),
                'customer_ref' => sanitize_text_field((string) ($renewal['customer_ref'] ?? '')),
                'payment_method_ref' => sanitize_text_field((string) ($renewal['payment_method_ref'] ?? '')),
                'payment_meta' => wp_json_encode((array) ($renewal['raw'] ?? [])),
                'paid_at' => current_time('mysql', true),
            ]);

            if (is_array($new_order) && !empty($new_order['id'])) {
                $this->activate_boost_from_order((int) $new_order['id'], $renewal);
                $this->update_boost_order($order_id, [
                    'order_status' => 'renewed',
                    'renewal_attempts' => 0,
                    'next_renewal_at' => null,
                ]);
                if (method_exists($this, 'record_analytics_event')) {
                    $this->record_analytics_event($post_id, 'boost_renewal', [
                        'context' => 'monetization',
                        'meta_key' => 'gateway',
                        'meta_value' => 'stripe',
                        'value' => (float) ($row['total_amount'] ?? 0),
                        'owner_id' => (int) get_post_field('post_author', $post_id),
                        'user_id' => absint((int) ($row['user_id'] ?? 0)),
                    ]);
                }
            }
        }
    }


    private function attempt_stripe_auto_renew(array $order) {
        $secret_key = $this->get_active_stripe_secret_key();
        if ($secret_key === '') {
            return new WP_Error('eep_boost_stripe_missing_key', __('Stripe secret key missing for auto-renew.', 'nexdirectory'));
        }
        $customer_ref = trim((string) ($order['customer_ref'] ?? ''));
        if ($customer_ref === '') {
            $customer_ref = trim((string) get_post_meta((int) ($order['post_id'] ?? 0), 'eep_boost_customer_ref', true));
        }
        $payment_method_ref = trim((string) ($order['payment_method_ref'] ?? ''));
        if ($payment_method_ref === '') {
            $payment_method_ref = trim((string) get_post_meta((int) ($order['post_id'] ?? 0), 'eep_boost_payment_method_ref', true));
        }
        if ($customer_ref === '' || $payment_method_ref === '') {
            return new WP_Error('eep_boost_stripe_missing_pm', __('Stored Stripe payment method is not available for auto-renew.', 'nexdirectory'));
        }

        $amount_cents = max(1, (int) round(((float) ($order['total_amount'] ?? 0)) * 100));
        $currency = strtolower(sanitize_text_field((string) ($order['currency'] ?? $this->get_boost_currency())));
        $response = wp_remote_post('https://api.stripe.com/v1/payment_intents', [
            'timeout' => 25,
            'headers' => [
                'Authorization' => 'Bearer ' . $secret_key,
                'Content-Type' => 'application/x-www-form-urlencoded',
            ],
            'body' => [
                'amount' => (string) $amount_cents,
                'currency' => $currency,
                'customer' => $customer_ref,
                'payment_method' => $payment_method_ref,
                'off_session' => 'true',
                'confirm' => 'true',
                'description' => sprintf(__('Boost auto-renewal for listing #%d', 'nexdirectory'), (int) ($order['post_id'] ?? 0)),
                'metadata[renewal_for_order]' => (string) absint((int) ($order['id'] ?? 0)),
                'metadata[post_id]' => (string) absint((int) ($order['post_id'] ?? 0)),
            ],
        ]);
        if (is_wp_error($response)) {
            return new WP_Error('eep_boost_stripe_auto_request_failed', __('Stripe auto-renew request failed.', 'nexdirectory'));
        }
        $status = (int) wp_remote_retrieve_response_code($response);
        $payload = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($status < 200 || $status >= 300 || !is_array($payload)) {
            $message = __('Stripe auto-renew failed.', 'nexdirectory');
            if (is_array($payload) && !empty($payload['error']['message'])) {
                $message = sanitize_text_field((string) $payload['error']['message']);
            }
            return new WP_Error('eep_boost_stripe_auto_failed', $message);
        }
        $intent_status = sanitize_key((string) ($payload['status'] ?? ''));
        if ($intent_status !== 'succeeded') {
            return new WP_Error('eep_boost_stripe_auto_not_paid', __('Stripe auto-renew payment was not completed.', 'nexdirectory'));
        }
        return [
            'gateway' => 'stripe',
            'external_order_id' => sanitize_text_field((string) ($payload['id'] ?? '')),
            'external_payment_id' => sanitize_text_field((string) ($payload['id'] ?? '')),
            'customer_ref' => $customer_ref,
            'payment_method_ref' => $payment_method_ref,
            'raw' => $payload,
        ];
    }


    private function downgrade_expired_boosts() {
        global $wpdb;
        $table = $this->get_boost_orders_table_name();
        $now = gmdate('Y-m-d H:i:s');
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT DISTINCT post_id FROM {$table}
             WHERE expires_at IS NOT NULL
               AND expires_at < %s
               AND order_status IN ('active','renewal_failed','expired','renewed')
             LIMIT 500",
            $now
        ), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        if (!is_array($rows) || empty($rows)) {
            return;
        }
        foreach ($rows as $row) {
            $post_id = absint((int) ($row['post_id'] ?? 0));
            if ($post_id <= 0) {
                continue;
            }
            $state = $this->get_listing_boost_state($post_id);
            if (!empty($state['active'])) {
                continue;
            }
            update_post_meta($post_id, 'eep_boost_active', '0');
        }
    }


    public function filter_posts_clauses_for_boost_ranking($clauses, $query) {
        if (!$query instanceof WP_Query) {
            return $clauses;
        }
        if (!$this->is_boost_enabled()) {
            return $clauses;
        }
        if (!(bool) $query->get('eep_apply_boost_order')) {
            return $clauses;
        }
        if ((string) $query->get('post_type') !== self::POST_TYPE) {
            return $clauses;
        }

        global $wpdb;
        $active_alias = 'eep_boost_pm_active';
        $expires_alias = 'eep_boost_pm_expires';
        if (strpos((string) $clauses['join'], $active_alias) === false) {
            $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} {$active_alias} ON ({$wpdb->posts}.ID = {$active_alias}.post_id AND {$active_alias}.meta_key = 'eep_boost_active')";
        }
        if (strpos((string) $clauses['join'], $expires_alias) === false) {
            $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} {$expires_alias} ON ({$wpdb->posts}.ID = {$expires_alias}.post_id AND {$expires_alias}.meta_key = 'eep_boost_expires_gmt')";
        }

        $now_gmt = esc_sql(gmdate('Y-m-d H:i:s'));
        $remaining_days_expr = "CASE WHEN {$active_alias}.meta_value = '1' AND {$expires_alias}.meta_value >= '{$now_gmt}' THEN GREATEST(TIMESTAMPDIFF(SECOND, '{$now_gmt}', {$expires_alias}.meta_value), 0) / 86400 ELSE 0 END";
        $priority_score_expr = "CASE WHEN {$active_alias}.meta_value = '1' AND {$expires_alias}.meta_value >= '{$now_gmt}' THEN 10 ELSE 0 END";
        $existing_order = trim((string) ($clauses['orderby'] ?? ''));
        $mode = $this->get_boost_ranking_mode();
        if ($mode === 'time_weighted') {
            $priority_score_expr = "{$priority_score_expr} + {$remaining_days_expr}";
        }
        $default_order = "{$priority_score_expr} DESC, {$wpdb->posts}.post_date DESC";
        $clauses['orderby'] = $existing_order !== '' ? ($default_order . ', ' . $existing_order) : $default_order;
        if (empty($clauses['groupby'])) {
            $clauses['groupby'] = "{$wpdb->posts}.ID";
        }

        return $clauses;
    }


    private function get_boost_analytics_summary($days = 30) {
        $days = max(0, (int) $days);
        global $wpdb;
        $table = $this->get_boost_orders_table_name();
        $since = $days > 0 ? gmdate('Y-m-d H:i:s', time() - ($days * DAY_IN_SECONDS)) : '';
        $params = [];
        $date_clause = '';
        if ($since !== '') {
            $date_clause = ' AND created_at >= %s';
            $params[] = $since;
        }

        $summary_sql = "SELECT
            SUM(CASE WHEN order_status IN ('active','paid','renewed') AND is_renewal = 0 THEN 1 ELSE 0 END) AS boosts_sold,
            SUM(CASE WHEN order_status IN ('active','paid','renewed') THEN total_amount ELSE 0 END) AS revenue,
            SUM(CASE WHEN order_status IN ('active','paid') AND is_renewal = 1 THEN 1 ELSE 0 END) AS renewals_success,
            SUM(CASE WHEN is_renewal = 1 THEN 1 ELSE 0 END) AS renewals_total
            FROM {$table}
            WHERE 1=1{$date_clause}";
        if (!empty($params)) {
            $summary_row = $wpdb->get_row($wpdb->prepare($summary_sql, $params), ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        } else {
            $summary_row = $wpdb->get_row($summary_sql, ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        }

        $active_sql = "SELECT COUNT(DISTINCT post_id)
            FROM {$table}
            WHERE order_status IN ('active','paid')
              AND expires_at IS NOT NULL
              AND expires_at >= %s";
        $active_count = (int) $wpdb->get_var($wpdb->prepare($active_sql, gmdate('Y-m-d H:i:s'))); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

        $boosts_sold = (int) round((float) ($summary_row['boosts_sold'] ?? 0));
        $revenue = (float) ($summary_row['revenue'] ?? 0);
        $renewals_success = (int) round((float) ($summary_row['renewals_success'] ?? 0));
        $renewals_total = (int) round((float) ($summary_row['renewals_total'] ?? 0));
        $renewal_rate = $renewals_total > 0 ? round(($renewals_success / max(1, $renewals_total)) * 100, 2) : 0.0;

        return [
            'boosts_sold' => $boosts_sold,
            'boost_revenue' => round($revenue, 2),
            'renewal_rate' => $renewal_rate,
            'active_boosted' => $active_count,
        ];
    }
}
