<?php

if (!defined('ABSPATH')) { exit; }

class AIEO_DMM_Web_Push extends AIEO_DMM_Module_Base {

    
    const SCHEMA_OPTION  = 'aieo_push_schema_version';
    const SCHEMA_VERSION = '12'; 

    
    const CRON_HOOK = 'aieo_push_nightly_recompute';

    
    const CRON_DISPATCH_HOOK = 'aieo_push_dispatch_tick';

    
    const REWRITE_FLUSHED_OPTION = 'aieo_push_rewrite_flushed';

    
    const ENGINE_OPTION       = 'aieo_push_engine_status';
    const ENGINE_STATUS_LIVE   = 'live';
    const ENGINE_STATUS_PAUSED = 'paused';

    public static function get_engine_status() {
        $val = (string) get_option(self::ENGINE_OPTION, self::ENGINE_STATUS_LIVE);
        return $val === self::ENGINE_STATUS_PAUSED ? self::ENGINE_STATUS_PAUSED : self::ENGINE_STATUS_LIVE;
    }

    public static function is_engine_live() {
        return self::get_engine_status() === self::ENGINE_STATUS_LIVE;
    }

    public static function set_engine_status($status) {
        $val = $status === self::ENGINE_STATUS_PAUSED ? self::ENGINE_STATUS_PAUSED : self::ENGINE_STATUS_LIVE;
        update_option(self::ENGINE_OPTION, $val, false);
        return $val;
    }

    
    public static function engine_paused_message() {
        return __('Engine is paused — outbound actions are blocked. Toggle "Resume — go live" at the top of the Web Push tab to resume.', 'ai-eshop-optimizer');
    }

    
    const BANNER_OPTION = 'aieo_push_banner_settings';

    
    public static function default_banner_settings() {
        return array(
            
            
            
            
            
            'banner_enabled'        => true,
            
            
            
            
            'position'              => 'top',
            'offset'                => 80,            
            
            
            
            
            'delay_ms'              => 10000,
            
            'bg'                    => '#ffffff',
            'text'                  => '#1f2937',
            'border_color'          => '#e5e7eb',
            'border_width'          => 0,
            'border_radius'         => 12,
            'icon_color'            => '#F46767',
            
            'btn_primary_bg'        => '#F46767',
            'btn_primary_text'      => '#ffffff',
            'btn_primary_border'    => '#F46767',
            'btn_primary_border_w'  => 0,
            
            'btn_secondary_bg'      => '#f3f4f6',
            'btn_secondary_text'   => '#4b5563',
            'btn_secondary_border'  => '#f3f4f6',
            'btn_secondary_border_w'=> 0,
            
            'btn_radius'            => 8,
            
            'copy_title'            => __('Μείνε ενημερωμένη', 'ai-eshop-optimizer'),
            'copy_body'             => __('Πάρε άμεσα ειδοποίηση για προσφορές, νέες αφίξεις και επανάληψη αγαπημένων προϊόντων.', 'ai-eshop-optimizer'),
            'copy_accept'           => __('Ναι, θέλω', 'ai-eshop-optimizer'),
            'copy_dismiss'          => __('Όχι τώρα', 'ai-eshop-optimizer'),
            
            
            
            
            'copy_thanks_yes'       => __('Ευχαριστούμε! Επιβεβαίωσε στο pop-up του browser για να ενεργοποιηθούν οι ειδοποιήσεις.', 'ai-eshop-optimizer'),
            'copy_thanks_no'        => __('Εντάξει! Μπορείς να ενεργοποιήσεις τις ειδοποιήσεις αργότερα από το μενού του site.', 'ai-eshop-optimizer'),
            'thanks_autoclose_secs' => 5,
        );
    }

    public static function get_banner_settings() {
        $stored = get_option(self::BANNER_OPTION, array());
        if (!is_array($stored)) { $stored = array(); }
        return array_merge(self::default_banner_settings(), $stored);
    }

    
    const RECOMPUTE_BATCH = 1000;

    
    const RECOMPUTE_MAX_PER_REQUEST = 50000;

    public function get_id()       { return 'web-push'; }
    public function get_label()    { return __('Web Push Subscribers', 'ai-eshop-optimizer'); }
    public function get_priority() { return 71; } 
    public function get_group()    { return 'customer_profiling'; }

    public function is_pro_locked() { return true; }
    public function get_pro_promo() {
        return array(
            'title'       => __('Web Push Newsletter', 'ai-eshop-optimizer'),
            'description' => __('Browser-based newsletter that reaches lapsed customers without depending on email deliverability. Targeted segments, automated reactivation flows, and lookalike audiences powered by your own attribution data.', 'ai-eshop-optimizer'),
            'bullets'     => array(
                __('Native browser push — no SMTP, no spam folders, no Mailchimp fees.', 'ai-eshop-optimizer'),
                __('Behavioural segments (Top customers, Lost cohort, Lookalikes, Abandoned cart).', 'ai-eshop-optimizer'),
                __('Triggered flows: welcome, reactivation, price-drop, back-in-stock.', 'ai-eshop-optimizer'),
                __('Full dispatch log with per-subscriber delivery + click attribution.', 'ai-eshop-optimizer'),
            ),
        );
    }

    public function register_hooks() {
        
        
        
        $push_dir = AIEO_DIR . 'includes/dmm/push/';
        $push_files = array(
            'class-aieo-push-sender.php',
            'class-aieo-push-rest.php',
            'class-aieo-newsletter-adapter-base.php',
            'class-aieo-newsletter-adapter-tnp.php',
            'class-aieo-newsletter-adapter-mailchimp.php',
            'class-aieo-newsletter-adapter-klaviyo.php',
            'class-aieo-newsletter-adapter-webhook.php',
            'class-aieo-newsletter-registry.php',
            'class-aieo-push-trigger-base.php',
            'class-aieo-push-dispatcher.php',
        );
        foreach ($push_files as $f) {
            if (file_exists($push_dir . $f)) {
                require_once $push_dir . $f;
            }
        }
        
        
        $triggers_dir = $push_dir . 'triggers/';
        if (is_dir($triggers_dir)) {
            foreach ((array) glob($triggers_dir . 'class-aieo-push-trigger-*.php') as $tf) {
                require_once $tf;
                
                $class = $this->trigger_class_from_file(basename($tf));
                if ($class !== '' && class_exists($class) && class_exists('AIEO_Push_Trigger_Registry')) {
                    AIEO_Push_Trigger_Registry::instance()->register(new $class());
                }
            }
        }

        add_action('plugins_loaded', array($this, 'maybe_install'), 25);
        add_action('wp_ajax_aieo_dmm_webpush_recompute', array($this, 'ajax_recompute'));
        add_action('wp_ajax_aieo_dmm_webpush_send_test', array($this, 'ajax_send_test'));
        add_action('wp_ajax_aieo_dmm_webpush_save_banner', array($this, 'ajax_save_banner'));
        add_action('wp_ajax_aieo_dmm_webpush_save_newsletter', array($this, 'ajax_save_newsletter'));
        add_action('wp_ajax_aieo_dmm_webpush_save_template', array($this, 'ajax_save_template'));
        add_action('wp_ajax_aieo_dmm_webpush_test_newsletter', array($this, 'ajax_test_newsletter'));
        add_action('wp_ajax_aieo_dmm_webpush_sync_cohort', array($this, 'ajax_sync_cohort'));
        add_action('wp_ajax_aieo_dmm_webpush_create_campaign', array($this, 'ajax_create_campaign'));
        add_action('wp_ajax_aieo_dmm_webpush_launch_campaign', array($this, 'ajax_launch_campaign'));
        add_action('wp_ajax_aieo_dmm_webpush_toggle_engine', array($this, 'ajax_toggle_engine'));
        add_action('admin_post_aieo_dmm_webpush_export_cohort', array($this, 'handle_export_cohort'));
        add_action('admin_post_aieo_dmm_webpush_export_lookalike', array($this, 'handle_export_lookalike'));

        
        
        
        add_action(self::CRON_HOOK, array($this, 'cron_nightly_recompute'));
        if (!wp_next_scheduled(self::CRON_HOOK)) {
            wp_schedule_event($this->next_athens_0230_utc(), 'daily', self::CRON_HOOK);
        }

        
        
        add_action(self::CRON_DISPATCH_HOOK, array($this, 'cron_dispatch_tick'));
        add_filter('cron_schedules', array($this, 'register_dispatch_interval'));
        if (!wp_next_scheduled(self::CRON_DISPATCH_HOOK)) {
            wp_schedule_event(time() + 60, 'aieo_push_5min', self::CRON_DISPATCH_HOOK);
        }

        
        if (class_exists('AIEO_Push_REST')) {
            AIEO_Push_REST::instance();
        }

        
        add_action('init', array($this, 'register_sw_rewrite'));
        add_filter('query_vars', array($this, 'add_sw_query_var'));
        add_action('template_redirect', array($this, 'maybe_serve_sw'), 1);

        
        add_action('wp_enqueue_scripts', array($this, 'enqueue_optin_banner'));

        
        
        
        
        
        
        
        
        
        add_filter('rocket_delay_js_exclusions', array($this, 'register_rocket_delay_js_exclusions'));
    }

    
    public function register_rocket_delay_js_exclusions($exclusions) {
        if (!is_array($exclusions)) { $exclusions = array(); }
        foreach (array('aieo-push-optin', 'aieoPush', 'aieo-push-sw', 'aieo-magic-link-modal') as $token) {
            if (!in_array($token, $exclusions, true)) { $exclusions[] = $token; }
        }
        return $exclusions;
    }

    
    private function next_athens_0230_utc() {
        try {
            $tz   = new \DateTimeZone('Europe/Athens');
            $next = new \DateTime('tomorrow 02:30:00', $tz);
            return (int) $next->getTimestamp();
        } catch (\Throwable $e) {
            
            return time() + DAY_IN_SECONDS;
        }
    }

    
    public function register_dispatch_interval($schedules) {
        if (!isset($schedules['aieo_push_5min'])) {
            $schedules['aieo_push_5min'] = array(
                'interval' => 5 * MINUTE_IN_SECONDS,
                'display'  => __('Every 5 minutes (AIEO push dispatch)', 'ai-eshop-optimizer'),
            );
        }
        return $schedules;
    }

    
    public function cron_dispatch_tick() {
        if (!class_exists('AIEO_Push_Dispatcher')) { return; }
        try {
            $stats = AIEO_Push_Dispatcher::instance()->process_tick();
            update_option('aieo_push_last_dispatch_tick', array(
                'at'    => time(),
                'stats' => $stats,
            ), false);
            if (function_exists('aieo_debug_log')) {
                aieo_debug_log('AIEO_Push dispatch tick: ' . wp_json_encode($stats));
            }
        } catch (\Throwable $e) {
            if (function_exists('aieo_debug_log')) {
                aieo_debug_log('AIEO_Push dispatch tick failed: ' . $e->getMessage());
            }
        }
    }

    
    private function trigger_class_from_file($basename) {
        $stem = preg_replace('/^class-aieo-push-trigger-/', '', $basename);
        $stem = preg_replace('/\.php$/', '', $stem);
        if ($stem === '' || $stem === false) { return ''; }
        
        $parts = explode('-', $stem);
        $parts = array_map('ucfirst', $parts);
        return 'AIEO_Push_Trigger_' . implode('', $parts);
    }

    public function enqueue_admin_assets() {
        
        wp_enqueue_style('wp-color-picker');
        wp_enqueue_script('wp-color-picker');
        wp_add_inline_script('wp-color-picker',
            'jQuery(function($){$(".aieo-dmm-push-color").wpColorPicker();});'
        );
    }

    
    
    

    public function maybe_install() {
        
        
        $push_dir = AIEO_DIR . 'includes/dmm/push/';
        if (file_exists($push_dir . 'class-aieo-push-sender.php')) {
            require_once $push_dir . 'class-aieo-push-sender.php';
        }
        if (file_exists($push_dir . 'class-aieo-push-rest.php')) {
            require_once $push_dir . 'class-aieo-push-rest.php';
        }

        if ((string) get_option(self::SCHEMA_OPTION, '') === self::SCHEMA_VERSION) {
            return;
        }

        global $wpdb;
        $tbl_ci = $wpdb->prefix . 'aieo_customer_intelligence';

        $base = AIEO_DIR . 'includes/dmm/sql/';
        require_once $base . 'push-schema.php';
        require_once $base . 'push-sprocs.php';

        
        
        
        
        

        if (function_exists('aieo_push_install_schema')) {
            aieo_push_install_schema();
        }
        if (function_exists('aieo_push_install_sprocs')) {
            aieo_push_install_sprocs();
        }
        update_option(self::SCHEMA_OPTION, self::SCHEMA_VERSION, false);

        
        delete_option(self::REWRITE_FLUSHED_OPTION);
    }

    
    public static function force_schema_refresh() {
        delete_option(self::SCHEMA_OPTION);
    }

    
    
    

    public function register_sw_rewrite() {
        add_rewrite_rule('^aieo-push-sw\.js$', 'index.php?aieo_push_sw=1', 'top');
        if ((int) get_option(self::REWRITE_FLUSHED_OPTION, 0) !== 1) {
            flush_rewrite_rules(false);
            update_option(self::REWRITE_FLUSHED_OPTION, 1, false);
        }
    }

    public function add_sw_query_var($vars) {
        $vars[] = 'aieo_push_sw';
        return $vars;
    }

    
    public function maybe_serve_sw() {
        if ((int) get_query_var('aieo_push_sw') !== 1) { return; }
        $path = AIEO_DIR . 'assets/js/aieo-push-sw.js';
        if (!file_exists($path)) {
            status_header(404);
            exit;
        }
        nocache_headers();
        header('Content-Type: application/javascript; charset=utf-8');
        header('Service-Worker-Allowed: /');
        header('Cache-Control: public, max-age=3600');
        
        readfile($path);
        exit;
    }

    
    
    

    public function enqueue_optin_banner() {
        if (is_admin()) { return; }

        
        
        
        
        
        $banner_settings = self::get_banner_settings();
        if (empty($banner_settings['banner_enabled'])) {
            return;
        }

        
        if (is_user_logged_in() && function_exists('current_user_can') && current_user_can('manage_options')) {
            
            
            if (defined('AIEO_PUSH_HIDE_FOR_ADMINS') && AIEO_PUSH_HIDE_FOR_ADMINS) {
                return;
            }
        }

        $css_dir  = defined('AIEO_RUNTIME_DIR') ? AIEO_RUNTIME_DIR : '';
        $css_uri  = defined('AIEO_RUNTIME_URI') ? AIEO_RUNTIME_URI : '';
        $css_path = $css_dir . 'assets/css/aieo-push.css';
        $js_path  = $css_dir . 'assets/js/aieo-push-optin.js';

        if (!$css_uri || !file_exists($css_path) || !file_exists($js_path)) {
            return;
        }

        wp_enqueue_style(
            'aieo-push-optin',
            $css_uri . 'assets/css/aieo-push.css',
            array(),
            (string) filemtime($css_path)
        );
        wp_enqueue_script(
            'aieo-push-optin',
            $css_uri . 'assets/js/aieo-push-optin.js',
            array(),
            (string) filemtime($js_path),
            true
        );

        
        
        $b = self::get_banner_settings();
        $inline = sprintf(
            '.aieo-push-banner{'
            . '--aieo-pb-bg:%s;--aieo-pb-text:%s;--aieo-pb-muted:%s;'
            . '--aieo-pb-border-color:%s;--aieo-pb-border-width:%dpx;--aieo-pb-border-radius:%dpx;'
            . '--aieo-pb-icon-color:%s;'
            . '--aieo-pb-btn-primary-bg:%s;--aieo-pb-btn-primary-text:%s;'
            . '--aieo-pb-btn-primary-border:%s;--aieo-pb-btn-primary-border-w:%dpx;'
            . '--aieo-pb-btn-secondary-bg:%s;--aieo-pb-btn-secondary-text:%s;'
            . '--aieo-pb-btn-secondary-border:%s;--aieo-pb-btn-secondary-border-w:%dpx;'
            . '--aieo-pb-btn-radius:%dpx;'
            . '--aieo-pb-offset:%dpx;}',
            esc_attr((string) $b['bg']),
            esc_attr((string) $b['text']),
            esc_attr((string) $b['text']), 
            esc_attr((string) $b['border_color']),
            (int) $b['border_width'],
            (int) $b['border_radius'],
            esc_attr((string) $b['icon_color']),
            esc_attr((string) $b['btn_primary_bg']),
            esc_attr((string) $b['btn_primary_text']),
            esc_attr((string) $b['btn_primary_border']),
            (int) $b['btn_primary_border_w'],
            esc_attr((string) $b['btn_secondary_bg']),
            esc_attr((string) $b['btn_secondary_text']),
            esc_attr((string) $b['btn_secondary_border']),
            (int) $b['btn_secondary_border_w'],
            (int) $b['btn_radius'],
            (int) $b['offset']
        );
        wp_add_inline_style('aieo-push-optin', $inline);

        
        $vapid_pub = '';
        if (class_exists('AIEO_Push_Sender')) {
            try {
                $keys = AIEO_Push_Sender::instance()->get_keys();
                $vapid_pub = (string) ($keys['public_b64url'] ?? '');
            } catch (\Throwable $e) {
                $vapid_pub = '';
            }
        }

        wp_localize_script('aieo-push-optin', 'aieoPush', array(
            'vapidPublicKey' => $vapid_pub,
            'swUrl'          => home_url('/aieo-push-sw.js'),
            'restRoot'       => esc_url_raw(rest_url()),
            'restNonce'      => wp_create_nonce('wp_rest'),
            'delayMs'        => (int) $b['delay_ms'],
            'debug'          => defined('WP_DEBUG') && WP_DEBUG,
            'banner'         => array(
                'position' => (string) $b['position'],
            ),
            'copy'           => array(
                'title'      => (string) $b['copy_title'],
                'body'       => (string) $b['copy_body'],
                'accept'     => (string) $b['copy_accept'],
                'dismiss'    => (string) $b['copy_dismiss'],
                'thanks_yes' => (string) $b['copy_thanks_yes'],
                'thanks_no'  => (string) $b['copy_thanks_no'],
            ),
            'thanksAutocloseMs' => (int) $b['thanks_autoclose_secs'] * 1000,
        ));
    }

    
    
    

    public function ajax_send_test() {
        AIEO_DMM::verify_ajax_request();

        if (!self::is_engine_live()) {
            wp_send_json_error(array('message' => self::engine_paused_message()));
        }

        global $wpdb;
        $user_id = get_current_user_id();
        if ($user_id <= 0) {
            wp_send_json_error(array('message' => __('No logged-in user.', 'ai-eshop-optimizer')));
        }

        $tbl = $wpdb->prefix . 'aieo_push_subscriptions';
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT subscription_id, endpoint, p256dh, auth_secret
             FROM {$tbl}
             WHERE user_id = %d AND status = 'active'",
            $user_id
        ), ARRAY_A);

        if (empty($rows)) {
            wp_send_json_error(array(
                'message' => __('No active subscription found for your account. Subscribe from the storefront first (the banner appears after 8s on the first visit), then come back and click here.', 'ai-eshop-optimizer'),
            ));
        }

        if (!class_exists('AIEO_Push_Sender')) {
            require_once AIEO_DIR . 'includes/dmm/push/class-aieo-push-sender.php';
        }
        $sender = AIEO_Push_Sender::instance();

        $sent = 0;
        $failed = 0;
        $details = array();
        $tbl_dispatch = $wpdb->prefix . 'aieo_push_dispatch';
        $now = current_time('mysql', true);

        foreach ($rows as $row) {
            $result = $sender->send_empty(array(
                'endpoint'    => $row['endpoint'],
                'p256dh'      => $row['p256dh'],
                'auth_secret' => $row['auth_secret'],
            ), 60);

            
            $wpdb->insert($tbl_dispatch, array(
                'subscription_id'  => (int) $row['subscription_id'],
                'user_id'          => $user_id,
                'trigger_id'       => 'admin_test',
                'target_url'       => home_url('/'),
                'state'            => $result['ok'] ? 'sent' : 'failed',
                'provider_status'  => (int) $result['status'],
                'provider_error'   => substr((string) $result['error'], 0, 200),
                'queued_at'        => $now,
                'sent_at'          => $result['ok'] ? $now : null,
                'created_at'       => $now,
            ), array('%d','%d','%s','%s','%s','%d','%s','%s','%s','%s'));

            if ($result['ok']) {
                $sent++;
                
            } else {
                $failed++;
                if ((int) $result['status'] === 410 || (int) $result['status'] === 404) {
                    $wpdb->update($tbl, array('status' => 'bounced', 'bounced_at' => $now),
                        array('subscription_id' => (int) $row['subscription_id']),
                        array('%s', '%s'), array('%d'));
                }
            }
            $details[] = array(
                'sub'    => (int) $row['subscription_id'],
                'status' => (int) $result['status'],
                'ok'     => (bool) $result['ok'],
                'error'  => (string) $result['error'],
            );
        }

        wp_send_json_success(array(
            'message' => sprintf(
                
                __('Test push: %1$d sent, %2$d failed.', 'ai-eshop-optimizer'),
                $sent,
                $failed
            ),
            'sent'    => $sent,
            'failed'  => $failed,
            'details' => $details,
        ));
    }

    
    
    

    
    public function find_latest_source_table() {
        global $wpdb;

        $temp = $wpdb->prefix . 'aieo_temp_orders';
        $temp_has_data = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$temp} WHERE EponymousID IS NOT NULL AND EponymousID > 0 LIMIT 1"
        );
        if ($temp_has_data > 0) {
            return $temp;
        }

        $rows = $wpdb->get_results(
            "SELECT TABLE_NAME, TABLE_ROWS
             FROM INFORMATION_SCHEMA.TABLES
             WHERE TABLE_SCHEMA = DATABASE()
               AND TABLE_NAME REGEXP '^{$wpdb->prefix}[0-9]{4}_[0-9]{2}_orders\$'
             ORDER BY TABLE_NAME DESC
             LIMIT 1",
            ARRAY_A
        );
        if (!empty($rows)) {
            return (string) $rows[0]['TABLE_NAME'];
        }
        
        $rows = $wpdb->get_results(
            "SELECT TABLE_NAME
             FROM INFORMATION_SCHEMA.TABLES
             WHERE TABLE_SCHEMA = DATABASE()
               AND TABLE_NAME REGEXP '^{$wpdb->prefix}aieo_[0-9]{4}_[0-9]{2}_orders\$'
             ORDER BY TABLE_NAME DESC
             LIMIT 1",
            ARRAY_A
        );
        if (!empty($rows)) {
            return (string) $rows[0]['TABLE_NAME'];
        }
        return null;
    }

    
    
    

    
    private function run_recompute($override_source = null, $start_offset = 0, $max_processed = null) {
        global $wpdb;
        $this->maybe_install();
        $tbl_ci = $wpdb->prefix . 'aieo_customer_intelligence';

        $source = $override_source !== null && $override_source !== ''
            ? (string) $override_source
            : (string) $this->find_latest_source_table();

        if ($source === '' || !$this->is_valid_source_table($source)) {
            return array(
                'ok'      => false,
                'source'  => '',
                'message' => __('No valid AIEO snapshot table found. Run the data-prep pipeline first (Data Prep → "Save Settings & Analyze Data").', 'ai-eshop-optimizer'),
            );
        }

        $batch           = self::RECOMPUTE_BATCH;
        $max             = $max_processed !== null ? (int) $max_processed : self::RECOMPUTE_MAX_PER_REQUEST;
        $offset          = max(0, (int) $start_offset);
        $started_at      = microtime(true);
        $rows_before     = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl_ci}");
        $batches_run     = 0;
        $processed_total = 0;

        while ($processed_total < $max) {
            $rows_at_step_before = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl_ci}");

            $wpdb->query($wpdb->prepare(
                'CALL aieo_sp_recompute_customer_intelligence_batch(%s, %d, %d)',
                $source,
                $offset,
                $batch
            ));

            $rows_at_step_after = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl_ci}");
            $delta              = max(0, $rows_at_step_after - $rows_at_step_before);
            $batches_run       += 1;
            $processed_total   += $batch;
            $offset            += $batch;

            if ($delta === 0 && $batches_run > 1) {
                break;
            }
        }

        
        
        $wpdb->query('CALL aieo_sp_assign_segments()');

        $rows_after = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl_ci}");
        $duration   = round(microtime(true) - $started_at, 2);

        return array(
            'ok'          => true,
            'source'      => $source,
            'rows_total'  => $rows_after,
            'rows_added'  => max(0, $rows_after - $rows_before),
            'batches_run' => $batches_run,
            'duration'    => $duration,
            'next_offset' => $offset,
            'message'     => sprintf(
                
                __('Recompute complete from %1$s — %2$d profiles (+%3$d new) in %4$ss. Segments assigned.', 'ai-eshop-optimizer'),
                $source,
                $rows_after,
                max(0, $rows_after - $rows_before),
                $duration
            ),
        );
    }

    
    public function ajax_recompute() {
        AIEO_DMM::verify_ajax_request();

        $offset   = isset($_POST['offset']) ? (int) wp_unslash($_POST['offset']) : 0;
        $override = isset($_POST['source_table'])
            ? sanitize_text_field(wp_unslash((string) $_POST['source_table']))
            : null;

        $result = $this->run_recompute($override, $offset);

        if (!$result['ok']) {
            wp_send_json_error(array('message' => $result['message']));
        }
        wp_send_json_success($result);
    }

    
    public function cron_nightly_recompute() {
        $result = $this->run_recompute(null, 0, 200000);
        update_option('aieo_push_last_nightly_at', time(), false);
        update_option('aieo_push_last_nightly_status', $result, false);
        if (defined('WP_DEBUG') && WP_DEBUG && function_exists('aieo_debug_log')) {
            aieo_debug_log('AIEO_Push nightly recompute: ' . wp_json_encode($result));
        }
    }

    
    private function is_valid_source_table($name) {
        global $wpdb;
        $name = (string) $name;
        if ($name === '' || strlen($name) > 64) { return false; }
        $temp_name = $wpdb->prefix . 'aieo_temp_orders';
        if ($name === $temp_name) { return true; }
        
        $regex = '/^' . preg_quote($wpdb->prefix . 'aieo_', '/') . '[0-9]{4}_[0-9]{2}_orders$/';
        return (bool) preg_match($regex, $name);
    }

    
    
    

    public function render_subtab() {
        global $wpdb;
        $tbl_ci = $wpdb->prefix . 'aieo_customer_intelligence';
        $exists = (string) $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $tbl_ci)) === $tbl_ci;

        echo '<div class="aieo-dmm-section">';
        echo '<h2>' . esc_html__('Web Push — Customer Intelligence', 'ai-eshop-optimizer') . '</h2>';
        echo '<p class="description">'
            . esc_html__('Per-customer CDP table sourced from AIEO\'s pre-built order snapshot (omnichannel: eshop + in-store). Push delivery + newsletter sync + lookalike export all wired to this single source of truth.', 'ai-eshop-optimizer')
            . '</p>';

        
        $this->render_engine_status_card();

        if (!$exists) {
            echo '<div class="aieo-dmm-card"><p>'
                . esc_html__('Intelligence table is being installed. Reload the page once to finish setup.', 'ai-eshop-optimizer')
                . '</p></div></div>';
            return;
        }

        
        $detected = (string) $this->find_latest_source_table();
        $candidates = $this->list_candidate_source_tables();

        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Source data', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 8px;">'
            . esc_html__('Recompute reads from one of AIEO\'s order snapshots — either the live working table or a monthly archive. Auto-pick prefers the freshest data.', 'ai-eshop-optimizer')
            . '</p>';
        if ($detected !== '') {
            echo '<p style="margin:0 0 8px;"><strong>' . esc_html__('Detected:', 'ai-eshop-optimizer') . '</strong> '
                . '<code>' . esc_html($detected) . '</code></p>';
        } else {
            echo '<p style="margin:0 0 8px;color:#b32d2e;">'
                . esc_html__('No source snapshot found — run the AIEO data-prep pipeline first.', 'ai-eshop-optimizer')
                . '</p>';
        }
        if (!empty($candidates)) {
            echo '<details style="margin-top:6px;"><summary style="cursor:pointer;color:#646970;">'
                . esc_html__('Available snapshots', 'ai-eshop-optimizer') . '</summary>';
            echo '<ul style="margin:6px 0 0 20px;list-style:disc;font-size:12px;">';
            foreach ($candidates as $c) {
                echo '<li><code>' . esc_html($c['name']) . '</code> — '
                    . esc_html(number_format_i18n((int) $c['rows'])) . ' '
                    . esc_html__('rows', 'ai-eshop-optimizer') . '</li>';
            }
            echo '</ul></details>';
        }

        
        echo '<div style="margin-top:14px;display:flex;align-items:center;gap:10px;flex-wrap:wrap;">';
        echo '<label for="aieo-webpush-source-table" style="font-size:12px;color:#374151;">'
            . esc_html__('Source:', 'ai-eshop-optimizer') . '</label>';
        echo '<select id="aieo-webpush-source-table" style="max-width:280px;">';
        echo '<option value="">' . esc_html__('Auto-pick (latest)', 'ai-eshop-optimizer') . '</option>';
        foreach ($candidates as $c) {
            $name = (string) $c['name'];
            $rows = (int) $c['rows'];
            $is_detected = ($name === $detected);
            echo '<option value="' . esc_attr($name) . '"'
                . ($is_detected ? ' data-detected="1"' : '') . '>'
                . esc_html($name) . ' — '
                . esc_html(number_format_i18n($rows)) . ' ' . esc_html__('rows', 'ai-eshop-optimizer')
                . ($is_detected ? ' ' . esc_html__('(latest)', 'ai-eshop-optimizer') : '')
                . '</option>';
        }
        echo '</select>';
        echo '<button type="button" class="button button-primary" id="aieo-webpush-recompute"'
            . ($detected === '' ? ' disabled' : '') . '>';
        echo esc_html__('Recompute now', 'ai-eshop-optimizer');
        echo '</button>';
        echo ' <span id="aieo-webpush-recompute-status" style="color:#646970;font-size:13px;"></span>';
        echo '</div>';

        
        $last_nightly = (int) get_option('aieo_push_last_nightly_at', 0);
        $next_cron    = (int) wp_next_scheduled(self::CRON_HOOK);
        echo '<p style="margin-top:10px;font-size:12px;color:#646970;">';
        echo '<strong>' . esc_html__('Nightly recompute:', 'ai-eshop-optimizer') . '</strong> ';
        echo esc_html__('runs at 02:30 Europe/Athens, chains segment assignment.', 'ai-eshop-optimizer');
        echo ' &middot; ';
        if ($last_nightly > 0) {
            echo esc_html__('Last:', 'ai-eshop-optimizer') . ' '
                . esc_html(human_time_diff($last_nightly)) . ' ' . esc_html__('ago', 'ai-eshop-optimizer');
        } else {
            echo esc_html__('Last: never', 'ai-eshop-optimizer');
        }
        if ($next_cron > 0) {
            echo ' &middot; ' . esc_html__('Next:', 'ai-eshop-optimizer') . ' '
                . esc_html(human_time_diff($next_cron));
        }
        echo '</p>';
        echo $this->recompute_inline_js(); 
        echo '</div>';

        
        $rows_total      = (int)   $wpdb->get_var("SELECT COUNT(*) FROM {$tbl_ci}");
        $with_orders     = (int)   $wpdb->get_var("SELECT COUNT(*) FROM {$tbl_ci} WHERE orders_count > 0");
        $with_wp_user    = (int)   $wpdb->get_var("SELECT COUNT(*) FROM {$tbl_ci} WHERE wp_user_id > 0");
        $sum_ltv         = (float) $wpdb->get_var("SELECT IFNULL(SUM(ltv_total),0) FROM {$tbl_ci}");
        $median_aov      = $this->median_aov($tbl_ci);
        $row_last_recomp = (string) $wpdb->get_var("SELECT MAX(updated_at) FROM {$tbl_ci}");

        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Overview', 'ai-eshop-optimizer') . '</h3>';
        echo '<div class="aieo-attr-tiles">';
        $this->tile(__('Profiles in CDP', 'ai-eshop-optimizer'), number_format_i18n($rows_total),
            __('Total rows in wp_aieo_customer_intelligence — every customer the CDP knows about. Includes WP users, guest checkouts, and identified anonymous sessions. Built nightly by Recompute.', 'ai-eshop-optimizer'));
        $this->tile(__('Linked to WP user', 'ai-eshop-optimizer'), number_format_i18n($with_wp_user),
            __('Subset of profiles that have a wp_users row (registered accounts). Guests / one-time checkouts are NOT included here, even though they ARE in the CDP.', 'ai-eshop-optimizer'));
        $this->tile(__('With ≥ 1 order', 'ai-eshop-optimizer'), number_format_i18n($with_orders),
            __('Profiles with orders_count ≥ 1. Excludes profiles built from sessions/wishlist alone with zero purchases — these are the addressable revenue base.', 'ai-eshop-optimizer'));
        $this->tile(__('Site-wide LTV', 'ai-eshop-optimizer'), $this->fmt_money($sum_ltv),
            __('Sum of ltv_total across every customer profile. Equivalent to total historical revenue across all known customers (excludes still-anonymous sessions).', 'ai-eshop-optimizer'));
        $this->tile(__('Median AOV', 'ai-eshop-optimizer'), $this->fmt_money($median_aov),
            __('Median per-customer AOV (middle value, not influenced by outliers like the 5,000 € whale). Use this as the typical-customer AOV; mean AOV is skewed high.', 'ai-eshop-optimizer'));
        $this->tile(__('Last recompute', 'ai-eshop-optimizer'),
            $row_last_recomp ? esc_html(human_time_diff(strtotime($row_last_recomp . ' UTC'))) . ' ' . esc_html__('ago', 'ai-eshop-optimizer') : '—',
            __('Most recent updated_at in customer_intelligence. Stale = > 24h since the nightly Recompute cron at 02:30 Athens. Click "Recompute now" to refresh manually.', 'ai-eshop-optimizer'));
        echo '</div></div>';
        echo '</div>';

        
        
        
        
        
        $card_anchor = function ($slug) {
            echo '<span id="aieo-card-' . esc_attr($slug) . '" style="display:block;height:0;margin-top:-24px;padding-top:24px;"></span>';
        };

        $card_anchor('subscribers');
        $this->render_subscribers_card();
        $card_anchor('newsletter-settings');
        $this->render_newsletter_settings_card();
        $card_anchor('campaign-launcher');
        $this->render_campaign_launcher_card();
        
        
        
        echo '<div class="aieo-dmm-section"><p class="description" style="font-size:12px;">';
        echo wp_kses_post(sprintf(
            
            __('Looking for the opt-in banner styling (position, delay, colors, copy, thank-you toast)? It now lives under %1$sVisual Display → Web Push Banner Appearance%2$s.', 'ai-eshop-optimizer'),
            '<a href="' . esc_url(add_query_arg(array('page' => AIEO_DMM::MENU_SLUG, 'dmm_tab' => 'web_push_display'), admin_url('admin.php'))) . '">',
            '</a>'
        ));
        echo '</p></div>';
        $card_anchor('segments');
        $this->render_segments_card($tbl_ci);
        $card_anchor('reactivation');
        $this->render_reactivation_card($tbl_ci);
        $card_anchor('lost-cohort');
        $this->render_lost_cohort_playbook_card($tbl_ci);
        $card_anchor('lookalike-export');
        $this->render_lookalike_export_card($tbl_ci);
        $card_anchor('top-customers');
        $this->render_top_customers_card($tbl_ci);

        
        $card_anchor('triggers');
        $this->render_triggers_card();
        $card_anchor('dispatch-log');
        $this->render_dispatch_log_card();
        $this->render_templates_card();
    }

    
    private function render_triggers_card() {
        global $wpdb;
        $tbl_d = $wpdb->prefix . 'aieo_push_dispatch';

        $triggers = class_exists('AIEO_Push_Trigger_Registry')
            ? AIEO_Push_Trigger_Registry::instance()->all()
            : array();

        $last_tick = (array) get_option('aieo_push_last_dispatch_tick', array());
        $next_tick = (int) wp_next_scheduled(self::CRON_DISPATCH_HOOK);

        echo '<div class="aieo-dmm-card" style="background:#eff6ff;border-left:3px solid #1d4ed8;">';
        echo '<h3>' . esc_html__('Triggers — Phase 4 + 5', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 14px;font-size:12px;color:#1e3a8a;">'
            . esc_html__('Each trigger has its own candidate-producing query; the universal dispatcher (every 5 min) runs caps + quiet hours + 7-day dedup + template pick + AES-128-GCM encrypted send. Click counts come from the SW beacon → push_dispatch update.', 'ai-eshop-optimizer')
            . '</p>';

        
        echo '<p style="margin:0 0 12px;font-size:12px;color:#374151;">';
        if (!empty($last_tick['at'])) {
            echo '<strong>' . esc_html__('Last tick:', 'ai-eshop-optimizer') . '</strong> '
                . esc_html(human_time_diff((int) $last_tick['at']))
                . ' ' . esc_html__('ago', 'ai-eshop-optimizer');
            if (!empty($last_tick['stats'])) {
                $s = $last_tick['stats'];
                echo ' — ' . sprintf(
                    
                    esc_html__('%1$d processed, %2$d sent, %3$d suppressed, %4$d failed', 'ai-eshop-optimizer'),
                    (int) ($s['processed'] ?? 0), (int) ($s['sent'] ?? 0),
                    (int) ($s['suppressed'] ?? 0), (int) ($s['failed'] ?? 0)
                );
            }
        } else {
            echo esc_html__('Last tick: never (cron will run within 5 minutes)', 'ai-eshop-optimizer');
        }
        if ($next_tick > 0) {
            echo ' &middot; ' . esc_html__('Next:', 'ai-eshop-optimizer') . ' '
                . esc_html(human_time_diff($next_tick));
        }
        echo '</p>';

        
        $agg = $wpdb->get_results(
            "SELECT trigger_id,
                    SUM(IF(state IN ('sent','clicked','dismissed'), 1, 0)) AS sent,
                    SUM(IF(state = 'clicked', 1, 0))                       AS clicked,
                    SUM(IF(state = 'suppressed', 1, 0))                    AS suppressed,
                    SUM(IF(state = 'failed', 1, 0))                        AS failed,
                    MAX(sent_at) AS last_sent_at
             FROM {$tbl_d}
             GROUP BY trigger_id",
            ARRAY_A
        );
        $by_id = array();
        foreach ($agg as $r) { $by_id[(string) $r['trigger_id']] = $r; }

        if (empty($triggers)) {
            echo '<p style="font-size:12px;color:#9ca3af;">' . esc_html__('No triggers registered yet.', 'ai-eshop-optimizer') . '</p>';
            echo '</div>';
            return;
        }

        echo '<table class="widefat striped" style="font-size:12px;">';
        echo '<thead><tr>';
        echo '<th>' . esc_html__('Trigger', 'ai-eshop-optimizer') . '</th>';
        echo '<th style="text-align:right;">' . esc_html__('Sent', 'ai-eshop-optimizer') . '</th>';
        echo '<th style="text-align:right;">' . esc_html__('Clicked', 'ai-eshop-optimizer') . '</th>';
        echo '<th style="text-align:right;">' . esc_html__('CTR', 'ai-eshop-optimizer') . '</th>';
        echo '<th style="text-align:right;">' . esc_html__('Suppressed', 'ai-eshop-optimizer') . '</th>';
        echo '<th style="text-align:right;">' . esc_html__('Failed', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Last sent', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Caps · dedup', 'ai-eshop-optimizer') . '</th>';
        echo '</tr></thead><tbody>';
        foreach ($triggers as $tid => $trigger) {
            $a = $by_id[$tid] ?? array('sent'=>0,'clicked'=>0,'suppressed'=>0,'failed'=>0,'last_sent_at'=>null);
            $sent_n   = (int) $a['sent'];
            $click_n  = (int) $a['clicked'];
            $ctr      = $sent_n > 0 ? round(100 * $click_n / $sent_n, 1) : 0;
            echo '<tr>';
            echo '<td><strong>' . esc_html($trigger->get_label()) . '</strong>';
            echo ' <code style="font-size:11px;color:#6b7280;">' . esc_html($tid) . '</code></td>';
            echo '<td style="text-align:right;">' . esc_html(number_format_i18n($sent_n)) . '</td>';
            echo '<td style="text-align:right;">' . esc_html(number_format_i18n($click_n)) . '</td>';
            echo '<td style="text-align:right;">' . esc_html($ctr) . '%</td>';
            echo '<td style="text-align:right;color:#9ca3af;">' . esc_html(number_format_i18n((int) $a['suppressed'])) . '</td>';
            echo '<td style="text-align:right;color:' . ((int) $a['failed'] > 0 ? '#b91c1c' : '#9ca3af') . ';">'
                . esc_html(number_format_i18n((int) $a['failed'])) . '</td>';
            echo '<td>' . ($a['last_sent_at'] ? esc_html(human_time_diff(strtotime((string) $a['last_sent_at'] . ' UTC')) . ' ' . __('ago', 'ai-eshop-optimizer')) : '—') . '</td>';
            echo '<td style="color:#6b7280;">' . esc_html(sprintf(
                
                __('%1$d/d · %2$d/wk · %3$dd dedup', 'ai-eshop-optimizer'),
                $trigger->cap_per_day(), $trigger->cap_per_week(), $trigger->dedup_days()
            )) . '</td>';
            echo '</tr>';
        }
        echo '</tbody></table>';
        echo '</div>';
    }

    
    private function render_dispatch_log_card() {
        global $wpdb;
        $tbl_d = $wpdb->prefix . 'aieo_push_dispatch';
        $rows = $wpdb->get_results(
            "SELECT dispatch_id, trigger_id, target_product_id, target_url,
                    state, suppress_reason, provider_status, provider_error,
                    queued_at, sent_at, clicked_at
             FROM {$tbl_d}
             ORDER BY dispatch_id DESC
             LIMIT 100",
            ARRAY_A
        );

        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Dispatch log — last 100', 'ai-eshop-optimizer') . '</h3>';
        if (empty($rows)) {
            echo '<p style="font-size:12px;color:#9ca3af;">'
                . esc_html__('No dispatch rows yet — first push will appear here.', 'ai-eshop-optimizer') . '</p>';
            echo '</div>';
            return;
        }
        echo '<table class="widefat striped" style="font-size:12px;">';
        echo '<thead><tr>';
        echo '<th>#</th>';
        echo '<th>' . esc_html__('Trigger', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Product', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('State', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Reason', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('When', 'ai-eshop-optimizer') . '</th>';
        echo '</tr></thead><tbody>';

        $color_for = array(
            'sent'       => '#0d9488',
            'clicked'    => '#15803d',
            'dismissed'  => '#6b7280',
            'queued'     => '#1d4ed8',
            'suppressed' => '#9ca3af',
            'failed'     => '#b91c1c',
        );
        foreach ($rows as $r) {
            $state = (string) $r['state'];
            $color = $color_for[$state] ?? '#374151';
            $when  = $r['sent_at'] ?: ($r['clicked_at'] ?: $r['queued_at']);
            $when_human = $when ? human_time_diff(strtotime((string) $when . ' UTC')) . ' ' . __('ago', 'ai-eshop-optimizer') : '—';
            echo '<tr>';
            echo '<td>' . (int) $r['dispatch_id'] . '</td>';
            echo '<td><code>' . esc_html((string) $r['trigger_id']) . '</code></td>';
            echo '<td><a href="' . esc_url((string) $r['target_url']) . '" target="_blank" rel="noopener">#'
                . (int) $r['target_product_id'] . '</a></td>';
            echo '<td><span style="color:' . esc_attr($color) . ';font-weight:600;">' . esc_html($state) . '</span></td>';
            echo '<td style="font-size:11px;color:#6b7280;">';
            if ($state === 'suppressed') { echo esc_html((string) $r['suppress_reason']); }
            elseif ($state === 'failed')  { echo esc_html((int) $r['provider_status']) . ' ' . esc_html((string) $r['provider_error']); }
            else { echo '&mdash;'; }
            echo '</td>';
            echo '<td>' . esc_html($when_human) . '</td>';
            echo '</tr>';
        }
        echo '</tbody></table>';
        echo '</div>';
    }

    
    private function render_templates_card() {
        global $wpdb;
        $tbl = $wpdb->prefix . 'aieo_push_templates';
        $rows = $wpdb->get_results(
            "SELECT template_id, trigger_id, segment_id, variant_label, weight,
                    title_template, body_template, icon_url, is_active
             FROM {$tbl}
             ORDER BY trigger_id, segment_id, variant_label",
            ARRAY_A
        );

        echo '<div class="aieo-dmm-card" style="background:#fef3c7;border-left:3px solid #d97706;">';
        echo '<h3>' . esc_html__('Templates (Phase 6) — per-trigger, per-segment, weighted variants', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 12px;font-size:12px;color:#78350f;">'
            . esc_html__('Tokens like {{product_name}} {{drop_pct}} {{current_price}} get filled from each candidate\'s payload at send time. Multiple variants per (trigger, segment) row are weight-randomised by the dispatcher; segment="*" matches everyone. Edit copy below — AJAX saves on blur.', 'ai-eshop-optimizer')
            . '</p>';

        if (empty($rows)) {
            echo '<p style="font-size:12px;color:#9ca3af;">'
                . esc_html__('No templates yet — first plugin load will seed defaults.', 'ai-eshop-optimizer') . '</p>';
            echo '</div>';
            return;
        }

        echo '<table class="widefat striped aieo-tpl-table" style="font-size:12px;">';
        echo '<thead><tr>';
        echo '<th>' . esc_html__('Trigger', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Segment', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Variant', 'ai-eshop-optimizer') . '</th>';
        echo '<th style="width:80px;">' . esc_html__('Weight', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Title', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Body', 'ai-eshop-optimizer') . '</th>';
        echo '<th style="width:60px;">' . esc_html__('Active', 'ai-eshop-optimizer') . '</th>';
        echo '</tr></thead><tbody>';

        foreach ($rows as $r) {
            $tid = (int) $r['template_id'];
            $row_attr = ' data-template-id="' . esc_attr($tid) . '"';
            echo '<tr' . $row_attr . '>'; 
            echo '<td><code>' . esc_html((string) $r['trigger_id']) . '</code></td>';
            echo '<td><code>' . esc_html((string) $r['segment_id']) . '</code></td>';
            echo '<td>' . esc_html((string) $r['variant_label']) . '</td>';
            echo '<td><input type="number" min="1" max="100" class="aieo-tpl-input small-text" data-field="weight" value="' . esc_attr((int) $r['weight']) . '"></td>';
            echo '<td><input type="text" class="aieo-tpl-input regular-text" data-field="title_template" style="width:100%;" value="' . esc_attr((string) $r['title_template']) . '"></td>';
            echo '<td><input type="text" class="aieo-tpl-input regular-text" data-field="body_template"  style="width:100%;" value="' . esc_attr((string) $r['body_template']) . '"></td>';
            echo '<td style="text-align:center;"><input type="checkbox" class="aieo-tpl-input" data-field="is_active"' . (((int) $r['is_active']) ? ' checked' : '') . '></td>';
            echo '</tr>';
        }
        echo '</tbody></table>';

        echo '<p style="margin:8px 0 0;font-size:11px;color:#6b7280;" id="aieo-tpl-status">'
            . esc_html__('Tip: edits autosave on blur (or change for checkbox). Field name is shown via the data-field attribute.', 'ai-eshop-optimizer')
            . '</p>';

        
        $nonce = wp_create_nonce(class_exists('AIEO_DMM') ? \AIEO_DMM::NONCE_ACTION : 'aieo_dmm_nonce');
        echo '<script>(function(){
            var status = document.getElementById("aieo-tpl-status");
            var save = function(input){
                var row = input.closest("tr");
                if (!row) return;
                var id = parseInt(row.getAttribute("data-template-id"), 10) || 0;
                var field = input.getAttribute("data-field");
                var value = (input.type === "checkbox") ? (input.checked ? 1 : 0) : input.value;
                var fd = new FormData();
                fd.append("action", "aieo_dmm_webpush_save_template");
                fd.append("nonce", "' . esc_js($nonce) . '");
                fd.append("template_id", id);
                fd.append("field", field);
                fd.append("value", value);
                if (status) status.textContent = "Saving…";
                fetch(ajaxurl, {method:"POST", credentials:"same-origin", body:fd})
                    .then(function(r){return r.json();})
                    .then(function(j){
                        if (status) status.textContent = (j && j.success) ? "Saved." : ("Error: " + (j && j.data && j.data.message || "unknown"));
                    })
                    .catch(function(){ if (status) status.textContent = "Network error."; });
            };
            document.querySelectorAll(".aieo-tpl-input").forEach(function(inp){
                if (inp.type === "checkbox") {
                    inp.addEventListener("change", function(){ save(inp); });
                } else {
                    inp.addEventListener("blur", function(){ save(inp); });
                }
            });
        })();</script>';

        echo '</div>';
    }

    
    private function render_reactivation_card($tbl_ci) {
        global $wpdb;

        $tbl_subs = $wpdb->prefix . 'aieo_push_subscriptions';
        $cohorts  = self::reactivation_cohort_catalog();

        
        
        
        $cohort_rows = $wpdb->get_results(
            "SELECT
                ci.dormancy_cohort,
                COUNT(*) AS cnt,
                IFNULL(SUM(ci.ltv_total), 0)            AS sum_ltv,
                IFNULL(AVG(ci.reactivation_score), 0)   AS avg_score,
                SUM(CASE WHEN ci.reactivation_score >= 50 THEN 1 ELSE 0 END) AS high_score,
                SUM(CASE WHEN ci.wp_user_id > 0 AND EXISTS (
                    SELECT 1 FROM {$tbl_subs} sub
                    WHERE sub.user_id = ci.wp_user_id AND sub.status = 'active'
                ) THEN 1 ELSE 0 END)                                       AS push_reachable,
                SUM(CASE WHEN ci.reactivation_attempts_count > 0 THEN 1 ELSE 0 END) AS attempted,
                SUM(CASE WHEN ci.reactivated_at IS NOT NULL THEN 1 ELSE 0 END)      AS reactivated
             FROM {$tbl_ci} ci
             WHERE ci.dormancy_cohort <> ''
             GROUP BY ci.dormancy_cohort",
            ARRAY_A
        );
        $stats_by_cohort = array();
        foreach ($cohort_rows as $r) {
            $stats_by_cohort[$r['dormancy_cohort']] = $r;
        }

        
        $total_dormant = 0; $high_score_count = 0; $push_reachable = 0;
        $attempted = 0; $reactivated = 0; $recoverable_ltv = 0.0;
        foreach ($stats_by_cohort as $r) {
            $total_dormant    += (int) $r['cnt'];
            $high_score_count += (int) $r['high_score'];
            $push_reachable   += (int) $r['push_reachable'];
            $attempted        += (int) $r['attempted'];
            $reactivated      += (int) $r['reactivated'];
        }
        $recoverable_ltv = (float) $wpdb->get_var(
            "SELECT IFNULL(SUM(ltv_total), 0) FROM {$tbl_ci}
             WHERE dormancy_cohort <> '' AND reactivation_score >= 50"
        );

        echo '<div class="aieo-dmm-section">';
        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Reactivation opportunity', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 10px;">'
            . esc_html__('The dormant pool is the biggest single revenue lever in a mature store. We split it into four lifecycle cohorts and score every customer 0–100 by how recoverable they are. The win-back trigger (Phase 5) walks this queue in score order with cohort-specific message strength.', 'ai-eshop-optimizer')
            . '</p>';

        
        echo '<div class="aieo-attr-tiles">';
        $this->tile(__('Total dormant', 'ai-eshop-optimizer'),       number_format_i18n($total_dormant),
            __('Customers with no order in the dormancy window (typically 60+ days inactive). The full pool from which the win-back trigger picks targets.', 'ai-eshop-optimizer'));
        $this->tile(__('Recoverable LTV', 'ai-eshop-optimizer'),     $this->fmt_money($recoverable_ltv),
            __('Sum of historical LTV across all dormant customers — the upper bound of revenue that could re-flow if every dormant customer reactivated. Aspirational, not forecast.', 'ai-eshop-optimizer'));
        $this->tile(__('High-score targets', 'ai-eshop-optimizer'),  number_format_i18n($high_score_count),
            __('Dormant customers whose reactivation_score is high enough to be a worthwhile push target. Score blends recency, LTV, prior frequency, sentiment.', 'ai-eshop-optimizer'));
        $this->tile(__('Push-reachable (active sub)', 'ai-eshop-optimizer'), number_format_i18n($push_reachable),
            __('High-score targets who ALSO have an active push subscription right now. The actual addressable count for the next dispatch tick — usually << High-score targets early on.', 'ai-eshop-optimizer'));
        echo '</div>';

        
        $this->render_reactivation_funnel($high_score_count, $push_reachable, $attempted, $reactivated);

        
        $this->render_cohort_funnel_table($cohorts, $stats_by_cohort);

        
        $this->render_reactivation_candidates_table($tbl_ci);

        echo '</div></div>';
    }

    
    private function render_reactivation_funnel($targets, $reachable, $attempted, $reactivated) {
        $stages = array(
            array('label' => __('High-score targets', 'ai-eshop-optimizer'),    'value' => $targets,     'color' => '#0891b2'),
            array('label' => __('Push-reachable',     'ai-eshop-optimizer'),    'value' => $reachable,   'color' => '#7c3aed'),
            array('label' => __('Attempted',          'ai-eshop-optimizer'),    'value' => $attempted,   'color' => '#f59e0b'),
            array('label' => __('Reactivated',        'ai-eshop-optimizer'),    'value' => $reactivated, 'color' => '#15803d'),
        );
        $top = max(1, $targets); 
        $attempt_rate     = $reachable > 0   ? round(100 * $attempted   / $reachable,   1) : 0;
        $reactivation_rate = $attempted > 0  ? round(100 * $reactivated / $attempted,   1) : 0;
        $overall_rate      = $targets > 0    ? round(100 * $reactivated / $targets,     2) : 0;

        echo '<h4 style="margin:18px 0 8px;font-size:13px;">' . esc_html__('Reactivation funnel', 'ai-eshop-optimizer') . '</h4>';
        echo '<p class="description" style="margin:0 0 10px;font-size:12px;">'
            . esc_html__('Live pipeline state. The "Attempted" and "Reactivated" stages stay at 0 until Phase 5 (win-back trigger) runs its first dispatch.', 'ai-eshop-optimizer')
            . '</p>';

        echo '<div style="display:flex;gap:8px;margin-bottom:8px;">';
        foreach ($stages as $i => $st) {
            $pct = $top > 0 ? max(2, round(100 * $st['value'] / $top, 1)) : 2;
            echo '<div style="flex:1;background:#f3f4f6;border-radius:6px;overflow:hidden;position:relative;height:64px;">';
            echo '<div style="position:absolute;inset:0 ' . esc_attr((string) (100 - $pct)) . '% 0 0;background:' . esc_attr($st['color']) . ';opacity:0.85;"></div>';
            echo '<div style="position:relative;padding:8px 12px;color:#111827;">';
            echo '<div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#374151;">' . esc_html($st['label']) . '</div>';
            echo '<div style="font-size:18px;font-weight:700;line-height:1.2;color:' . ($pct > 25 ? '#fff' : '#111827') . ';">' . esc_html(number_format_i18n($st['value'])) . '</div>';
            echo '</div>';
            echo '</div>';
            if ($i < count($stages) - 1) {
                echo '<div style="display:flex;align-items:center;color:#9ca3af;font-size:18px;">→</div>';
            }
        }
        echo '</div>';

        echo '<p style="margin:6px 0 0;font-size:12px;color:#646970;">'
            . sprintf(
                
                esc_html__('Attempt rate (Attempted ÷ Reachable): %1$s%% · Reactivation rate (Reactivated ÷ Attempted): %2$s%% · Overall conversion (Reactivated ÷ Targets): %3$s%%', 'ai-eshop-optimizer'),
                esc_html((string) $attempt_rate),
                esc_html((string) $reactivation_rate),
                esc_html((string) $overall_rate)
            )
            . '</p>';
    }

    
    private function render_cohort_funnel_table($cohorts, $stats_by_cohort) {
        echo '<h4 style="margin:18px 0 6px;font-size:13px;">' . esc_html__('Cohorts — sizing, channel reach, funnel', 'ai-eshop-optimizer') . '</h4>';
        echo '<table class="widefat striped aieo-attr-sortable-table">';
        echo '<thead><tr>';
        echo '<th data-sort-type="text">' . esc_html__('Cohort', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th>' . esc_html__('Strategy', 'ai-eshop-optimizer') . '</th>';
        echo '<th data-sort-type="num">' . esc_html__('Total', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('Pool LTV', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('Avg score', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('High-score', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('Push reach', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('Attempted', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('Reactivated', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('Conv %', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '</tr></thead><tbody>';

        foreach ($cohorts as $slug => $info) {
            $s = isset($stats_by_cohort[$slug]) ? $stats_by_cohort[$slug] : array();
            $cnt        = (int)   ($s['cnt']            ?? 0);
            $sum_ltv    = (float) ($s['sum_ltv']        ?? 0);
            $avg_score  = (float) ($s['avg_score']      ?? 0);
            $high       = (int)   ($s['high_score']     ?? 0);
            $reach      = (int)   ($s['push_reachable'] ?? 0);
            $attempted  = (int)   ($s['attempted']      ?? 0);
            $reacted    = (int)   ($s['reactivated']    ?? 0);
            $conv_pct   = $attempted > 0 ? round(100 * $reacted / $attempted, 1) : 0;
            $reach_pct  = $high > 0 ? round(100 * $reach / $high, 0) : 0;

            echo '<tr>';
            echo '<td data-sort-value="' . esc_attr($slug) . '">';
            echo '<strong>' . esc_html($info['label']) . '</strong>';
            echo '<br><code style="font-size:11px;color:#646970;">' . esc_html($slug) . '</code>';
            echo '<div style="font-size:11px;color:#646970;margin-top:2px;">' . esc_html($info['recency']) . '</div>';
            echo '</td>';
            echo '<td style="font-size:12px;color:#374151;">' . esc_html($info['strategy']) . '</td>';
            echo '<td data-sort-value="' . $cnt . '">' . esc_html(number_format_i18n($cnt)) . '</td>'; 
            echo '<td data-sort-value="' . $sum_ltv . '">' . esc_html($this->fmt_money($sum_ltv)) . '</td>'; 
            echo '<td data-sort-value="' . $avg_score . '">' . esc_html(number_format_i18n($avg_score, 1)) . '</td>'; 
            echo '<td data-sort-value="' . $high . '"><strong>' . esc_html(number_format_i18n($high)) . '</strong></td>'; 
            echo '<td data-sort-value="' . $reach . '">' . esc_html(number_format_i18n($reach)); 
            if ($reach_pct > 0) {
                echo ' <small style="color:#646970;">(' . esc_html((string) $reach_pct) . '%)</small>';
            }
            echo '</td>';
            echo '<td data-sort-value="' . $attempted . '">' . esc_html(number_format_i18n($attempted)) . '</td>'; 
            echo '<td data-sort-value="' . $reacted . '">' . esc_html(number_format_i18n($reacted)) . '</td>'; 
            echo '<td data-sort-value="' . $conv_pct . '">' . esc_html((string) $conv_pct) . '%</td>'; 
            echo '</tr>';
        }
        echo '</tbody></table>';
        echo '<p style="margin:6px 0 0;font-size:11px;color:#9ca3af;">'
            . esc_html__('"Push reach" = high-score candidates whose linked WP user has an active push subscription. The percentage in parentheses is the share of the high-score pool reachable today.', 'ai-eshop-optimizer')
            . '</p>';
    }

    private function render_reactivation_candidates_table($tbl_ci) {
        global $wpdb;
        $candidates = $wpdb->get_results(
            "SELECT eponymous_id, wp_user_id, email, ltv_total, orders_count, recency_days,
                    top_brand_id, dormancy_cohort, reactivation_score,
                    reactivation_attempts_count, last_reactivation_attempt_at, reactivated_at,
                    JSON_VALUE(replenishment_json, '$.avg_dbo') AS avg_dbo
             FROM {$tbl_ci}
             WHERE dormancy_cohort <> ''
               AND reactivated_at IS NULL
             ORDER BY reactivation_score DESC, ltv_total DESC
             LIMIT 50",
            ARRAY_A
        );

        echo '<h4 style="margin:22px 0 6px;font-size:13px;">' . esc_html__('Top reactivation candidates', 'ai-eshop-optimizer') . '</h4>';
        echo '<p class="description" style="margin:0 0 8px;">'
            . esc_html__('The 50 highest-scoring dormants who have not yet been reactivated. The win-back trigger walks this queue in score order — high-LTV recently-drifted customers first, long-lost low-spenders last.', 'ai-eshop-optimizer')
            . '</p>';

        if (empty($candidates)) {
            echo '<p style="color:#646970;">'
                . esc_html__('No dormant candidates yet. Run "Recompute now" first.', 'ai-eshop-optimizer')
                . '</p>';
            return;
        }

        $vitals_table = $wpdb->prefix . 'aieo_core_product_vitals';
        $cohorts = self::reactivation_cohort_catalog();

        echo '<table class="widefat striped aieo-attr-sortable-table">';
        echo '<thead><tr>';
        echo '<th style="width:50px;">#</th>';
        echo '<th data-sort-type="num">' . esc_html__('Score', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="text">' . esc_html__('Cohort', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="text">' . esc_html__('Customer', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('LTV', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('Orders', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('Recency (d)', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="text">' . esc_html__('Top brand', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('Attempts', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '</tr></thead><tbody>';

        foreach ($candidates as $i => $r) {
            $epo       = (int) $r['eponymous_id'];
            $wp_uid    = (int) $r['wp_user_id'];
            $email     = (string) $r['email'];
            $score     = (int) $r['reactivation_score'];
            $cohort    = (string) $r['dormancy_cohort'];
            $cohort_lbl = isset($cohorts[$cohort]) ? $cohorts[$cohort]['label'] : $cohort;
            $cohort_color = isset($cohorts[$cohort]) ? $cohorts[$cohort]['color'] : '#6b7280';
            $brand_id  = (int) $r['top_brand_id'];
            $brand_name = $brand_id > 0 ? $this->lookup_brand_name($brand_id, $vitals_table) : '';
            $edit_url  = $wp_uid > 0 ? admin_url('user-edit.php?user_id=' . $wp_uid) : '';
            $display   = $email !== '' ? $email : ('#' . $epo);
            $attempts  = (int) $r['reactivation_attempts_count'];

            echo '<tr>';
            echo '<td>' . (int) ($i + 1) . '</td>';
            echo '<td data-sort-value="' . $score . '"><strong style="color:' . esc_attr($this->score_color($score)) . ';">' . $score . '</strong></td>'; 
            echo '<td data-sort-value="' . esc_attr($cohort) . '">';
            echo '<span style="display:inline-block;padding:2px 7px;border-radius:10px;background:' . esc_attr($cohort_color) . ';color:#fff;font-size:10px;font-weight:600;">' . esc_html($cohort_lbl) . '</span>';
            echo '</td>';
            echo '<td data-sort-value="' . esc_attr($display) . '">';
            if ($edit_url !== '') {
                echo '<strong><a href="' . esc_url($edit_url) . '">' . esc_html($display) . '</a></strong>';
            } else {
                echo '<strong>' . esc_html($display) . '</strong>';
            }
            echo '<br><small style="color:#646970;">epo: ' . $epo . '</small>'; 
            echo '</td>';
            echo '<td data-sort-value="' . esc_attr((string) $r['ltv_total']) . '">' . esc_html($this->fmt_money((float) $r['ltv_total'])) . '</td>';
            echo '<td data-sort-value="' . (int) $r['orders_count'] . '">' . esc_html(number_format_i18n((int) $r['orders_count'])) . '</td>';
            echo '<td data-sort-value="' . (int) $r['recency_days'] . '">' . esc_html(number_format_i18n((int) $r['recency_days'])) . '</td>';
            echo '<td data-sort-value="' . esc_attr($brand_name) . '">' . esc_html($brand_name !== '' ? $brand_name : '—') . '</td>';
            echo '<td data-sort-value="' . $attempts . '">' . esc_html(number_format_i18n($attempts)) . '</td>'; 
            echo '</tr>';
        }
        echo '</tbody></table>';
    }

    
    private function score_color($score) {
        $s = (int) $score;
        if ($s >= 75) { return '#15803d'; } 
        if ($s >= 50) { return '#0891b2'; } 
        if ($s >= 25) { return '#f59e0b'; } 
        return '#9ca3af';                    
    }

    
    private function render_newsletter_settings_card() {
        if (!class_exists('AIEO_Newsletter_Registry')) {
            return;
        }
        $reg = AIEO_Newsletter_Registry::instance();
        $cat = $reg->catalogue();
        $s   = $reg->get_settings();
        $current_provider = (string) $s['provider'];

        echo '<div class="aieo-dmm-section">';
        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Newsletter integration', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 10px;">'
            . esc_html__('Connect AIEO to your email/newsletter platform so cohort syncs and campaigns run through it. Choose a provider, paste the API credentials, and Test connection. Once connected, the "Sync to newsletter" button on each cohort pushes contacts with cohort tags AutomateWoo / your campaign builder can target.', 'ai-eshop-optimizer')
            . '</p>';

        echo '<form id="aieo-dmm-newsletter-form" class="aieo-dmm-form">';
        echo '<table class="form-table"><tr>';
        echo '<th>' . esc_html__('Provider', 'ai-eshop-optimizer') . '</th>';
        echo '<td><select name="provider" id="aieo-newsletter-provider">';
        foreach ($cat as $slug => $info) {
            echo '<option value="' . esc_attr($slug) . '"' . selected($current_provider, $slug, false) . '>' . esc_html($info['label']) . '</option>';
        }
        echo '</select></td></tr></table>';

        
        foreach ($cat as $slug => $info) {
            if ($slug === '' || empty($info['class']) || !class_exists($info['class'])) { continue; }
            $instance = new $info['class'](is_array($s[$slug] ?? null) ? $s[$slug] : array());
            $hidden = $current_provider === $slug ? '' : ' style="display:none;"';
            echo '<div class="aieo-newsletter-provider-fields" data-provider="' . esc_attr($slug) . '"' . $hidden . '>'; 
            echo '<h4 style="margin:14px 0 6px;font-size:13px;">' . esc_html($info['label']) . '</h4>';
            echo '<table class="form-table">';
            foreach ($instance->get_settings_fields() as $field) {
                $key   = (string) $field['key'];
                $type  = (string) $field['type'];
                $label = (string) $field['label'];
                $val   = (string) ($s[$slug][$key] ?? '');
                echo '<tr><th>' . esc_html($label) . '</th><td>';
                if ($type === 'password') {
                    echo '<input type="password" name="' . esc_attr($slug) . '[' . esc_attr($key) . ']" value="' . esc_attr($val) . '" class="regular-text" autocomplete="off">';
                } else {
                    echo '<input type="text" name="' . esc_attr($slug) . '[' . esc_attr($key) . ']" value="' . esc_attr($val) . '" class="regular-text">';
                }
                if (!empty($field['description'])) {
                    echo '<p class="description">' . esc_html((string) $field['description']) . '</p>';
                }
                echo '</td></tr>';
            }
            echo '</table>';
            echo '</div>';
        }

        echo '<p class="submit">';
        echo '<button type="submit" class="button button-primary aieo-dmm-btn-fixed" data-action="aieo_dmm_webpush_save_newsletter">';
        echo esc_html__('Save provider settings', 'ai-eshop-optimizer');
        echo '</button> ';
        echo '<button type="button" class="button button-secondary" id="aieo-newsletter-test"' . ($current_provider === '' ? ' disabled' : '') . '>';
        echo esc_html__('Test connection', 'ai-eshop-optimizer');
        echo '</button>';
        echo ' <span id="aieo-newsletter-test-status" style="margin-left:10px;font-size:13px;color:#646970;"></span>';
        echo '</p>';
        echo '</form>';

        
        $this->render_newsletter_sync_log_table();

        
        $nonce = wp_create_nonce(AIEO_DMM::NONCE_ACTION);
        $ajax  = admin_url('admin-ajax.php');
        ?>
        <script>
        (function(){
            const sel = document.getElementById('aieo-newsletter-provider');
            const testBtn = document.getElementById('aieo-newsletter-test');
            const testStatus = document.getElementById('aieo-newsletter-test-status');
            if (sel) {
                sel.addEventListener('change', function(){
                    document.querySelectorAll('.aieo-newsletter-provider-fields').forEach(el => {
                        el.style.display = (el.getAttribute('data-provider') === sel.value) ? '' : 'none';
                    });
                    if (testBtn) { testBtn.disabled = sel.value === ''; }
                });
            }
            if (testBtn) {
                testBtn.addEventListener('click', async function(){
                    testBtn.disabled = true;
                    testStatus.style.color = '#646970';
                    testStatus.textContent = '<?php echo esc_js(__('Testing…', 'ai-eshop-optimizer')); ?>';
                    try {
                        const fd = new FormData();
                        fd.set('action', 'aieo_dmm_webpush_test_newsletter');
                        fd.set('nonce', '<?php echo esc_js($nonce); ?>');
                        const resp = await fetch('<?php echo esc_js($ajax); ?>', { method:'POST', body:fd, credentials:'same-origin' });
                        const data = await resp.json();
                        if (data && data.success) {
                            testStatus.style.color = '#15803d';
                            testStatus.textContent = data.data.message;
                        } else {
                            testStatus.style.color = '#b32d2e';
                            testStatus.textContent = (data && data.data && data.data.message) || 'Failed.';
                        }
                    } catch (e) {
                        testStatus.style.color = '#b32d2e';
                        testStatus.textContent = String(e);
                    } finally {
                        testBtn.disabled = false;
                    }
                });
            }
        })();
        </script>
        <?php
        echo '</div></div>';
    }

    private function render_newsletter_sync_log_table() {
        global $wpdb;
        $tbl = $wpdb->prefix . 'aieo_newsletter_sync_log';
        $rows = $wpdb->get_results(
            "SELECT sync_id, provider, cohort, audience_count, synced_count, failed_count,
                    status, error_text, started_at, completed_at
             FROM {$tbl}
             ORDER BY sync_id DESC
             LIMIT 10",
            ARRAY_A
        );
        if (empty($rows)) { return; }

        echo '<h4 style="margin:18px 0 6px;font-size:13px;">' . esc_html__('Recent syncs', 'ai-eshop-optimizer') . '</h4>';
        echo '<table class="widefat striped">';
        echo '<thead><tr>';
        echo '<th>#</th><th>' . esc_html__('Provider', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Cohort', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Audience', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Synced', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Failed', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Status', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Started', 'ai-eshop-optimizer') . '</th>';
        echo '</tr></thead><tbody>';
        foreach ($rows as $r) {
            $status_color = $r['status'] === 'completed' ? '#15803d' : ($r['status'] === 'failed' ? '#b32d2e' : '#646970');
            echo '<tr>';
            echo '<td>' . (int) $r['sync_id'] . '</td>';
            echo '<td><code>' . esc_html((string) $r['provider']) . '</code></td>';
            echo '<td>' . esc_html((string) $r['cohort']) . '</td>';
            echo '<td>' . esc_html(number_format_i18n((int) $r['audience_count'])) . '</td>';
            echo '<td>' . esc_html(number_format_i18n((int) $r['synced_count'])) . '</td>';
            echo '<td>' . esc_html(number_format_i18n((int) $r['failed_count'])) . '</td>';
            echo '<td><span style="color:' . esc_attr($status_color) . ';font-weight:600;">' . esc_html((string) $r['status']) . '</span>';
            if (!empty($r['error_text'])) {
                echo '<br><small style="color:#b32d2e;">' . esc_html(substr((string) $r['error_text'], 0, 100)) . '</small>';
            }
            echo '</td>';
            echo '<td><small>' . esc_html((string) $r['started_at']) . '</small></td>';
            echo '</tr>';
        }
        echo '</tbody></table>';
    }

    
    private function render_campaign_launcher_card() {
        global $wpdb;
        $tbl_ci    = $wpdb->prefix . 'aieo_customer_intelligence';
        $tbl_camp  = $wpdb->prefix . 'aieo_push_campaigns';
        $cohorts   = self::reactivation_cohort_catalog();

        echo '<div class="aieo-dmm-section">';
        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Campaign launcher', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 10px;">'
            . esc_html__('Operator-initiated broadcast for special moments — site relaunch, anniversary, signature offer. Pick the audience by cohort + score + LTV; pick the channel(s) — push (now), newsletter (synced for your campaign builder to send), or both. The push side fires immediately on launch; the newsletter side syncs the audience with a tag your platform can target.', 'ai-eshop-optimizer')
            . '</p>';

        echo '<form id="aieo-dmm-campaign-form" class="aieo-dmm-form">';
        echo '<table class="form-table">';

        
        echo '<tr><th>' . esc_html__('Campaign name', 'ai-eshop-optimizer') . '</th><td><input type="text" name="name" class="regular-text" placeholder="' . esc_attr__('Spring relaunch — Lost cohort win-back', 'ai-eshop-optimizer') . '" required></td></tr>';

        echo '<tr><th>' . esc_html__('Audience cohort', 'ai-eshop-optimizer') . '</th><td><select name="audience_cohort"><option value="">' . esc_html__('— Pick a cohort —', 'ai-eshop-optimizer') . '</option>';
        foreach ($cohorts as $slug => $info) {
            echo '<option value="' . esc_attr($slug) . '">' . esc_html($info['label']) . ' (' . esc_html($info['recency']) . ')</option>';
        }
        echo '</select></td></tr>';

        echo '<tr><th>' . esc_html__('Min reactivation score', 'ai-eshop-optimizer') . '</th><td><input type="number" name="min_score" min="0" max="100" value="50" class="small-text">';
        echo '<p class="description">' . esc_html__('Skip customers below this score. 50 = "worth attempting" by default.', 'ai-eshop-optimizer') . '</p></td></tr>';

        echo '<tr><th>' . esc_html__('Min LTV (€)', 'ai-eshop-optimizer') . '</th><td><input type="number" name="min_ltv" min="0" step="0.01" value="0" class="small-text">';
        echo '<p class="description">' . esc_html__('Skip customers below this LTV. Use 100 to match the lost-cohort "worth chasing" filter.', 'ai-eshop-optimizer') . '</p></td></tr>';

        
        echo '<tr><th>' . esc_html__('Channels', 'ai-eshop-optimizer') . '</th><td>';
        echo '<label><input type="checkbox" name="channel_push" value="1" checked> ' . esc_html__('Push (immediate, to active subscribers)', 'ai-eshop-optimizer') . '</label><br>';
        echo '<label><input type="checkbox" name="channel_newsletter" value="1"> ' . esc_html__('Newsletter (sync audience to provider, send via campaign builder)', 'ai-eshop-optimizer') . '</label>';
        echo '</td></tr>';

        
        echo '<tr><th>' . esc_html__('Push title', 'ai-eshop-optimizer') . '</th><td><input type="text" name="title" class="regular-text" maxlength="120" placeholder="' . esc_attr__('Σου ετοιμάσαμε κάτι ξεχωριστό', 'ai-eshop-optimizer') . '"></td></tr>';
        echo '<tr><th>' . esc_html__('Push body', 'ai-eshop-optimizer') . '</th><td><textarea name="body" class="large-text" rows="2" maxlength="255" placeholder="' . esc_attr__('Επιστρέφουμε με νέα συλλογή και αποκλειστική προσφορά για σένα.', 'ai-eshop-optimizer') . '"></textarea></td></tr>';
        echo '<tr><th>' . esc_html__('Landing URL', 'ai-eshop-optimizer') . '</th><td><input type="url" name="target_url" class="regular-text" value="' . esc_url(home_url('/')) . '"></td></tr>';

        
        echo '<tr><th>' . esc_html__('Schedule', 'ai-eshop-optimizer') . '</th><td>';
        echo '<label><input type="radio" name="schedule_when" value="now" checked> ' . esc_html__('Send now', 'ai-eshop-optimizer') . '</label> &nbsp; ';
        echo '<label><input type="radio" name="schedule_when" value="later"> ' . esc_html__('Schedule for later', 'ai-eshop-optimizer') . '</label> ';
        echo '<input type="datetime-local" name="schedule_at" style="margin-left:8px;">';
        echo '<p class="description">' . esc_html__('"Send now" creates the campaign as draft + you click Launch on the next step. Later schedules a cron firing.', 'ai-eshop-optimizer') . '</p>';
        echo '</td></tr>';

        echo '<tr><th>' . esc_html__('Send rate / minute', 'ai-eshop-optimizer') . '</th><td><input type="number" name="send_rate" min="10" max="2000" value="200" class="small-text">';
        echo '<p class="description">' . esc_html__('Push throttle to avoid simultaneous-fire spikes on the push servers. 200/min handles 12k subscribers in an hour.', 'ai-eshop-optimizer') . '</p></td></tr>';

        echo '</table>';
        echo '<p class="submit">';
        echo '<button type="submit" class="button button-primary aieo-dmm-btn-fixed" data-action="aieo_dmm_webpush_create_campaign">';
        echo esc_html__('Create campaign (draft)', 'ai-eshop-optimizer');
        echo '</button>';
        echo '</p>';
        echo '</form>';

        
        $this->render_campaigns_history_table($tbl_camp);

        echo '</div></div>';
    }

    private function render_campaigns_history_table($tbl_camp) {
        global $wpdb;
        $rows = $wpdb->get_results(
            "SELECT campaign_id, name, channels, status, audience_count, sent_count, failed_count,
                    scheduled_at, launched_at, completed_at, created_at
             FROM {$tbl_camp}
             ORDER BY campaign_id DESC
             LIMIT 20",
            ARRAY_A
        );
        if (empty($rows)) { return; }
        $nonce = wp_create_nonce(AIEO_DMM::NONCE_ACTION);
        $ajax  = admin_url('admin-ajax.php');

        echo '<h4 style="margin:18px 0 6px;font-size:13px;">' . esc_html__('Recent campaigns', 'ai-eshop-optimizer') . '</h4>';
        echo '<table class="widefat striped">';
        echo '<thead><tr>';
        echo '<th>#</th><th>' . esc_html__('Name', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Channels', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Audience', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Sent', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Status', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Action', 'ai-eshop-optimizer') . '</th>';
        echo '</tr></thead><tbody>';
        foreach ($rows as $r) {
            $color = $r['status'] === 'sent' ? '#15803d'
                   : ($r['status'] === 'failed' ? '#b32d2e'
                   : ($r['status'] === 'running' ? '#0891b2' : '#646970'));
            echo '<tr>';
            echo '<td>' . (int) $r['campaign_id'] . '</td>';
            echo '<td><strong>' . esc_html((string) $r['name']) . '</strong></td>';
            echo '<td><code>' . esc_html((string) $r['channels']) . '</code></td>';
            echo '<td>' . esc_html(number_format_i18n((int) $r['audience_count'])) . '</td>';
            echo '<td>' . esc_html(number_format_i18n((int) $r['sent_count']));
            if ((int) $r['failed_count'] > 0) {
                echo ' <small style="color:#b32d2e;">(' . esc_html(number_format_i18n((int) $r['failed_count'])) . ' fail)</small>';
            }
            echo '</td>';
            echo '<td><span style="color:' . esc_attr($color) . ';font-weight:600;">' . esc_html((string) $r['status']) . '</span></td>';
            echo '<td>';
            if ($r['status'] === 'draft' || $r['status'] === 'scheduled') {
                echo '<button type="button" class="button button-small aieo-launch-campaign" data-id="' . (int) $r['campaign_id'] . '">'
                    . esc_html__('Launch', 'ai-eshop-optimizer') . '</button>';
            } else {
                echo '—';
            }
            echo '</td>';
            echo '</tr>';
        }
        echo '</tbody></table>';
        ?>
        <script>
        (function(){
            document.querySelectorAll('.aieo-launch-campaign').forEach(btn => {
                btn.addEventListener('click', async function(){
                    if (!confirm('<?php echo esc_js(__('Launch this campaign now? It will send to the entire audience.', 'ai-eshop-optimizer')); ?>')) { return; }
                    btn.disabled = true;
                    btn.textContent = '<?php echo esc_js(__('Launching…', 'ai-eshop-optimizer')); ?>';
                    try {
                        const fd = new FormData();
                        fd.set('action', 'aieo_dmm_webpush_launch_campaign');
                        fd.set('nonce', '<?php echo esc_js($nonce); ?>');
                        fd.set('campaign_id', btn.dataset.id);
                        const resp = await fetch('<?php echo esc_js($ajax); ?>', { method:'POST', body:fd, credentials:'same-origin' });
                        const data = await resp.json();
                        if (data && data.success) {
                            alert(data.data.message);
                            window.location.reload();
                        } else {
                            alert((data && data.data && data.data.message) || 'Failed.');
                            btn.disabled = false;
                            btn.textContent = '<?php echo esc_js(__('Launch', 'ai-eshop-optimizer')); ?>';
                        }
                    } catch (e) {
                        alert(String(e));
                        btn.disabled = false;
                        btn.textContent = '<?php echo esc_js(__('Launch', 'ai-eshop-optimizer')); ?>';
                    }
                });
            });
        })();
        </script>
        <?php
    }

    
    private function render_lookalike_export_card($tbl_ci) {
        global $wpdb;
        $cohorts_with_email = $wpdb->get_results(
            "SELECT dormancy_cohort, COUNT(*) c
             FROM {$tbl_ci}
             WHERE email <> ''
             GROUP BY dormancy_cohort
             ORDER BY FIELD(dormancy_cohort, '', 'drifting', 'lapsed', 'long_dormant', 'lost')",
            ARRAY_A
        );
        $base = admin_url('admin-post.php?action=aieo_dmm_webpush_export_lookalike');
        $nonce = wp_create_nonce('aieo_dmm_webpush_export_lookalike');

        echo '<div class="aieo-dmm-section">';
        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Lookalike audience export (Meta + Google)', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 10px;">'
            . esc_html__('Both Meta and Google accept SHA-256-hashed customer email lists as the seed for lookalike audiences — they find new prospects who match the profile of your AIEO segment. Download the hashed CSV, upload via the platform UI, build the lookalike there. Native OAuth API integration is on the Phase 7 roadmap; the manual upload flow is fast (<5 min) and works for any cohort.', 'ai-eshop-optimizer')
            . '</p>';

        echo '<h4 style="margin:14px 0 6px;font-size:13px;">' . esc_html__('Hashed CSV — drop into the platform UI', 'ai-eshop-optimizer') . '</h4>';
        echo '<p class="description" style="margin:0 0 8px;font-size:12px;">'
            . esc_html__('Each export contains: sha256_email, sha256_phone (when available). UTF-8, no BOM (Meta/Google prefer raw). Capped at 50,000 rows per file.', 'ai-eshop-optimizer')
            . '</p>';
        echo '<div style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;">';
        foreach ($cohorts_with_email as $row) {
            $cohort = (string) $row['dormancy_cohort'];
            $label = $cohort === '' ? __('Active customers', 'ai-eshop-optimizer') : ucwords(str_replace('_', ' ', $cohort));
            $count = (int) $row['c'];
            if ($count === 0) { continue; }
            $url = add_query_arg(array(
                'cohort'   => $cohort === '' ? 'active' : $cohort,
                '_wpnonce' => $nonce,
            ), $base);
            echo '<a class="button button-secondary" href="' . esc_url($url) . '">';
            echo '⬇ ' . esc_html($label);
            echo ' <span style="color:#9ca3af;">(' . esc_html(number_format_i18n($count)) . ')</span>';
            echo '</a>';
        }
        echo '</div>';

        echo '<h4 style="margin:18px 0 6px;font-size:13px;">' . esc_html__('Upload UIs', 'ai-eshop-optimizer') . '</h4>';
        echo '<div style="display:flex;flex-wrap:wrap;gap:8px;">';
        echo '<a class="button button-primary" href="https://www.facebook.com/adsmanager/manage/audiences" target="_blank" rel="noopener">';
        echo esc_html__('Open Meta Audience Manager →', 'ai-eshop-optimizer');
        echo '</a>';
        echo '<a class="button button-primary" href="https://ads.google.com/aw/audiences" target="_blank" rel="noopener">';
        echo esc_html__('Open Google Ads Audience Manager →', 'ai-eshop-optimizer');
        echo '</a>';
        echo '</div>';

        echo '<div style="margin-top:14px;padding:12px;background:#f0f9ff;border-left:3px solid #0891b2;border-radius:4px;font-size:12px;color:#0c4a6e;">';
        echo '<strong>' . esc_html__('Workflow:', 'ai-eshop-optimizer') . '</strong>';
        echo '<ol style="margin:6px 0 0 20px;">';
        echo '<li>' . esc_html__('Download a hashed CSV for the cohort that represents your "ideal customer" — usually VIP + Loyalist + active-Brand-Devoted.', 'ai-eshop-optimizer') . '</li>';
        echo '<li>' . esc_html__('Open Meta or Google Audience Manager. Choose "Customer list" / "Customer match" → upload CSV.', 'ai-eshop-optimizer') . '</li>';
        echo '<li>' . esc_html__('Once it processes (15–60 min), create a Lookalike Audience from it (Meta) or a Similar Audience (Google).', 'ai-eshop-optimizer') . '</li>';
        echo '<li>' . esc_html__('Use that audience in your prospecting campaigns. Re-export monthly to keep the seed fresh.', 'ai-eshop-optimizer') . '</li>';
        echo '</ol>';
        echo '</div>';

        echo '</div></div>';
    }

    
    private function render_lost_cohort_playbook_card($tbl_ci) {
        global $wpdb;

        $row = $wpdb->get_row(
            "SELECT COUNT(*) AS cnt,
                    IFNULL(SUM(ltv_total), 0)  AS sum_ltv,
                    IFNULL(AVG(ltv_total), 0)  AS avg_ltv,
                    SUM(CASE WHEN ltv_total >= 100 THEN 1 ELSE 0 END) AS high_ltv,
                    SUM(CASE WHEN orders_count >= 3 THEN 1 ELSE 0 END) AS multi_order,
                    SUM(CASE WHEN email <> '' THEN 1 ELSE 0 END)      AS has_email
             FROM {$tbl_ci}
             WHERE dormancy_cohort = 'lost'",
            ARRAY_A
        );
        $count        = (int)   ($row['cnt'] ?? 0);
        $pool_ltv     = (float) ($row['sum_ltv'] ?? 0);
        $avg_ltv      = (float) ($row['avg_ltv'] ?? 0);
        $high_ltv     = (int)   ($row['high_ltv'] ?? 0);
        $multi_order  = (int)   ($row['multi_order'] ?? 0);
        $has_email    = (int)   ($row['has_email'] ?? 0);

        
        
        
        
        $worth_chasing = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$tbl_ci}
             WHERE dormancy_cohort = 'lost'
               AND email <> ''
               AND (ltv_total >= 100 OR orders_count >= 3)"
        );
        $worth_chasing_ltv = (float) $wpdb->get_var(
            "SELECT IFNULL(SUM(ltv_total), 0) FROM {$tbl_ci}
             WHERE dormancy_cohort = 'lost'
               AND email <> ''
               AND (ltv_total >= 100 OR orders_count >= 3)"
        );

        echo '<div class="aieo-dmm-section">';
        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Lost cohort playbook (365+ days)', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 10px;">'
            . esc_html__('The lost cohort needs different mechanics than the trigger queue: it\'s a campaign play (site relaunch, anniversary, big sale, identity refresh), not an event-driven push. Push reach is near-zero for these customers — most were never on the site after we shipped push. Email + newsletter is the natural channel; the CSV export below gets you actionable today.', 'ai-eshop-optimizer')
            . '</p>';

        
        echo '<div class="aieo-attr-tiles">';
        $this->tile(__('Lost customers', 'ai-eshop-optimizer'),     number_format_i18n($count),
            __('Customers in dormancy_cohort = "lost" — no order in 365+ days. Treated separately from "dormant" because they need campaign mechanics, not event-driven nudges.', 'ai-eshop-optimizer'));
        $this->tile(__('Pool LTV', 'ai-eshop-optimizer'),           $this->fmt_money($pool_ltv),
            __('Total historical LTV across the lost pool. The aspirational ceiling — what we\'d recover if every lost customer came back at their original LTV. Unrealistic, but a useful upper bound.', 'ai-eshop-optimizer'));
        $this->tile(__('Avg LTV', 'ai-eshop-optimizer'),            $this->fmt_money($avg_ltv),
            __('Mean LTV per lost customer (Pool LTV ÷ Lost customers). Tells you whether this cohort is mostly one-time low-value buyers or a few high-value lapsed regulars.', 'ai-eshop-optimizer'));
        $this->tile(__('Have email', 'ai-eshop-optimizer'),         number_format_i18n($has_email),
            __('Lost customers with a non-empty email address — i.e. addressable via newsletter / TNP. The remainder is unreachable through any owned channel.', 'ai-eshop-optimizer'));
        $this->tile(__('Worth chasing', 'ai-eshop-optimizer'),      number_format_i18n($worth_chasing),
            __('Lost customers with email AND (LTV ≥ 100 € OR ≥ 3 prior orders). Skips the one-time low-spenders that dominate any long-tail and would dilute campaign metrics.', 'ai-eshop-optimizer'));
        $this->tile(__('Chase-pool LTV', 'ai-eshop-optimizer'),     $this->fmt_money($worth_chasing_ltv),
            __('Total historical LTV across just the "Worth chasing" subset. Realistic ceiling for what a well-aimed lost-cohort campaign could surface.', 'ai-eshop-optimizer'));
        echo '</div>';

        echo '<p style="margin:8px 0 0;font-size:12px;color:#646970;">'
            . esc_html__('"Worth chasing" = lost customers with email AND (LTV ≥ €100 OR ≥ 3 prior orders). Skips the one-time low-spenders the long tail gives you.', 'ai-eshop-optimizer')
            . '</p>';

        
        echo '<h4 style="margin:18px 0 6px;font-size:13px;">' . esc_html__('Recommended plays', 'ai-eshop-optimizer') . '</h4>';
        echo '<ol style="margin:0 0 14px 20px;font-size:13px;line-height:1.6;color:#374151;">';
        echo '<li><strong>' . esc_html__('Site-relaunch / brand-refresh blast', 'ai-eshop-optimizer') . '</strong> — '
            . esc_html__('“We\'ve redesigned the site for you. New look, same FF.” Single-shot to the chase pool. Highest reactivation lift of any lost-cohort play.', 'ai-eshop-optimizer')
            . '</li>';
        echo '<li><strong>' . esc_html__('Last-chance signature offer', 'ai-eshop-optimizer') . '</strong> — '
            . esc_html__('25–30% off + free shipping with a brand-specific framing. Generate a one-time WooCommerce coupon code. After: mute the non-responders permanently.', 'ai-eshop-optimizer')
            . '</li>';
        echo '<li><strong>' . esc_html__('Newsletter re-permission ("are you still with us?")', 'ai-eshop-optimizer') . '</strong> — '
            . esc_html__('Two-line check-in with one CTA. Honest framing protects the sender reputation; respondents earn a follow-up offer, non-respondents get muted.', 'ai-eshop-optimizer')
            . '</li>';
        echo '<li><strong>' . esc_html__('Anniversary-of-last-purchase', 'ai-eshop-optimizer') . '</strong> — '
            . esc_html__('Annual scheduled trigger keyed to first_order_at + N years. “It\'s been a year.” Lower volume, higher relevance. Phase 6+ once we automate.', 'ai-eshop-optimizer')
            . '</li>';
        echo '<li><strong>' . esc_html__('Lookalike-audience export', 'ai-eshop-optimizer') . '</strong> — '
            . esc_html__('Ship the lost-cohort emails to Meta/Google as a custom audience seed. Their lookalike algorithm finds new customers with similar profiles for paid acquisition. Indirect, but the asset earns its keep.', 'ai-eshop-optimizer')
            . '</li>';
        echo '</ol>';

        
        echo '<h4 style="margin:18px 0 6px;font-size:13px;">' . esc_html__('Export — drop into MailChimp / Klaviyo / your ESP', 'ai-eshop-optimizer') . '</h4>';
        echo '<p class="description" style="margin:0 0 8px;font-size:12px;">'
            . esc_html__('Each export is a CSV with: email, eponymous_id, wp_user_id, ltv, orders_count, recency_days, top_brand, top_category. Re-importable into any email platform. Use the "Worth chasing" filter to skip noise.', 'ai-eshop-optimizer')
            . '</p>';

        $base_url = admin_url('admin-post.php?action=aieo_dmm_webpush_export_cohort');
        $nonce    = wp_create_nonce('aieo_dmm_webpush_export_cohort');

        $exports = array(
            array(
                'cohort'   => 'lost',
                'filter'   => 'worth_chasing',
                'label'    => __('Lost — worth chasing (with email, LTV ≥ €100 or ≥ 3 orders)', 'ai-eshop-optimizer'),
                'count'    => $worth_chasing,
            ),
            array(
                'cohort'   => 'lost',
                'filter'   => 'all',
                'label'    => __('Lost — full cohort', 'ai-eshop-optimizer'),
                'count'    => $count,
            ),
            array(
                'cohort'   => 'long_dormant',
                'filter'   => 'all',
                'label'    => __('Long-dormant — full cohort (181–365d)', 'ai-eshop-optimizer'),
                'count'    => (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl_ci} WHERE dormancy_cohort = 'long_dormant'"),
            ),
            array(
                'cohort'   => 'lapsed',
                'filter'   => 'all',
                'label'    => __('Lapsed — full cohort (91–180d)', 'ai-eshop-optimizer'),
                'count'    => (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl_ci} WHERE dormancy_cohort = 'lapsed'"),
            ),
        );

        echo '<div style="display:flex;flex-wrap:wrap;gap:8px;">';
        foreach ($exports as $exp) {
            $url = add_query_arg(array(
                'cohort'   => $exp['cohort'],
                'filter'   => $exp['filter'],
                '_wpnonce' => $nonce,
            ), $base_url);
            echo '<a class="button button-secondary" href="' . esc_url($url) . '">';
            echo '⬇ ' . esc_html($exp['label']);
            echo ' <span style="color:#9ca3af;">(' . esc_html(number_format_i18n((int) $exp['count'])) . ')</span>';
            echo '</a>';
        }
        echo '</div>';

        
        $manual = $wpdb->get_results(
            "SELECT eponymous_id, email, ltv_total, orders_count, recency_days, top_brand_id
             FROM {$tbl_ci}
             WHERE dormancy_cohort = 'lost'
               AND email <> ''
               AND (ltv_total >= 100 OR orders_count >= 3)
             ORDER BY ltv_total DESC
             LIMIT 20",
            ARRAY_A
        );
        if (!empty($manual)) {
            $vitals_table = $wpdb->prefix . 'aieo_core_product_vitals';
            echo '<h4 style="margin:18px 0 6px;font-size:13px;">' . esc_html__('Top 20 lost customers worth a personal touch', 'ai-eshop-optimizer') . '</h4>';
            echo '<p class="description" style="margin:0 0 8px;font-size:12px;">'
                . esc_html__('Customers in the lost cohort with high LTV — worth a hand-crafted email from the brand owner before the automated lost-cohort blast. The personal touch converts ~3-5× better than templated sends on this segment.', 'ai-eshop-optimizer')
                . '</p>';
            echo '<table class="widefat striped aieo-attr-sortable-table">';
            echo '<thead><tr>';
            echo '<th style="width:50px;">#</th>';
            echo '<th data-sort-type="text">' . esc_html__('Customer', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
            echo '<th data-sort-type="num">' . esc_html__('LTV', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
            echo '<th data-sort-type="num">' . esc_html__('Orders', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
            echo '<th data-sort-type="num">' . esc_html__('Last seen', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
            echo '<th data-sort-type="text">' . esc_html__('Top brand', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
            echo '</tr></thead><tbody>';
            foreach ($manual as $i => $c) {
                $brand_name = (int) $c['top_brand_id'] > 0 ? $this->lookup_brand_name((int) $c['top_brand_id'], $vitals_table) : '';
                echo '<tr>';
                echo '<td>' . (int) ($i + 1) . '</td>';
                echo '<td data-sort-value="' . esc_attr((string) $c['email']) . '"><strong>' . esc_html((string) $c['email']) . '</strong>';
                echo '<br><small style="color:#646970;">epo: ' . (int) $c['eponymous_id'] . '</small></td>';
                echo '<td data-sort-value="' . esc_attr((string) $c['ltv_total']) . '">' . esc_html($this->fmt_money((float) $c['ltv_total'])) . '</td>';
                echo '<td data-sort-value="' . (int) $c['orders_count'] . '">' . esc_html(number_format_i18n((int) $c['orders_count'])) . '</td>';
                echo '<td data-sort-value="' . (int) $c['recency_days'] . '">' . esc_html(sprintf(
                    
                    _n('%d day ago', '%d days ago', (int) $c['recency_days'], 'ai-eshop-optimizer'),
                    (int) $c['recency_days']
                )) . '</td>';
                echo '<td>' . esc_html($brand_name !== '' ? $brand_name : '—') . '</td>';
                echo '</tr>';
            }
            echo '</tbody></table>';
        }

        
        echo '<div style="margin-top:14px;padding:12px;background:#f0f9ff;border-left:3px solid #0891b2;border-radius:4px;font-size:12px;color:#0c4a6e;">';
        echo '<strong>' . esc_html__('Phase 6+ automation hooks:', 'ai-eshop-optimizer') . '</strong> '
            . esc_html__('campaign launcher UI (audience filter + message + scheduled send), one-time WooCommerce coupon code generation per recipient, email-channel adapter (Mailchimp/Klaviyo/native WP), anniversary-of-last-purchase scheduled trigger, lookalike audience auto-export to Meta/Google.', 'ai-eshop-optimizer');
        echo '</div>';

        echo '</div></div>';
    }

    
    
    

    
    public function ajax_save_template() {
        AIEO_DMM::verify_ajax_request();
        global $wpdb;
        $tbl = $wpdb->prefix . 'aieo_push_templates';

        $tid   = isset($_POST['template_id']) ? (int) sanitize_text_field( wp_unslash( $_POST['template_id'] ) ) : 0;
        $field = isset($_POST['field']) ? sanitize_key((string) wp_unslash($_POST['field'])) : '';
        $raw   = isset($_POST['value']) ? wp_unslash((string) sanitize_text_field( wp_unslash( $_POST['value'] ) )) : '';

        $allowed = array('weight', 'title_template', 'body_template', 'icon_url', 'is_active');
        if ($tid <= 0 || !in_array($field, $allowed, true)) {
            wp_send_json_error(array('message' => __('Bad input.', 'ai-eshop-optimizer')));
        }

        if ($field === 'weight') {
            $value = max(1, min(100, (int) $raw));
            $fmt   = '%d';
        } elseif ($field === 'is_active') {
            $value = ((int) $raw) === 1 ? 1 : 0;
            $fmt   = '%d';
        } elseif ($field === 'icon_url') {
            $value = esc_url_raw($raw);
            $fmt   = '%s';
        } else {
            
            $value = wp_strip_all_tags($raw);
            $fmt   = '%s';
        }

        $rows = (int) $wpdb->update(
            $tbl,
            array($field => $value),
            array('template_id' => $tid),
            array($fmt),
            array('%d')
        );
        wp_send_json_success(array('rows' => $rows));
    }

    public function ajax_save_newsletter() {
        AIEO_DMM::verify_ajax_request();
        if (!class_exists('AIEO_Newsletter_Registry')) {
            wp_send_json_error(array('message' => __('Newsletter framework not loaded.', 'ai-eshop-optimizer')));
        }
        $patch = array();
        if (isset($_POST['provider'])) {
            $patch['provider'] = sanitize_key((string) wp_unslash($_POST['provider']));
        }
        foreach (array('tnp', 'mailchimp', 'klaviyo', 'webhook') as $slug) {
            if (isset($_POST[$slug]) && is_array($_POST[$slug])) {
                $patch[$slug] = array_map('sanitize_text_field', wp_unslash($_POST[$slug]));
            }
        }
        AIEO_Newsletter_Registry::instance()->save_settings($patch);
        wp_send_json_success(array(
            'message' => __('Newsletter provider settings saved.', 'ai-eshop-optimizer'),
        ));
    }

    public function ajax_test_newsletter() {
        AIEO_DMM::verify_ajax_request();
        if (!class_exists('AIEO_Newsletter_Registry')) {
            wp_send_json_error(array('message' => __('Newsletter framework not loaded.', 'ai-eshop-optimizer')));
        }
        $adapter = AIEO_Newsletter_Registry::instance()->adapter();
        if ($adapter === null) {
            wp_send_json_error(array('message' => __('No provider configured. Save settings first.', 'ai-eshop-optimizer')));
        }
        $result = $adapter->test_connection();
        if ($result['ok']) {
            $extras = '';
            if (!empty($result['details']['account_name'])) {
                $extras = ' — ' . $result['details']['account_name'];
            }
            wp_send_json_success(array(
                'message' => sprintf(
                    
                    __('✓ %1$s connection OK%2$s', 'ai-eshop-optimizer'),
                    $adapter->get_label(),
                    $extras
                ),
            ));
        }
        wp_send_json_error(array(
            'message' => sprintf(
                
                __('✗ %1$s connection failed: %2$s', 'ai-eshop-optimizer'),
                $adapter->get_label(),
                (string) $result['error']
            ),
        ));
    }

    public function ajax_sync_cohort() {
        AIEO_DMM::verify_ajax_request();
        if (!self::is_engine_live()) {
            wp_send_json_error(array('message' => self::engine_paused_message()));
        }
        if (!class_exists('AIEO_Newsletter_Registry')) {
            wp_send_json_error(array('message' => __('Newsletter framework not loaded.', 'ai-eshop-optimizer')));
        }
        $cohort = isset($_POST['cohort']) ? sanitize_key((string) wp_unslash($_POST['cohort'])) : '';
        $allowed = array('drifting', 'lapsed', 'long_dormant', 'lost', 'discount_hunter', 'pure_intent', 'vip', 'loyalist', 'brand_devoted', 'replenisher', 'newbie');
        if (!in_array($cohort, $allowed, true)) {
            wp_send_json_error(array('message' => __('Invalid cohort.', 'ai-eshop-optimizer')));
        }
        $filter = array(
            'only_email' => true,
            'min_score'  => isset($_POST['min_score']) ? (int) $_POST['min_score'] : 0,
            'min_ltv'    => isset($_POST['min_ltv'])   ? (float) $_POST['min_ltv'] : 0,
        );
        $started = microtime(true);
        $result  = AIEO_Newsletter_Registry::instance()->sync_cohort($cohort, $filter);
        $duration = round(microtime(true) - $started, 2);
        if (!empty($result['ok']) || (isset($result['synced']) && (int) $result['synced'] > 0)) {
            wp_send_json_success(array(
                'message' => sprintf(
                    
                    __('Synced %1$d / %2$d failed for cohort "%3$s" in %4$ss.', 'ai-eshop-optimizer'),
                    (int) ($result['synced'] ?? 0),
                    (int) ($result['failed'] ?? 0),
                    $cohort,
                    $duration
                ),
                'result' => $result,
            ));
        }
        wp_send_json_error(array(
            'message' => sprintf(
                
                __('Sync failed for cohort "%1$s": %2$s', 'ai-eshop-optimizer'),
                $cohort,
                (string) ($result['error'] ?? 'unknown')
            ),
        ));
    }

    
    
    

    public function ajax_create_campaign() {
        AIEO_DMM::verify_ajax_request();
        global $wpdb;

        $name           = sanitize_text_field(wp_unslash((string) ($_POST['name'] ?? '')));
        $cohort         = sanitize_key((string) wp_unslash($_POST['audience_cohort'] ?? ''));
        $min_score      = isset($_POST['min_score']) ? max(0, min(100, (int) $_POST['min_score'])) : 0;
        $min_ltv        = isset($_POST['min_ltv'])   ? max(0, (float) $_POST['min_ltv'])           : 0;
        $title          = sanitize_text_field(wp_unslash((string) ($_POST['title']      ?? '')));
        $body           = sanitize_textarea_field(wp_unslash((string) ($_POST['body']   ?? '')));
        $target_url     = esc_url_raw(wp_unslash((string) ($_POST['target_url']         ?? '')));
        $when           = sanitize_key((string) wp_unslash($_POST['schedule_when']      ?? 'now'));
        $sched_at_raw   = (string) wp_unslash($_POST['schedule_at']                     ?? '');
        $send_rate      = isset($_POST['send_rate']) ? max(10, min(2000, (int) $_POST['send_rate'])) : 200;
        $do_push        = !empty($_POST['channel_push']);
        $do_newsletter  = !empty($_POST['channel_newsletter']);

        if ($name === '' || $cohort === '' || (!$do_push && !$do_newsletter)) {
            wp_send_json_error(array('message' => __('Name, cohort, and at least one channel are required.', 'ai-eshop-optimizer')));
        }

        $channels = array();
        if ($do_push)       { $channels[] = 'push'; }
        if ($do_newsletter) { $channels[] = 'newsletter'; }

        
        
        
        $tbl_ci = $wpdb->prefix . 'aieo_customer_intelligence';
        $wc = self::cohort_where_clause($cohort);
        $count_args = array_merge($wc['args'], array($min_score, $min_ltv));
        $audience_count = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$tbl_ci}
             WHERE {$wc['sql']}
               AND email <> ''
               AND reactivation_score >= %d
               AND ltv_total >= %f",
            $count_args
        ));

        $scheduled_at = null;
        $status = 'draft';
        if ($when === 'later' && $sched_at_raw !== '') {
            
            $ts = strtotime($sched_at_raw);
            if ($ts > time()) {
                $scheduled_at = gmdate('Y-m-d H:i:s', $ts);
                $status = 'scheduled';
            }
        }

        $audience_filter = array(
            'cohort'    => $cohort,
            'min_score' => $min_score,
            'min_ltv'   => $min_ltv,
        );

        $tbl = $wpdb->prefix . 'aieo_push_campaigns';
        $wpdb->insert($tbl, array(
            'name'                  => $name,
            'audience_filter_json'  => wp_json_encode($audience_filter),
            'channels'              => implode(',', $channels),
            'title_template'        => $title,
            'body_template'         => $body,
            'cta_label'             => '',
            'target_url'            => $target_url,
            'status'                => $status,
            'audience_count'        => $audience_count,
            'send_rate_per_minute'  => $send_rate,
            'scheduled_at'          => $scheduled_at,
            'created_by'            => get_current_user_id(),
        ));
        $cid = (int) $wpdb->insert_id;

        wp_send_json_success(array(
            'message' => sprintf(
                
                __('Campaign #%1$d created with audience %2$d. Status: %3$s.', 'ai-eshop-optimizer'),
                $cid,
                $audience_count,
                $status
            ),
            'campaign_id' => $cid,
        ));
    }

    public function ajax_launch_campaign() {
        AIEO_DMM::verify_ajax_request();
        if (!self::is_engine_live()) {
            wp_send_json_error(array('message' => self::engine_paused_message()));
        }
        global $wpdb;
        $cid = isset($_POST['campaign_id']) ? (int) $_POST['campaign_id'] : 0;
        if ($cid <= 0) {
            wp_send_json_error(array('message' => __('Invalid campaign id.', 'ai-eshop-optimizer')));
        }
        $tbl   = $wpdb->prefix . 'aieo_push_campaigns';
        $tbl_ci = $wpdb->prefix . 'aieo_customer_intelligence';
        $tbl_subs = $wpdb->prefix . 'aieo_push_subscriptions';
        $tbl_dispatch = $wpdb->prefix . 'aieo_push_dispatch';

        $camp = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$tbl} WHERE campaign_id = %d", $cid), ARRAY_A);
        if (!$camp) {
            wp_send_json_error(array('message' => __('Campaign not found.', 'ai-eshop-optimizer')));
        }
        if (!in_array($camp['status'], array('draft', 'scheduled'), true)) {
            wp_send_json_error(array('message' => __('Campaign already launched or completed.', 'ai-eshop-optimizer')));
        }

        $filter = json_decode((string) $camp['audience_filter_json'], true);
        $cohort = (string) ($filter['cohort'] ?? '');
        $min_score = (int) ($filter['min_score'] ?? 0);
        $min_ltv = (float) ($filter['min_ltv'] ?? 0);
        $channels = explode(',', (string) $camp['channels']);

        $started_at = current_time('mysql', true);
        $wpdb->update($tbl, array(
            'status'      => 'running',
            'launched_at' => $started_at,
        ), array('campaign_id' => $cid));

        $sent = 0; $failed = 0;
        $newsletter_segment_id = '';
        $newsletter_provider   = '';

        
        if (in_array('push', $channels, true) && class_exists('AIEO_Push_Sender')) {
            
            
            $wc = self::cohort_where_clause($cohort);
            
            
            $ci_clause = preg_replace('/\b(sale_purchase_cnt|new_product_purchase_cnt|gift_completed_count|monthly_offer_purchase_cnt|dormancy_cohort)\b/', 'ci.$1', $wc['sql']);
            $disp_args = array_merge($wc['args'], array($min_score, $min_ltv));
            $sub_rows = $wpdb->get_results($wpdb->prepare(
                "SELECT s.subscription_id, s.endpoint, s.p256dh, s.auth_secret, s.user_id, ci.eponymous_id
                 FROM {$tbl_subs} s
                 JOIN {$tbl_ci} ci ON ci.wp_user_id = s.user_id
                 WHERE s.status = 'active'
                   AND {$ci_clause}
                   AND ci.reactivation_score >= %d
                   AND ci.ltv_total >= %f",
                $disp_args
            ), ARRAY_A);
            $sender = AIEO_Push_Sender::instance();
            foreach ((array) $sub_rows as $sr) {
                $result = $sender->send_empty(array(
                    'endpoint'    => $sr['endpoint'],
                    'p256dh'      => $sr['p256dh'],
                    'auth_secret' => $sr['auth_secret'],
                ), 60);
                $now = current_time('mysql', true);
                $wpdb->insert($tbl_dispatch, array(
                    'subscription_id' => (int) $sr['subscription_id'],
                    'user_id'         => (int) $sr['user_id'],
                    'trigger_id'      => 'campaign:' . $cid,
                    'target_url'      => (string) $camp['target_url'],
                    'state'           => $result['ok'] ? 'sent' : 'failed',
                    'provider_status' => (int) $result['status'],
                    'provider_error'  => substr((string) $result['error'], 0, 200),
                    'queued_at'       => $started_at,
                    'sent_at'         => $result['ok'] ? $now : null,
                    'created_at'      => $now,
                ));
                
                $wpdb->query($wpdb->prepare(
                    "UPDATE {$tbl_ci}
                     SET reactivation_attempts_count = reactivation_attempts_count + 1,
                         last_reactivation_attempt_at = %s
                     WHERE eponymous_id = %d",
                    $now, (int) $sr['eponymous_id']
                ));
                if ($result['ok']) { $sent++; } else { $failed++; }
            }
        }

        
        if (in_array('newsletter', $channels, true) && class_exists('AIEO_Newsletter_Registry')) {
            $sync = AIEO_Newsletter_Registry::instance()->sync_cohort($cohort, array(
                'only_email' => true,
                'min_score'  => $min_score,
                'min_ltv'    => $min_ltv,
            ));
            $sent   += (int) ($sync['synced'] ?? 0);
            $failed += (int) ($sync['failed'] ?? 0);
            $newsletter_segment_id = (string) ($sync['provider_segment_id'] ?? '');
            $adapter = AIEO_Newsletter_Registry::instance()->adapter();
            if ($adapter !== null) { $newsletter_provider = $adapter->get_id(); }
        }

        $wpdb->update($tbl, array(
            'status'                => 'sent',
            'sent_count'            => $sent,
            'failed_count'          => $failed,
            'newsletter_provider'   => $newsletter_provider,
            'newsletter_segment_id' => $newsletter_segment_id,
            'completed_at'          => current_time('mysql', true),
        ), array('campaign_id' => $cid));

        wp_send_json_success(array(
            'message' => sprintf(
                
                __('Campaign #%1$d launched: %2$d sent, %3$d failed.', 'ai-eshop-optimizer'),
                $cid, $sent, $failed
            ),
            'sent'   => $sent,
            'failed' => $failed,
        ));
    }

    
    public function handle_export_lookalike() {
        if (!current_user_can(AIEO_CAPABILITY)) {
            wp_die(esc_html__('Insufficient permissions.', 'ai-eshop-optimizer'));
        }
        check_admin_referer('aieo_dmm_webpush_export_lookalike');

        $cohort = isset($_GET['cohort']) ? sanitize_key((string) wp_unslash($_GET['cohort'])) : '';
        $allowed = array('active', 'drifting', 'lapsed', 'long_dormant', 'lost', 'discount_hunter', 'pure_intent', 'vip', 'loyalist', 'brand_devoted', 'replenisher', 'newbie');
        if (!in_array($cohort, $allowed, true)) {
            wp_die(esc_html__('Invalid cohort.', 'ai-eshop-optimizer'));
        }

        global $wpdb;
        $tbl_ci = $wpdb->prefix . 'aieo_customer_intelligence';
        
        $cohort_clause = $cohort === 'active'
            ? $wpdb->prepare(' AND ci.dormancy_cohort = %s ', '')
            : $wpdb->prepare(' AND ci.dormancy_cohort = %s ', $cohort);

        $rows = $wpdb->get_results(
            "SELECT ci.email
             FROM {$tbl_ci} ci
             WHERE ci.email <> ''
             {$cohort_clause}
             LIMIT 50000",
            ARRAY_A
        );

        $filename = sprintf('aieo-lookalike-%s-%s.csv', $cohort, gmdate('Ymd-His'));
        nocache_headers();
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');

        $out = fopen('php://output', 'w');
        fputcsv($out, array('sha256_email'));  
        foreach ((array) $rows as $r) {
            $email = strtolower(trim((string) $r['email']));
            if ($email === '' || !is_email($email)) { continue; }
            fputcsv($out, array(hash('sha256', $email)));
        }
        fclose($out);
        exit;
    }

    
    public function handle_export_cohort() {
        if (!current_user_can(AIEO_CAPABILITY)) {
            wp_die(esc_html__('Insufficient permissions.', 'ai-eshop-optimizer'));
        }
        check_admin_referer('aieo_dmm_webpush_export_cohort');

        $cohort = isset($_GET['cohort']) ? sanitize_key((string) wp_unslash($_GET['cohort'])) : '';
        $filter = isset($_GET['filter']) ? sanitize_key((string) wp_unslash($_GET['filter'])) : 'all';
        if (!in_array($cohort, array('drifting', 'lapsed', 'long_dormant', 'lost', 'discount_hunter', 'pure_intent', 'vip', 'loyalist', 'brand_devoted', 'replenisher', 'newbie'), true)) {
            wp_die(esc_html__('Invalid cohort.', 'ai-eshop-optimizer'));
        }

        global $wpdb;
        $tbl_ci    = $wpdb->prefix . 'aieo_customer_intelligence';
        $vitals    = $wpdb->prefix . 'aieo_core_product_vitals';

        $where = $wpdb->prepare(' WHERE ci.dormancy_cohort = %s ', $cohort);
        if ($filter === 'worth_chasing') {
            $where .= " AND ci.email <> '' AND (ci.ltv_total >= 100 OR ci.orders_count >= 3) ";
        }

        $sql = "SELECT ci.eponymous_id, ci.wp_user_id, ci.email, ci.ltv_total, ci.orders_count,
                       ci.recency_days, ci.top_brand_id, ci.top_category_id, ci.reactivation_score
                FROM {$tbl_ci} ci
                {$where}
                ORDER BY ci.ltv_total DESC";
        $rows = $wpdb->get_results($sql, ARRAY_A);

        
        $brand_cache = array();
        $cat_cache   = array();
        $name_for_brand = function ($id) use (&$brand_cache, $wpdb, $vitals) {
            $id = (int) $id;
            if ($id <= 0) { return ''; }
            if (isset($brand_cache[$id])) { return $brand_cache[$id]; }
            $name = (string) $wpdb->get_var($wpdb->prepare(
                "SELECT brand FROM {$vitals} WHERE brand_id = %d AND brand <> '' LIMIT 1",
                $id
            ));
            $brand_cache[$id] = $name;
            return $name;
        };
        $name_for_cat = function ($id) use (&$cat_cache, $wpdb, $vitals) {
            $id = (int) $id;
            if ($id <= 0) { return ''; }
            if (isset($cat_cache[$id])) { return $cat_cache[$id]; }
            $name = (string) $wpdb->get_var($wpdb->prepare(
                "SELECT head_category FROM {$vitals} WHERE head_id = %d AND head_category <> '' LIMIT 1",
                $id
            ));
            $cat_cache[$id] = $name;
            return $name;
        };

        $filename = sprintf(
            'aieo-push-cohort-%s-%s-%s.csv',
            $cohort,
            $filter,
            gmdate('Ymd-His')
        );

        nocache_headers();
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');

        
        echo "\xEF\xBB\xBF";

        $out = fopen('php://output', 'w');
        fputcsv($out, array(
            'email', 'eponymous_id', 'wp_user_id', 'ltv', 'orders_count',
            'recency_days', 'top_brand', 'top_category', 'reactivation_score',
        ));
        foreach ((array) $rows as $r) {
            fputcsv($out, array(
                (string) $r['email'],
                (int) $r['eponymous_id'],
                (int) $r['wp_user_id'],
                (float) $r['ltv_total'],
                (int) $r['orders_count'],
                (int) $r['recency_days'],
                $name_for_brand($r['top_brand_id']),
                $name_for_cat($r['top_category_id']),
                (int) $r['reactivation_score'],
            ));
        }
        fclose($out);
        exit;
    }

    
    
    public static function cohort_where_clause($cohort_slug) {
        
        static $badge_clauses = array(
            'sale_buyer'          => 'sale_purchase_cnt >= 1',
            'new_product_adopter' => 'new_product_purchase_cnt >= 1',
            'gift_completer'      => 'gift_completed_count >= 1',
            'monthly_responder'   => 'monthly_offer_purchase_cnt >= 1',
        );
        if (isset($badge_clauses[$cohort_slug])) {
            return array('sql' => $badge_clauses[$cohort_slug], 'args' => array());
        }
        
        if ($cohort_slug === 'active') {
            return array('sql' => "dormancy_cohort = ''", 'args' => array());
        }
        
        return array('sql' => 'dormancy_cohort = %s', 'args' => array((string) $cohort_slug));
    }

    public static function reactivation_cohort_catalog() {
        return array(
            'drifting' => array(
                'label'    => __('Drifting', 'ai-eshop-optimizer'),
                'recency'  => __('60–90 days', 'ai-eshop-optimizer'),
                'strategy' => __('Light reminder — new arrivals from their top brand. Push first, no offer.', 'ai-eshop-optimizer'),
                'color'    => '#0891b2',
            ),
            'lapsed' => array(
                'label'    => __('Lapsed', 'ai-eshop-optimizer'),
                'recency'  => __('91–180 days', 'ai-eshop-optimizer'),
                'strategy' => __('“We miss you” — personalized brand pull, light offer (e.g. 10% off favorite brand).', 'ai-eshop-optimizer'),
                'color'    => '#7c3aed',
            ),
            'long_dormant' => array(
                'label'    => __('Long-dormant', 'ai-eshop-optimizer'),
                'recency'  => __('181–365 days', 'ai-eshop-optimizer'),
                'strategy' => __('Strong offer — 20% off top category + social proof. Email primary, push echo.', 'ai-eshop-optimizer'),
                'color'    => '#ea580c',
            ),
            'lost' => array(
                'label'    => __('Lost', 'ai-eshop-optimizer'),
                'recency'  => __('365+ days', 'ai-eshop-optimizer'),
                'strategy' => __('One last best offer (free shipping + 25% / signature product), then mute permanently.', 'ai-eshop-optimizer'),
                'color'    => '#6b7280',
            ),
            
            
            
            
            
            
            'discount_hunter' => array(
                'label'    => __('Discount Hunter', 'ai-eshop-optimizer'),
                'recency'  => __('any recency', 'ai-eshop-optimizer'),
                'strategy' => __('Promo-led messaging. Free-shipping-threshold campaigns, percentage-off coupons, clearance-event pushes. ~3.8% of base, behavioural not recency.', 'ai-eshop-optimizer'),
                'color'    => '#b91c1c',
            ),
            'pure_intent' => array(
                'label'    => __('Pure-intent shoppers', 'ai-eshop-optimizer'),
                'recency'  => __('any recency', 'ai-eshop-optimizer'),
                'strategy' => __('sole_order_share ≥ 50%. They paid shipping for a single product — they know exactly what they want. Replenishment / new-variant / restock messaging on the products they sole-bought.', 'ai-eshop-optimizer'),
                'color'    => '#1d4ed8',
            ),
            'vip' => array(
                'label'    => __('VIP', 'ai-eshop-optimizer'),
                'recency'  => __('top-quintile LTV', 'ai-eshop-optimizer'),
                'strategy' => __('Concierge tier — early-access invites, premium-brand previews, personal-shopper outreach. Coupons NOT recommended (margin leak on already-loyal spenders).', 'ai-eshop-optimizer'),
                'color'    => '#dc2626',
            ),
            'loyalist' => array(
                'label'    => __('Loyalist', 'ai-eshop-optimizer'),
                'recency'  => __('5+ orders, active', 'ai-eshop-optimizer'),
                'strategy' => __('Recognition-led messaging — "thank you for X orders", referral asks, tier-graduation perks.', 'ai-eshop-optimizer'),
                'color'    => '#7c3aed',
            ),
            'brand_devoted' => array(
                'label'    => __('Brand-Devoted', 'ai-eshop-optimizer'),
                'recency'  => __('70%+ single-brand', 'ai-eshop-optimizer'),
                'strategy' => __('Brand-launch alerts on their concentrated brand, exclusive collaborations, restock-priority signals.', 'ai-eshop-optimizer'),
                'color'    => '#0891b2',
            ),
            'replenisher' => array(
                'label'    => __('Replenisher', 'ai-eshop-optimizer'),
                'recency'  => __('predictable cadence', 'ai-eshop-optimizer'),
                'strategy' => __('Time-aware reorder reminders pegged to their personal avg_dbo. Highest-converting cohort for cadence-triggered messaging.', 'ai-eshop-optimizer'),
                'color'    => '#15803d',
            ),
            'newbie' => array(
                'label'    => __('Newbie', 'ai-eshop-optimizer'),
                'recency'  => __('1 order, recent', 'ai-eshop-optimizer'),
                'strategy' => __('Onboarding sequence — product education, brand discovery, "complete your routine" cross-sells. NOT discount-led (sets the wrong expectation).', 'ai-eshop-optimizer'),
                'color'    => '#f59e0b',
            ),
            
            
            
            
            'sale_buyer' => array(
                'label'    => __('Sale-responsive buyers', 'ai-eshop-optimizer'),
                'recency'  => __('any recency', 'ai-eshop-optimizer'),
                'strategy' => __('Customers who bought from a SALE-flagged product card. Push them at the START of new sale events (early-access drops, flash promos). High signal: they\'ve proven discount sensitivity.', 'ai-eshop-optimizer'),
                'color'    => '#dc2626',
            ),
            'new_product_adopter' => array(
                'label'    => __('NEW-product adopters', 'ai-eshop-optimizer'),
                'recency'  => __('any recency', 'ai-eshop-optimizer'),
                'strategy' => __('Customers who bought from a NEW-flagged card — early-adopter pattern. Push fresh launches the day they go live. Low discount sensitivity — focus on novelty.', 'ai-eshop-optimizer'),
                'color'    => '#1a1a1a',
            ),
            'gift_completer' => array(
                'label'    => __('Free-gift completers', 'ai-eshop-optimizer'),
                'recency'  => __('any recency', 'ai-eshop-optimizer'),
                'strategy' => __('Customers who hit the cart-threshold to claim a free gift. They\'re receptive to over-and-above offers — push when a new gift offer launches with a brand they\'ve previously bought.', 'ai-eshop-optimizer'),
                'color'    => '#0a7d3a',
            ),
            'monthly_responder' => array(
                'label'    => __('Monthly-offer responders', 'ai-eshop-optimizer'),
                'recency'  => __('any recency', 'ai-eshop-optimizer'),
                'strategy' => __('Customers who bought from the ΠΡΟΣΦΟΡΑ ΤΟΥ ΜΗΝΑ rotation. Push them on the 1st of each month when the new monthly batch goes live — predictable conversion lift.', 'ai-eshop-optimizer'),
                'color'    => '#F46767',
            ),
        );
    }

    
    public function render_banner_appearance_card() {
        $b = self::get_banner_settings();

        echo '<div class="aieo-dmm-section">';
        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Banner appearance', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 10px;">'
            . esc_html__('Visual + behavior settings for the first-visit opt-in banner. Top position auto-detects the site header on most themes (block-based or classic) and lands the card just below it.', 'ai-eshop-optimizer')
            . '</p>';

        echo '<form id="aieo-dmm-push-banner-form" class="aieo-dmm-form">';

        
        
        
        $banner_on = !empty($b['banner_enabled']);
        echo '<div style="margin:0 0 14px;padding:12px 14px;background:' . ( $banner_on ? '#ecfdf5' : '#fef2f2' ) . ';border-left:3px solid ' . ( $banner_on ? '#15803d' : '#b91c1c' ) . ';border-radius:4px;">';
        echo '<label style="display:flex;align-items:center;gap:10px;font-weight:600;cursor:pointer;">';
        echo '<input type="checkbox" name="banner_enabled" value="1"' . checked( $banner_on, true, false ) . ' style="margin:0;">';
        echo '<span>' . esc_html__( 'Show the opt-in banner on the storefront', 'ai-eshop-optimizer' ) . '</span>';
        echo '</label>';
        echo '<p class="description" style="margin:6px 0 0 27px;font-size:12px;">'
            . esc_html__( 'Master display switch. Independent from the "send push messages" master switch — when this is OFF, no first-visit opt-in prompt is shown to new visitors, but customers who already subscribed keep receiving campaigns normally. Turn this OFF when running a campaign-freeze, doing a redesign, or when GDPR/legal review temporarily pauses new acquisition.', 'ai-eshop-optimizer' )
            . '</p>';
        echo '</div>';

        
        echo '<h4 style="margin:14px 0 6px;font-size:13px;">' . esc_html__('Position & timing', 'ai-eshop-optimizer') . '</h4>';
        echo '<table class="form-table">';
        echo '<tr><th>' . esc_html__('Position', 'ai-eshop-optimizer') . '</th><td><select name="position">';
        $opts = array(
            'top'    => __('Top — under the site header (auto-detected)', 'ai-eshop-optimizer'),
            'middle' => __('Middle — viewport-centered card', 'ai-eshop-optimizer'),
            'bottom' => __('Bottom — above the footer', 'ai-eshop-optimizer'),
        );
        foreach ($opts as $val => $label) {
            echo '<option value="' . esc_attr($val) . '"' . selected($b['position'], $val, false) . '>' . esc_html($label) . '</option>';
        }
        echo '</select></td></tr>';
        echo '<tr><th>' . esc_html__('Manual offset (px)', 'ai-eshop-optimizer') . '</th>'
            . '<td><input type="number" min="0" max="500" name="offset" value="' . (int) $b['offset'] . '" class="small-text">'
            . '<p class="description">' . esc_html__('Distance from the top/bottom edge. Used as a fallback when the header auto-detector can\'t find a header. Ignored for "middle".', 'ai-eshop-optimizer') . '</p></td></tr>';
        echo '<tr><th>' . esc_html__('Show delay (ms)', 'ai-eshop-optimizer') . '</th>'
            . '<td><input type="number" min="0" max="60000" step="500" name="delay_ms" value="' . (int) $b['delay_ms'] . '" class="small-text">'
            . '<p class="description">' . esc_html__('Milliseconds before the banner appears on first visit. A larger value (8000–12000) keeps the LCP / CLS clean for GTmetrix and Lighthouse — the SW + Push API check + first paint of the banner all happen well after the critical render path.', 'ai-eshop-optimizer') . '</p></td></tr>';
        echo '</table>';

        
        echo '<h4 style="margin:18px 0 6px;font-size:13px;">' . esc_html__('Modal', 'ai-eshop-optimizer') . '</h4>';
        echo '<table class="form-table">';
        $this->color_row('bg',           __('Background color', 'ai-eshop-optimizer'),     $b['bg'],           '#ffffff');
        $this->color_row('text',         __('Text color', 'ai-eshop-optimizer'),           $b['text'],         '#1f2937');
        $this->color_row('icon_color',   __('Icon color (bell)', 'ai-eshop-optimizer'),    $b['icon_color'],   '#F46767');
        $this->color_row('border_color', __('Border color', 'ai-eshop-optimizer'),         $b['border_color'], '#e5e7eb');
        echo '<tr><th>' . esc_html__('Border width (px)', 'ai-eshop-optimizer') . '</th>'
            . '<td><input type="number" min="0" max="10" name="border_width" value="' . (int) $b['border_width'] . '" class="small-text"></td></tr>';
        echo '<tr><th>' . esc_html__('Border radius (px)', 'ai-eshop-optimizer') . '</th>'
            . '<td><input type="number" min="0" max="40" name="border_radius" value="' . (int) $b['border_radius'] . '" class="small-text"></td></tr>';
        echo '</table>';

        
        echo '<h4 style="margin:18px 0 6px;font-size:13px;">' . esc_html__('Buttons', 'ai-eshop-optimizer') . '</h4>';
        echo '<table class="form-table">';
        echo '<tr><th colspan="2" style="padding-top:6px;color:#374151;">' . esc_html__('Primary (Yes)', 'ai-eshop-optimizer') . '</th></tr>';
        $this->color_row('btn_primary_bg',     __('Background', 'ai-eshop-optimizer'),  $b['btn_primary_bg'],     '#F46767');
        $this->color_row('btn_primary_text',   __('Text', 'ai-eshop-optimizer'),        $b['btn_primary_text'],   '#ffffff');
        $this->color_row('btn_primary_border', __('Border color', 'ai-eshop-optimizer'),$b['btn_primary_border'], '#F46767');
        echo '<tr><th>' . esc_html__('Border width (px)', 'ai-eshop-optimizer') . '</th>'
            . '<td><input type="number" min="0" max="6" name="btn_primary_border_w" value="' . (int) $b['btn_primary_border_w'] . '" class="small-text"></td></tr>';
        echo '<tr><th colspan="2" style="padding-top:6px;color:#374151;">' . esc_html__('Secondary (Not now)', 'ai-eshop-optimizer') . '</th></tr>';
        $this->color_row('btn_secondary_bg',     __('Background', 'ai-eshop-optimizer'),  $b['btn_secondary_bg'],     '#f3f4f6');
        $this->color_row('btn_secondary_text',   __('Text', 'ai-eshop-optimizer'),        $b['btn_secondary_text'],   '#4b5563');
        $this->color_row('btn_secondary_border', __('Border color', 'ai-eshop-optimizer'),$b['btn_secondary_border'], '#f3f4f6');
        echo '<tr><th>' . esc_html__('Border width (px)', 'ai-eshop-optimizer') . '</th>'
            . '<td><input type="number" min="0" max="6" name="btn_secondary_border_w" value="' . (int) $b['btn_secondary_border_w'] . '" class="small-text"></td></tr>';
        echo '<tr><th colspan="2" style="padding-top:6px;color:#374151;">' . esc_html__('Shared', 'ai-eshop-optimizer') . '</th></tr>';
        echo '<tr><th>' . esc_html__('Button border radius (px)', 'ai-eshop-optimizer') . '</th>'
            . '<td><input type="number" min="0" max="40" name="btn_radius" value="' . (int) $b['btn_radius'] . '" class="small-text"></td></tr>';
        echo '</table>';

        
        echo '<h4 style="margin:18px 0 6px;font-size:13px;">' . esc_html__('Copy (editable for A/B without a deploy)', 'ai-eshop-optimizer') . '</h4>';
        echo '<table class="form-table">';
        echo '<tr><th>' . esc_html__('Title', 'ai-eshop-optimizer') . '</th><td><input type="text" name="copy_title" value="' . esc_attr((string) $b['copy_title']) . '" class="regular-text"></td></tr>';
        echo '<tr><th>' . esc_html__('Body', 'ai-eshop-optimizer') . '</th><td><textarea name="copy_body" rows="2" class="large-text">' . esc_textarea((string) $b['copy_body']) . '</textarea></td></tr>';
        echo '<tr><th>' . esc_html__('Accept label', 'ai-eshop-optimizer') . '</th><td><input type="text" name="copy_accept" value="' . esc_attr((string) $b['copy_accept']) . '" class="regular-text"></td></tr>';
        echo '<tr><th>' . esc_html__('Dismiss label', 'ai-eshop-optimizer') . '</th><td><input type="text" name="copy_dismiss" value="' . esc_attr((string) $b['copy_dismiss']) . '" class="regular-text"></td></tr>';
        echo '<tr><th colspan="2" style="padding-top:6px;color:#374151;">' . esc_html__('Thank-you toast (after Yes / No click)', 'ai-eshop-optimizer') . '</th></tr>';
        echo '<tr><th>' . esc_html__('After "Yes" message', 'ai-eshop-optimizer') . '</th><td>'
            . '<textarea name="copy_thanks_yes" rows="2" class="large-text">' . esc_textarea((string) $b['copy_thanks_yes']) . '</textarea>'
            . '<p class="description">' . esc_html__('Browsers always show their own native permission prompt after the user clicks Yes — there is no API to bypass it. Use this message to nudge the visitor to also confirm in the browser pop-up.', 'ai-eshop-optimizer') . '</p>'
            . '</td></tr>';
        echo '<tr><th>' . esc_html__('After "No" message', 'ai-eshop-optimizer') . '</th><td>'
            . '<textarea name="copy_thanks_no" rows="2" class="large-text">' . esc_textarea((string) $b['copy_thanks_no']) . '</textarea>'
            . '</td></tr>';
        echo '<tr><th>' . esc_html__('Auto-close after (seconds)', 'ai-eshop-optimizer') . '</th><td>'
            . '<input type="number" min="0" max="30" name="thanks_autoclose_secs" value="' . (int) $b['thanks_autoclose_secs'] . '" class="small-text">'
            . ' <span class="description">' . esc_html__('0 = stay open until the user clicks the X.', 'ai-eshop-optimizer') . '</span>'
            . '</td></tr>';
        echo '</table>';

        echo '<p class="submit">';
        echo '<button type="submit" class="button button-primary aieo-dmm-btn-fixed" data-action="aieo_dmm_webpush_save_banner">';
        echo esc_html__('Save banner appearance', 'ai-eshop-optimizer');
        echo '</button></p>';
        echo '</form>';
        echo '</div></div>';
    }

    
    private function color_row($name, $label, $value, $default) {
        echo '<tr><th>' . esc_html($label) . '</th><td><input type="text" name="' . esc_attr($name) . '" value="' . esc_attr((string) $value) . '" class="aieo-dmm-push-color" data-default-color="' . esc_attr($default) . '"></td></tr>';
    }

    
    public function ajax_save_banner() {
        AIEO_DMM::verify_ajax_request();

        $cur = self::get_banner_settings();

        
        
        
        
        
        $cur['banner_enabled'] = isset($_POST['banner_enabled']) ? true : false;

        
        if (isset($_POST['position'])) {
            $v = sanitize_key(wp_unslash((string) $_POST['position']));
            $cur['position'] = in_array($v, array('top', 'middle', 'bottom'), true) ? $v : 'top';
        }
        $int_fields = array(
            'offset'              => array(0, 500),
            'delay_ms'            => array(0, 60000),
            'border_width'        => array(0, 10),
            'border_radius'       => array(0, 40),
            'btn_primary_border_w' => array(0, 6),
            'btn_secondary_border_w' => array(0, 6),
            'btn_radius'          => array(0, 40),
            'thanks_autoclose_secs' => array(0, 30),
        );
        foreach ($int_fields as $key => $bounds) {
            if (isset($_POST[$key])) {
                $v = (int) wp_unslash($_POST[$key]);
                $cur[$key] = max($bounds[0], min($bounds[1], $v));
            }
        }

        
        $color_fields = array(
            'bg', 'text', 'icon_color', 'border_color',
            'btn_primary_bg', 'btn_primary_text', 'btn_primary_border',
            'btn_secondary_bg', 'btn_secondary_text', 'btn_secondary_border',
        );
        foreach ($color_fields as $key) {
            if (isset($_POST[$key])) {
                $v = sanitize_hex_color(wp_unslash((string) $_POST[$key]));
                if ($v !== null && $v !== '') { $cur[$key] = $v; }
            }
        }

        
        foreach (array('copy_title', 'copy_accept', 'copy_dismiss') as $key) {
            if (isset($_POST[$key])) {
                $cur[$key] = sanitize_text_field(wp_unslash((string) $_POST[$key]));
            }
        }
        if (isset($_POST['copy_body'])) {
            $cur['copy_body'] = sanitize_textarea_field(wp_unslash((string) $_POST['copy_body']));
        }
        foreach (array('copy_thanks_yes', 'copy_thanks_no') as $key) {
            if (isset($_POST[$key])) {
                $cur[$key] = sanitize_textarea_field(wp_unslash((string) $_POST[$key]));
            }
        }

        update_option(self::BANNER_OPTION, $cur, false);

        wp_send_json_success(array(
            'message' => __('Banner appearance saved. Refresh the storefront to see the change.', 'ai-eshop-optimizer'),
        ));
    }

    
    private function render_engine_status_card() {
        $is_live = self::is_engine_live();
        $status_label = $is_live
            ? __('LIVE — outbound actions are real', 'ai-eshop-optimizer')
            : __('PAUSED — every outbound action is blocked', 'ai-eshop-optimizer');
        $bg    = $is_live ? '#dcfce7' : '#fef3c7';
        $bdr   = $is_live ? '#15803d' : '#b45309';
        $color = $is_live ? '#14532d' : '#78350f';
        $next_action = $is_live ? __('Pause for demo / staging', 'ai-eshop-optimizer') : __('Resume — go live', 'ai-eshop-optimizer');
        $btn_color   = $is_live ? '#b45309' : '#15803d';

        echo '<div class="aieo-dmm-section">';
        echo '<div class="aieo-dmm-card" style="border-left:4px solid ' . esc_attr($bdr) . ';background:' . esc_attr($bg) . ';">';
        echo '<div style="display:flex;align-items:center;gap:18px;flex-wrap:wrap;">';
        echo '<div style="flex:1;min-width:280px;">';
        echo '<div style="font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:' . esc_attr($color) . ';">'
            . esc_html__('Engine status', 'ai-eshop-optimizer') . '</div>';
        echo '<div style="font-size:18px;font-weight:700;color:' . esc_attr($color) . ';margin-top:2px;">'
            . esc_html($status_label) . '</div>';
        echo '<p style="margin:6px 0 0;font-size:12px;color:' . esc_attr($color) . ';">';
        if ($is_live) {
            echo esc_html__('Push notifications, campaign launches, and newsletter syncs WILL fire when triggered. Pause before any demo / staging walkthrough.', 'ai-eshop-optimizer');
        } else {
            echo esc_html__('Send Test Push, Launch Campaign, Sync Cohort, and the nightly send-chain are all blocked. Recompute + segment assignment still run (read-only). Suppressed sends are logged with reason="engine_paused".', 'ai-eshop-optimizer');
        }
        echo '</p>';
        echo '</div>';
        echo '<div>';
        echo '<button type="button" class="button button-primary" id="aieo-engine-toggle" style="background:' . esc_attr($btn_color) . ';border-color:' . esc_attr($btn_color) . ';">';
        echo esc_html($next_action);
        echo '</button>';
        echo '<div id="aieo-engine-toggle-status" style="font-size:11px;color:#646970;margin-top:6px;text-align:right;"></div>';
        echo '</div>';
        echo '</div>';

        $nonce = wp_create_nonce(AIEO_DMM::NONCE_ACTION);
        $ajax  = admin_url('admin-ajax.php');
        ?>
        <script>
        (function(){
            const btn = document.getElementById('aieo-engine-toggle');
            const status = document.getElementById('aieo-engine-toggle-status');
            if (!btn) return;
            btn.addEventListener('click', async function(){
                btn.disabled = true;
                status.style.color = '#646970';
                status.textContent = '<?php echo esc_js(__('Switching…', 'ai-eshop-optimizer')); ?>';
                try {
                    const fd = new FormData();
                    fd.set('action', 'aieo_dmm_webpush_toggle_engine');
                    fd.set('nonce', '<?php echo esc_js($nonce); ?>');
                    const resp = await fetch('<?php echo esc_js($ajax); ?>', { method:'POST', body:fd, credentials:'same-origin' });
                    const data = await resp.json();
                    if (data && data.success) {
                        window.location.reload();
                    } else {
                        status.style.color = '#b32d2e';
                        status.textContent = (data && data.data && data.data.message) || 'Failed.';
                        btn.disabled = false;
                    }
                } catch (e) {
                    status.style.color = '#b32d2e';
                    status.textContent = String(e);
                    btn.disabled = false;
                }
            });
        })();
        </script>
        <?php
        echo '</div></div>';
    }

    public function ajax_toggle_engine() {
        AIEO_DMM::verify_ajax_request();
        $current = self::get_engine_status();
        $next = $current === self::ENGINE_STATUS_LIVE ? self::ENGINE_STATUS_PAUSED : self::ENGINE_STATUS_LIVE;
        self::set_engine_status($next);
        wp_send_json_success(array(
            'status'  => $next,
            'message' => $next === self::ENGINE_STATUS_LIVE
                ? __('Engine resumed — outbound actions are LIVE.', 'ai-eshop-optimizer')
                : __('Engine paused — outbound actions are blocked.', 'ai-eshop-optimizer'),
        ));
    }

    
    private function render_subscribers_card() {
        global $wpdb;
        $tbl = $wpdb->prefix . 'aieo_push_subscriptions';

        $total      = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl}");
        $active     = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl} WHERE status = 'active'");
        $logged_in  = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl} WHERE status = 'active' AND user_id > 0");
        $anonymous  = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl} WHERE status = 'active' AND user_id = 0");
        $by_device  = $wpdb->get_results("SELECT device_kind, COUNT(*) c FROM {$tbl} WHERE status='active' GROUP BY device_kind ORDER BY c DESC", ARRAY_A);

        
        $admin_id    = get_current_user_id();
        $has_admin_sub = $admin_id > 0 ? (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$tbl} WHERE user_id = %d AND status = 'active'",
            $admin_id
        )) : 0;

        echo '<div class="aieo-dmm-section">';
        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Push subscribers', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 10px;">'
            . esc_html__('Browser push subscriptions captured by the first-visit opt-in banner. Anonymous subscribers are scoped by an aieo_push_session cookie until they log in (then bind to their WP user_id).', 'ai-eshop-optimizer')
            . '</p>';

        echo '<div class="aieo-attr-tiles">';
        $this->tile(__('Active subscriptions', 'ai-eshop-optimizer'), number_format_i18n($active),
            __('Push subscriptions in status="active" — eligible for the next dispatch tick. Decreases when browsers revoke, when permission is denied, or after 410 Gone bounces.', 'ai-eshop-optimizer'));
        $this->tile(__('Logged-in', 'ai-eshop-optimizer'), number_format_i18n($logged_in),
            __('Active subscriptions tied to a wp_users id (subscribed while authenticated, or upgraded from anonymous on later login). These can be targeted by user-level triggers (replenishment, win-back, etc.).', 'ai-eshop-optimizer'));
        $this->tile(__('Anonymous', 'ai-eshop-optimizer'), number_format_i18n($anonymous),
            __('Active subscriptions scoped only by aieo_push_session cookie — visitor never logged in. Reachable only by session-level triggers (price-drop on viewed, back-in-stock on viewed). Bind to a user_id at first login.', 'ai-eshop-optimizer'));
        $this->tile(__('Total ever (incl. revoked)', 'ai-eshop-optimizer'), number_format_i18n($total),
            __('All subscriptions ever recorded, including those now revoked or bounced. Active ÷ Total ever = retention rate; 60–80% is normal after the first month.', 'ai-eshop-optimizer'));
        echo '</div>';

        if (!empty($by_device)) {
            echo '<p style="margin:14px 0 6px;font-size:12px;color:#646970;"><strong>' . esc_html__('By device:', 'ai-eshop-optimizer') . '</strong> ';
            $parts = array();
            foreach ($by_device as $r) {
                $parts[] = esc_html($r['device_kind']) . ': ' . esc_html(number_format_i18n((int) $r['c']));
            }
            echo implode(' &middot; ', $parts); 
            echo '</p>';
        }

        
        echo '<div style="margin-top:14px;">';
        if ($has_admin_sub > 0) {
            echo '<button type="button" class="button button-secondary" id="aieo-webpush-send-test">';
            echo esc_html__('Send test push to my browser', 'ai-eshop-optimizer');
            echo '</button>';
            echo ' <span id="aieo-webpush-send-test-status" style="margin-left:10px;color:#646970;font-size:13px;"></span>';
            echo $this->send_test_inline_js(); 
        } else {
            echo '<p style="color:#92400e;font-size:13px;margin:0;">'
                . esc_html__('To send a test push, first subscribe from the storefront (the banner appears 8s after page load on a fresh browser), then return here.', 'ai-eshop-optimizer')
                . ' <a href="' . esc_url(home_url('/')) . '" target="_blank">' . esc_html__('Open storefront', 'ai-eshop-optimizer') . ' →</a>'
                . '</p>';
        }
        echo '</div>';

        echo '</div></div>';
    }

    private function send_test_inline_js() {
        $nonce = wp_create_nonce(AIEO_DMM::NONCE_ACTION);
        $ajax  = admin_url('admin-ajax.php');
        ob_start();
        ?>
        <script>
        (function(){
            const btn = document.getElementById('aieo-webpush-send-test');
            const status = document.getElementById('aieo-webpush-send-test-status');
            if (!btn) return;
            btn.addEventListener('click', async function() {
                btn.disabled = true;
                status.style.color = '#646970';
                status.textContent = '<?php echo esc_js(__('Sending…', 'ai-eshop-optimizer')); ?>';
                try {
                    const fd = new FormData();
                    fd.set('action', 'aieo_dmm_webpush_send_test');
                    fd.set('nonce', '<?php echo esc_js($nonce); ?>');
                    const resp = await fetch('<?php echo esc_js($ajax); ?>', {
                        method: 'POST', body: fd, credentials: 'same-origin'
                    });
                    const data = await resp.json();
                    if (data && data.success) {
                        status.style.color = data.data.failed > 0 ? '#92400e' : '#15803d';
                        status.textContent = data.data.message;
                    } else {
                        status.style.color = '#b32d2e';
                        status.textContent = (data && data.data && data.data.message) || '<?php echo esc_js(__('Failed.', 'ai-eshop-optimizer')); ?>';
                    }
                } catch (e) {
                    status.style.color = '#b32d2e';
                    status.textContent = String(e);
                } finally {
                    btn.disabled = false;
                }
            });
        })();
        </script>
        <?php
        return ob_get_clean();
    }

    
    private function render_segments_card($tbl_ci) {
        global $wpdb;

        
        $total = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$tbl_ci} WHERE segments_json IS NOT NULL AND segments_json <> '[]' AND segments_json <> ''"
        );

        
        
        $segments = self::segment_catalog();
        $rows = array();
        foreach ($segments as $slug => $info) {
            $needle = '"' . $slug . '"';
            $count = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$tbl_ci} WHERE segments_json LIKE %s",
                '%' . $wpdb->esc_like($needle) . '%'
            ));
            $rows[$slug] = $count;
        }

        echo '<div class="aieo-dmm-section">';
        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Segments', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 10px;">'
            . esc_html__('Customer classifications computed by aieo_sp_assign_segments. A customer can belong to multiple segments simultaneously (e.g. VIP + Brand-Devoted).', 'ai-eshop-optimizer')
            . '</p>';

        if ($total === 0) {
            echo '<p style="color:#646970;">'
                . esc_html__('Segments not yet assigned. Click "Recompute now" above to chain segmentation.', 'ai-eshop-optimizer')
                . '</p>';
            echo '</div></div>';
            return;
        }

        echo '<table class="widefat striped aieo-attr-sortable-table">';
        echo '<thead><tr>';
        echo '<th data-sort-type="text">' . esc_html__('Segment', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th>' . esc_html__('Definition', 'ai-eshop-optimizer') . '</th>';
        echo '<th data-sort-type="num">' . esc_html__('Customers', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">' . esc_html__('Coverage', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '</tr></thead><tbody>';

        
        
        foreach ($segments as $slug => $info) {
            $count = (int) $rows[$slug];
            $pct   = $total > 0 ? round(100 * $count / $total, 1) : 0;
            echo '<tr>';
            echo '<td data-sort-value="' . esc_attr($slug) . '">';
            echo '<strong>' . esc_html($info['label']) . '</strong><br>';
            echo '<code style="font-size:11px;color:#646970;">' . esc_html($slug) . '</code>';
            echo '</td>';
            echo '<td style="font-size:12px;color:#374151;">' . esc_html($info['rule']) . '</td>';
            echo '<td data-sort-value="' . $count . '">' . esc_html(number_format_i18n($count)) . '</td>'; 
            echo '<td data-sort-value="' . esc_attr((string) $pct) . '">';
            echo '<div style="display:inline-block;min-width:120px;background:#f3f4f6;border-radius:4px;overflow:hidden;vertical-align:middle;">';
            echo '<div style="height:14px;background:' . esc_attr($info['color']) . ';width:' . esc_attr((string) $pct) . '%;"></div>';
            echo '</div> <span style="color:#1f2937;font-size:12px;">' . esc_html((string) $pct) . '%</span>';
            echo '</td>';
            echo '</tr>';
        }
        echo '</tbody></table>';
        echo '</div></div>';
    }

    
    public static function segment_catalog() {
        return array(
            'vip' => array(
                'label' => __('VIP', 'ai-eshop-optimizer'),
                'color' => '#dc2626',
                'rule'  => __('Top-quintile LTV (monetary band 5) AND active (recency band ≥ 4).', 'ai-eshop-optimizer'),
            ),
            'loyalist' => array(
                'label' => __('Loyalist', 'ai-eshop-optimizer'),
                'color' => '#7c3aed',
                'rule'  => __('5+ orders AND active (recency band ≥ 3) AND not VIP.', 'ai-eshop-optimizer'),
            ),
            'brand_devoted' => array(
                'label' => __('Brand-Devoted', 'ai-eshop-optimizer'),
                'color' => '#0891b2',
                'rule'  => __('Top brand spend share > 70% AND ≥ 3 orders.', 'ai-eshop-optimizer'),
            ),
            'replenisher' => array(
                'label' => __('Replenisher', 'ai-eshop-optimizer'),
                'color' => '#15803d',
                'rule'  => __('≥ 3 orders AND avg days-between-orders 0.1–30 (predictable cadence).', 'ai-eshop-optimizer'),
            ),
            'lapsed' => array(
                'label' => __('Lapsed', 'ai-eshop-optimizer'),
                'color' => '#ea580c',
                'rule'  => __('Overdue relative to their OWN cadence — recency_days > 2.5 × avg_dbo (≥ 14 absolute days). The right target for win-back triggers; only meaningful because AIEO tracks per-customer DBO.', 'ai-eshop-optimizer'),
            ),
            'newbie' => array(
                'label' => __('Newbie', 'ai-eshop-optimizer'),
                'color' => '#f59e0b',
                'rule'  => __('Exactly 1 order AND not yet dormant.', 'ai-eshop-optimizer'),
            ),
            'dormant' => array(
                'label' => __('Dormant', 'ai-eshop-optimizer'),
                'color' => '#6b7280',
                'rule'  => __('Recency ≥ 60 days AND has past orders AND in bottom-40% recency band (so dormant doesn\'t co-fire with VIP/Loyalist on uniformly-old datasets).', 'ai-eshop-optimizer'),
            ),
            'discount_hunter' => array(
                'label' => __('Discount Hunter', 'ai-eshop-optimizer'),
                'color' => '#b91c1c',
                'rule'  => __('discount_share ≥ 0.5 — at least half of their order lines were bought with a coupon applied. Computed from wp_woocommerce_order_itemmeta._line_total < _line_subtotal. Prime audience for promo-led messaging, free-shipping-threshold campaigns, and clearance-event pushes.', 'ai-eshop-optimizer'),
            ),
        );
    }

    private function list_candidate_source_tables() {
        global $wpdb;
        $out = array();
        
        $temp = $wpdb->prefix . 'aieo_temp_orders';
        $temp_rows = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$temp} WHERE EponymousID IS NOT NULL AND EponymousID > 0"
        );
        if ($temp_rows > 0) {
            $out[] = array('name' => $temp, 'rows' => $temp_rows);
        }
        
        $snapshots = $wpdb->get_results(
            "SELECT TABLE_NAME, TABLE_ROWS
             FROM INFORMATION_SCHEMA.TABLES
             WHERE TABLE_SCHEMA = DATABASE()
               AND TABLE_NAME REGEXP '^{$wpdb->prefix}aieo_[0-9]{4}_[0-9]{2}_orders\$'
             ORDER BY TABLE_NAME DESC
             LIMIT 12",
            ARRAY_A
        );
        foreach ((array) $snapshots as $r) {
            $out[] = array('name' => (string) $r['TABLE_NAME'], 'rows' => (int) $r['TABLE_ROWS']);
        }
        return $out;
    }

    private function render_top_customers_card($tbl_ci) {
        global $wpdb;
        $rows = $wpdb->get_results(
            "SELECT eponymous_id, wp_user_id, email, orders_count, ltv_total, aov, recency_days,
                    top_brand_id, top_brand_share, top_category_id,
                    brand_affinity_json, replenishment_json, segments_json, rfm_cell,
                    last_order_at, source_table
             FROM {$tbl_ci}
             ORDER BY ltv_total DESC, orders_count DESC
             LIMIT 50",
            ARRAY_A
        );

        echo '<div class="aieo-dmm-section">';
        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Top 50 customers by LTV', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:0 0 8px;">'
            . esc_html__('Sanity check — your highest-value customers. Brand/category affinity is computed from line-level spend; DBO columns come from the AIEO advanced behavior step. RFM bands and segments populate in Phase 2.', 'ai-eshop-optimizer')
            . '</p>';

        if (empty($rows)) {
            echo '<p style="color:#646970;">'
                . esc_html__('No intelligence rows yet. Click "Recompute now" above.', 'ai-eshop-optimizer')
                . '</p>';
            echo '</div></div>';
            return;
        }

        echo '<table class="widefat striped aieo-attr-sortable-table">';
        echo '<thead><tr>';
        echo '<th style="width:50px;">#</th>';
        echo '<th data-sort-type="num">'  . esc_html__('Eponymous', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="text">' . esc_html__('Customer', 'ai-eshop-optimizer')  . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="text">' . esc_html__('RFM', 'ai-eshop-optimizer')      . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">'  . esc_html__('Orders', 'ai-eshop-optimizer')   . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">'  . esc_html__('LTV', 'ai-eshop-optimizer')      . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">'  . esc_html__('AOV', 'ai-eshop-optimizer')      . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">'  . esc_html__('Recency (d)', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="num">'  . esc_html__('Avg DBO', 'ai-eshop-optimizer')  . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th data-sort-type="text">' . esc_html__('Top brand', 'ai-eshop-optimizer') . ' <span class="aieo-attr-dt-sort-ind">⇅</span></th>';
        echo '<th>' . esc_html__('Segments', 'ai-eshop-optimizer') . '</th>';
        echo '</tr></thead><tbody>';

        $catalog = self::segment_catalog();

        $vitals_table = $wpdb->prefix . 'aieo_core_product_vitals';

        foreach ($rows as $i => $r) {
            $epo       = (int)   $r['eponymous_id'];
            $wp_uid    = (int)   $r['wp_user_id'];
            $email     = (string) $r['email'];
            $orders    = (int)   $r['orders_count'];
            $ltv       = (float) $r['ltv_total'];
            $aov       = (float) $r['aov'];
            $recency   = (int)   $r['recency_days'];
            $brand_id  = (int)   $r['top_brand_id'];
            $brand_share = (float) $r['top_brand_share'];
            $brand_name = $brand_id > 0 ? $this->lookup_brand_name($brand_id, $vitals_table) : '';
            $avg_dbo    = $this->extract_dbo($r['replenishment_json'], 'avg_dbo');
            $rfm_cell   = (string) $r['rfm_cell'];
            $segments   = $this->parse_segments($r['segments_json']);

            $edit_url   = $wp_uid > 0 ? admin_url('user-edit.php?user_id=' . $wp_uid) : '';
            $display    = $email !== '' ? $email : ('#' . $epo);

            echo '<tr>';
            echo '<td>' . (int) ($i + 1) . '</td>';
            echo '<td data-sort-value="' . $epo . '"><code>' . $epo . '</code></td>'; 
            echo '<td data-sort-value="' . esc_attr($display) . '">';
            if ($edit_url !== '') {
                echo '<strong><a href="' . esc_url($edit_url) . '">' . esc_html($display) . '</a></strong>';
            } else {
                echo '<strong>' . esc_html($display) . '</strong>';
            }
            if ($wp_uid > 0) {
                echo '<br><small style="color:#646970;">WP user: ' . $wp_uid . '</small>'; 
            }
            echo '</td>';
            echo '<td data-sort-value="' . esc_attr($rfm_cell) . '">'
                . ($rfm_cell !== '' ? '<code style="font-size:12px;color:#1f2937;">' . esc_html($rfm_cell) . '</code>' : '—')
                . '</td>';
            echo '<td data-sort-value="' . $orders . '">' . esc_html(number_format_i18n($orders)) . '</td>'; 
            echo '<td data-sort-value="' . $ltv . '">' . esc_html($this->fmt_money($ltv)) . '</td>'; 
            echo '<td data-sort-value="' . $aov . '">' . esc_html($this->fmt_money($aov)) . '</td>'; 
            echo '<td data-sort-value="' . $recency . '">' . esc_html(number_format_i18n($recency)) . '</td>'; 
            echo '<td data-sort-value="' . esc_attr((string) $avg_dbo) . '">' . esc_html(number_format_i18n((float) $avg_dbo, 1)) . '</td>';
            echo '<td data-sort-value="' . esc_attr($brand_name) . '">';
            if ($brand_name !== '') {
                echo esc_html($brand_name);
                if ($brand_share > 0) {
                    echo ' <small style="color:#646970;">' . esc_html(round($brand_share * 100, 1)) . '%</small>';
                }
            } else {
                echo '—';
            }
            echo '</td>';
            echo '<td>';
            if (!empty($segments)) {
                foreach ($segments as $slug) {
                    $info = isset($catalog[$slug]) ? $catalog[$slug] : null;
                    $color = $info ? $info['color'] : '#6b7280';
                    $label = $info ? $info['label'] : $slug;
                    echo '<span style="display:inline-block;padding:2px 7px;margin:1px 3px 1px 0;'
                        . 'border-radius:10px;background:' . esc_attr($color) . ';color:#fff;'
                        . 'font-size:10px;font-weight:600;letter-spacing:.02em;">'
                        . esc_html($label) . '</span>';
                }
            } else {
                echo '<span style="color:#9ca3af;">—</span>';
            }
            echo '</td>';
            echo '</tr>';
        }
        echo '</tbody></table></div></div>';
    }

    
    private function parse_segments($json) {
        if (!is_string($json) || $json === '' || $json === '[]') { return array(); }
        $arr = json_decode($json, true);
        if (!is_array($arr)) { return array(); }
        $out = array();
        foreach ($arr as $v) {
            if (is_string($v) && $v !== '') { $out[] = $v; }
        }
        return $out;
    }

    
    
    

    private function tile($label, $value, $help = '') {
        echo '<div class="aieo-attr-tile">';
        echo '<div class="aieo-attr-tile-label">';
        echo '<span>' . esc_html($label) . '</span>';
        if ($help !== '') {
            echo '<span class="aieo-attr-tile-help" aria-label="' . esc_attr($help) . '">i</span>';
        }
        echo '</div>';
        echo '<div class="aieo-attr-tile-value">' . esc_html((string) $value) . '</div>';
        if ($help !== '') {
            echo '<div class="aieo-attr-tile-popover">' . esc_html($help) . '</div>';
        }
        echo '</div>';
    }

    private function fmt_money($amount) {
        if (function_exists('wc_price')) {
            return wp_strip_all_tags(wc_price((float) $amount));
        }
        return number_format_i18n((float) $amount, 2);
    }

    private function median_aov($tbl_ci) {
        global $wpdb;
        $count = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$tbl_ci} WHERE aov > 0");
        if ($count <= 0) { return 0.0; }
        $offset = (int) floor($count / 2);
        return (float) $wpdb->get_var(
            "SELECT aov FROM {$tbl_ci} WHERE aov > 0 ORDER BY aov LIMIT 1 OFFSET {$offset}"
        );
    }

    
    private function lookup_brand_name($brand_id, $vitals_table) {
        global $wpdb;
        static $cache = array();
        $brand_id = (int) $brand_id;
        if ($brand_id <= 0) { return ''; }
        if (isset($cache[$brand_id])) { return $cache[$brand_id]; }
        $name = (string) $wpdb->get_var($wpdb->prepare(
            "SELECT brand FROM {$vitals_table} WHERE brand_id = %d AND brand <> '' LIMIT 1",
            $brand_id
        ));
        $cache[$brand_id] = $name;
        return $name;
    }

    private function lookup_category_name($head_id, $vitals_table) {
        global $wpdb;
        static $cache = array();
        $head_id = (int) $head_id;
        if ($head_id <= 0) { return ''; }
        if (isset($cache[$head_id])) { return $cache[$head_id]; }
        $name = (string) $wpdb->get_var($wpdb->prepare(
            "SELECT head_category FROM {$vitals_table} WHERE head_id = %d AND head_category <> '' LIMIT 1",
            $head_id
        ));
        $cache[$head_id] = $name;
        return $name;
    }

    
    private function fmt_affinity_top($json, $vitals_table, $kind) {
        if (!is_string($json) || $json === '' || $json === '{}') { return '—'; }
        $arr = json_decode($json, true);
        if (!is_array($arr) || empty($arr)) { return '—'; }
        arsort($arr);
        $top = array_slice($arr, 0, 3, true);
        $parts = array();
        foreach ($top as $term_id => $share) {
            $name = $kind === 'brand'
                ? $this->lookup_brand_name((int) $term_id, $vitals_table)
                : $this->lookup_category_name((int) $term_id, $vitals_table);
            if ($name === '') { $name = '#' . (int) $term_id; }
            $parts[] = $name . ' ' . round((float) $share * 100, 1) . '%';
        }
        return implode(', ', $parts);
    }

    private function extract_dbo($json, $key) {
        if (!is_string($json) || $json === '') { return 0; }
        $arr = json_decode($json, true);
        if (!is_array($arr) || !isset($arr[$key])) { return 0; }
        return (float) $arr[$key];
    }

    
    private function recompute_inline_js() {
        $nonce = wp_create_nonce(AIEO_DMM::NONCE_ACTION);
        $ajax  = admin_url('admin-ajax.php');
        ob_start();
        ?>
        <script>
        (function(){
            const btn = document.getElementById('aieo-webpush-recompute');
            const status = document.getElementById('aieo-webpush-recompute-status');
            if (!btn) return;
            btn.addEventListener('click', async function() {
                btn.disabled = true;
                status.style.color = '#646970';
                status.textContent = '<?php echo esc_js(__('Recomputing… this can take ~30s on large stores.', 'ai-eshop-optimizer')); ?>';
                try {
                    const fd = new FormData();
                    fd.set('action', 'aieo_dmm_webpush_recompute');
                    fd.set('nonce', '<?php echo esc_js($nonce); ?>');
                    fd.set('offset', '0');
                    const picker = document.getElementById('aieo-webpush-source-table');
                    if (picker && picker.value) {
                        fd.set('source_table', picker.value);
                    }
                    const resp = await fetch('<?php echo esc_js($ajax); ?>', {
                        method: 'POST', body: fd, credentials: 'same-origin'
                    });
                    const data = await resp.json();
                    if (data && data.success) {
                        status.style.color = '#15803d';
                        status.textContent = data.data.message;
                        setTimeout(() => { window.location.reload(); }, 1500);
                    } else {
                        status.style.color = '#b32d2e';
                        status.textContent = (data && data.data && data.data.message) || '<?php echo esc_js(__('Failed.', 'ai-eshop-optimizer')); ?>';
                        btn.disabled = false;
                    }
                } catch (e) {
                    status.style.color = '#b32d2e';
                    status.textContent = String(e);
                    btn.disabled = false;
                }
            });
        })();
        </script>
        <?php
        return ob_get_clean();
    }
}
