<?php

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

class AIEO_DMM_Cancellations extends AIEO_DMM_Module_Base {

    public function get_id()       { return 'cancellations'; }
    public function get_label()    { return __('Cancellations', 'ai-eshop-optimizer'); }
    public function get_priority() { return 45; }
    public function get_group()    { return 'customer_profiling'; }

    public function is_pro_locked() { return true; }
    public function get_pro_promo() {
        return array(
            'title'       => __('Cancellations Intelligence & Prevention', 'ai-eshop-optimizer'),
            'description' => __('A full-stack defence against the silent profit leak that is cancelled / refunded / failed orders. Captures WHY customers cancel at the moment it happens, builds a per-customer risk profile from their history, and uses that profile to PREVENT the next cancellation before it costs you anything — automatic COD blocking and warning notices at checkout for repeat cancellers and returners.', 'ai-eshop-optimizer'),
            'bullets'     => array(
                __('Live capture: every cancel / refund flips into a structured row with reason, products, lost revenue and refund type.', 'ai-eshop-optimizer'),
                __('Per-customer risk_band (0 = normal · 1 = elevated · 2 = high) derived from their cancel + return + refund pattern.', 'ai-eshop-optimizer'),
                __('🛡️ Preventative — auto-block Cash-on-Delivery for high-risk users so chronic refusers cannot place another COD order that costs you outbound + return shipping.', 'ai-eshop-optimizer'),
                __('🛡️ Preventative — optional warning notice on the checkout page for elevated / high-risk customers (purely informational; the customer still places the order).', 'ai-eshop-optimizer'),
                __('Per-product cancellation + return rates — find the SKUs whose PDP copy or photography is misleading customers.', 'ai-eshop-optimizer'),
                __('Cross-cuts with attribution + campaigns — see which traffic source and which UTM campaign deliver the highest-cancellation cohort.', 'ai-eshop-optimizer'),
                __('Outbound carrier-cost resolver — reads the actual courier cost behind every cancelled-after-shipment order so the ROI report is honest, not aspirational.', 'ai-eshop-optimizer'),
                __('Five drill-down tabs — Overview · Returns ledger · Products · Customers · Reason vocabulary editor.', 'ai-eshop-optimizer'),
            ),
        );
    }

    public function register_hooks() {
        add_action('wp_ajax_aieo_dmm_cancel_reason_save',     array($this, 'ajax_save_reason'));
        add_action('wp_ajax_aieo_dmm_cancel_reason_disable',  array($this, 'ajax_disable_reason'));
        add_action('wp_ajax_aieo_dmm_metrics_backfill',       array($this, 'ajax_metrics_backfill'));
        add_action('wp_ajax_aieo_dmm_metrics_backfill_step',  array($this, 'ajax_metrics_backfill_step'));
        add_action('wp_ajax_aieo_dmm_metrics_recompute_stale', array($this, 'ajax_metrics_recompute_stale'));
        add_action('wp_ajax_aieo_dmm_risk_gating_toggle',      array($this, 'ajax_risk_gating_toggle'));
        add_action('wp_ajax_aieo_dmm_outbound_cost_step',      array($this, 'ajax_outbound_cost_step'));
    }

    public function render_subtab() {
        $base_url = admin_url('admin.php?page=' . AIEO_DMM::MENU_SLUG . '&dmm_tab=cancellations');
        $section  = isset($_GET['cn_section']) ? sanitize_key((string) $_GET['cn_section']) : 'overview';
        if (!in_array($section, array('overview', 'returns', 'products', 'customers', 'vocabulary'), true)) { $section = 'overview'; }

        echo '<div class="aieo-dmm-section">';
        echo '<h2>' . esc_html__('Order Cancellations', 'ai-eshop-optimizer') . '</h2>';
        echo '<p class="description">' . esc_html__('Captured at the WooCommerce order edit screen. Per-order reasons + per-line refund logs feed the recommendation engine via the product / user behavior metrics tables.', 'ai-eshop-optimizer') . '</p>';

        echo '<div class="nav-tab-wrapper" style="margin-bottom:14px;">';
        foreach (array(
            'overview'   => __('Overview', 'ai-eshop-optimizer'),
            'returns'    => __('Returns ledger', 'ai-eshop-optimizer'),
            'products'   => __('Products', 'ai-eshop-optimizer'),
            'customers'  => __('Customers', 'ai-eshop-optimizer'),
            'vocabulary' => __('Reason vocabulary', 'ai-eshop-optimizer'),
        ) as $slug => $label) {
            $active = $section === $slug ? ' nav-tab-active' : '';
            echo '<a class="nav-tab' . $active . '" href="' . esc_url($base_url . '&cn_section=' . $slug) . '">' . esc_html($label) . '</a>'; 
        }
        echo '</div>';

        if      ($section === 'vocabulary') { $this->render_vocabulary(); }
        elseif  ($section === 'returns')    { $this->render_returns_ledger($base_url); }
        elseif  ($section === 'products')   { $this->render_products($base_url); }
        elseif  ($section === 'customers')  { $this->render_customers($base_url); }
        else                                { $this->render_overview(); }

        echo '</div>';
    }

    
    
    

    private function render_operator_guide_card() {
        echo '<div class="aieo-dmm-card" style="margin-bottom:14px;border-left:4px solid #0ea5e9;background:#f0f9ff;">';
        echo '<details>';
        echo '<summary style="cursor:pointer;font-size:14px;font-weight:600;">📖 ' . esc_html__('Operator guide — how to log cancellations &amp; returns', 'ai-eshop-optimizer') . '</summary>';
        ?>
        <div style="margin-top:14px;line-height:1.55;font-size:13px;color:#0c4a6e;">

            <h4 style="margin-top:0;color:#0c4a6e;"><?php esc_html_e('1. The four event types — different costs AND different recommendation-engine signals', 'ai-eshop-optimizer'); ?></h4>
            <p><?php echo wp_kses_post(__('When an order doesn&#8217;t complete normally, there are <strong>four distinct cases</strong>. Each carries a different cost to the shop <em>and</em> a different signal to the recommendation algorithm:', 'ai-eshop-optimizer')); ?></p>
            <table class="widefat striped" style="font-size:12px;">
                <thead><tr>
                    <th><?php esc_html_e('Type', 'ai-eshop-optimizer'); ?></th>
                    <th><?php esc_html_e('When it happens', 'ai-eshop-optimizer'); ?></th>
                    <th><?php esc_html_e('Shipping cost', 'ai-eshop-optimizer'); ?></th>
                    <th><?php esc_html_e('Signal about the PRODUCT', 'ai-eshop-optimizer'); ?></th>
                    <th><?php esc_html_e('Signal about the CUSTOMER', 'ai-eshop-optimizer'); ?></th>
                </tr></thead>
                <tbody>
                    <tr>
                        <td><strong style="color:#6b7280;"><?php esc_html_e('Cancellation', 'ai-eshop-optimizer'); ?></strong></td>
                        <td><?php echo wp_kses_post(__('Voided <strong>before</strong> shipping (out of stock, payment failed, duplicate order)', 'ai-eshop-optimizer')); ?></td>
                        <td><?php esc_html_e('❌ None', 'ai-eshop-optimizer'); ?></td>
                        <td>—</td>
                        <td>—</td>
                    </tr>
                    <tr>
                        <td><strong style="color:#b91c1c;"><?php esc_html_e('Returned', 'ai-eshop-optimizer'); ?></strong></td>
                        <td><?php echo wp_kses_post(__('Shipped, customer <strong>received it, opened it, sent it back</strong> (wrong size, quality issue)', 'ai-eshop-optimizer')); ?></td>
                        <td><?php esc_html_e('✅ Outbound + Return', 'ai-eshop-optimizer'); ?></td>
                        <td><?php echo wp_kses_post(__('<strong style="color:#b91c1c;">STRONG NEGATIVE</strong> — they saw it and rejected it', 'ai-eshop-optimizer')); ?></td>
                        <td><?php esc_html_e('Mild', 'ai-eshop-optimizer'); ?></td>
                    </tr>
                    <tr>
                        <td><?php echo wp_kses_post(__('<strong style="color:#ca8a04;">Kept</strong> (refund only)', 'ai-eshop-optimizer')); ?></td>
                        <td><?php echo wp_kses_post(__('Shipped, we refunded <strong>but told them to keep the item</strong>', 'ai-eshop-optimizer')); ?></td>
                        <td><?php esc_html_e('✅ Outbound only', 'ai-eshop-optimizer'); ?></td>
                        <td><?php esc_html_e('Weak', 'ai-eshop-optimizer'); ?></td>
                        <td><?php esc_html_e('Mild', 'ai-eshop-optimizer'); ?></td>
                    </tr>
                    <tr>
                        <td><?php echo wp_kses_post(__('<strong style="color:#7c3aed;">Refused</strong> (never received)', 'ai-eshop-optimizer')); ?></td>
                        <td><?php echo wp_kses_post(__('Shipped but the customer <strong>never received it</strong> — refused at the door, never collected from the pickup point, address unreachable', 'ai-eshop-optimizer')); ?></td>
                        <td><?php esc_html_e('✅ Outbound + Return (carrier brought it back)', 'ai-eshop-optimizer'); ?></td>
                        <td><?php echo wp_kses_post(__('<strong>Almost zero</strong> — never opened', 'ai-eshop-optimizer')); ?></td>
                        <td><?php echo wp_kses_post(__('<strong style="color:#7c3aed;">STRONGEST NEGATIVE</strong> — flaky / unreliable customer', 'ai-eshop-optimizer')); ?></td>
                    </tr>
                </tbody>
            </table>
            <p style="background:#ede9fe;padding:10px;border-radius:4px;color:#5b21b6;">
                💡 <?php echo wp_kses_post(__('<strong>Why the Returned vs Refused distinction matters:</strong> A product with 10% &laquo;Returned&raquo; rate (customers who saw it and didn&#8217;t want it) is a bad product — the recommendation engine down-ranks it. A product with 10% &laquo;Refused&raquo; rate tells you nothing about the product (those customers never opened it). Instead the algorithm penalises <strong>those customers</strong> with a higher risk band — they&#8217;re the COD-blocking candidates.', 'ai-eshop-optimizer')); ?>
            </p>

            <h4 style="color:#0c4a6e;"><?php esc_html_e('2. How to log a cancellation / return', 'ai-eshop-optimizer'); ?></h4>
            <ol>
                <li><?php echo wp_kses_post(__('Open the order in <strong>WooCommerce → Orders</strong>.', 'ai-eshop-optimizer')); ?></li>
                <li><?php echo wp_kses_post(__('In the right sidebar find the <strong>&laquo;AIEO Cancellation Reason&raquo;</strong> metabox.', 'ai-eshop-optimizer')); ?></li>
                <li><?php echo wp_kses_post(__('Pick the <strong>Reason</strong> from the dropdown — e.g. &laquo;Wrong size / fit (returned after receipt)&raquo;.', 'ai-eshop-optimizer')); ?>
                    <br><em style="color:#0369a1;"><?php esc_html_e('The correct Type (Cancellation / Returned / Kept / Refused) auto-fills based on the reason. Override it manually if needed.', 'ai-eshop-optimizer'); ?></em></li>
                <li><?php echo wp_kses_post(__('Add an optional <strong>Note</strong> with details.', 'ai-eshop-optimizer')); ?></li>
                <li><?php echo wp_kses_post(__('If it&#8217;s a <strong>multi-item order</strong>, see partial-return below.', 'ai-eshop-optimizer')); ?></li>
                <li><?php echo wp_kses_post(__('Fill the <strong>shipping cost</strong> fields (only shown for post-shipment types):', 'ai-eshop-optimizer')); ?>
                    <ul>
                        <li><?php echo wp_kses_post(__('<strong>Outbound</strong>: what we paid the courier to send it. Leave blank to auto-resolve from the carrier rate.', 'ai-eshop-optimizer')); ?></li>
                        <li><?php echo wp_kses_post(__('<strong>Return</strong>: what we paid to fetch it back. Enter <strong>manually</strong> — e.g. €3.50.', 'ai-eshop-optimizer')); ?></li>
                    </ul>
                </li>
                <li><?php echo wp_kses_post(__('Click <strong>Update</strong> or change the order status. The log row is captured automatically.', 'ai-eshop-optimizer')); ?></li>
            </ol>

            <h4 style="color:#0c4a6e;"><?php esc_html_e('3. Partial returns (multi-item orders)', 'ai-eshop-optimizer'); ?></h4>
            <p><?php esc_html_e('If a customer ordered 3 items and only returns 1:', 'ai-eshop-optimizer'); ?></p>
            <ol>
                <li><?php echo wp_kses_post(__('The <strong>AIEO Cancellation Reason</strong> metabox shows a <strong>checkbox list</strong> of every line item (only for orders with 2+ products — single-item orders skip the picker).', 'ai-eshop-optimizer')); ?></li>
                <li><?php echo wp_kses_post(__('✅ <strong>Tick only the items being returned</strong>.', 'ai-eshop-optimizer')); ?></li>
                <li><?php esc_html_e('Leave all checkboxes blank to apply to the whole order.', 'ai-eshop-optimizer'); ?></li>
                <li><?php echo wp_kses_post(__('The Returns ledger shows <em>Partial</em> in the Scope column automatically.', 'ai-eshop-optimizer')); ?></li>
            </ol>
            <p><?php echo wp_kses_post(__('<strong>Important:</strong> the checkboxes affect <strong>analytics only</strong> — the actual money refund happens through WooCommerce&#8217;s standard <em>Refund</em> button on the order edit screen.', 'ai-eshop-optimizer')); ?></p>

            <h4 style="color:#0c4a6e;"><?php esc_html_e('4. Where to find the reports', 'ai-eshop-optimizer'); ?></h4>
            <ul>
                <li><?php echo wp_kses_post(__('<strong>Overview</strong>: headline metrics, top reasons, top high-cancellation products + customers.', 'ai-eshop-optimizer')); ?></li>
                <li><?php echo wp_kses_post(__('<strong>Returns ledger</strong>: every event with filters (reason, date, type, scope) + cost summary.', 'ai-eshop-optimizer')); ?></li>
                <li><?php echo wp_kses_post(__('<strong>Products</strong>: sortable list of every product with returns + per-product drill-down to chronological events.', 'ai-eshop-optimizer')); ?></li>
                <li><?php echo wp_kses_post(__('<strong>Customers</strong>: sortable list of customers with total purchases vs returns + risk band.', 'ai-eshop-optimizer')); ?></li>
                <li><?php echo wp_kses_post(__('<strong>Reason vocabulary</strong>: add/edit reasons that appear in the order metabox dropdown. Each reason can be assigned a <strong>default Type</strong> so it auto-fills.', 'ai-eshop-optimizer')); ?></li>
            </ul>

            <h4 style="color:#0c4a6e;"><?php esc_html_e('5. Real-world examples', 'ai-eshop-optimizer'); ?></h4>
            <ul>
                <li>⚪ <?php echo wp_kses_post(__('<strong>Customer phoned and asked to cancel before we shipped</strong> → Reason: &laquo;Customer changed mind&raquo;, Type: <em>Cancellation</em>, no cost, no signal.', 'ai-eshop-optimizer')); ?></li>
                <li>⚪ <?php echo wp_kses_post(__('<strong>One product was out of stock, we voided the whole order</strong> → Reason: &laquo;Out of stock&raquo;, Type: <em>Cancellation</em> (auto), no cost.', 'ai-eshop-optimizer')); ?></li>
                <li>🔴 <?php echo wp_kses_post(__('<strong>Ordered 3, sent back 1 (wrong size)</strong> → Reason: &laquo;Wrong size / fit (returned after receipt)&raquo;, Type: <em>Returned</em> (auto), tick the one item being returned, fill in the return shipping cost. <strong>The product loses points in the recommendations.</strong>', 'ai-eshop-optimizer')); ?></li>
                <li>🟡 <?php echo wp_kses_post(__('<strong>Quality complaint — we let them keep the item and refunded</strong> → Reason: &laquo;Refunded but customer kept items&raquo;, Type: <em>Kept</em> (auto), outbound cost only. Mild product signal.', 'ai-eshop-optimizer')); ?></li>
                <li>🟣 <?php echo wp_kses_post(__('<strong>Courier returned it because the address was unreachable</strong> → Reason: &laquo;Address unreachable&raquo;, Type: <em>Refused</em> (auto), both shipping costs apply. <strong>The customer goes to a higher risk band — but the product isn&#8217;t penalised.</strong>', 'ai-eshop-optimizer')); ?></li>
                <li>🟣 <?php echo wp_kses_post(__('<strong>Customer refused delivery at the door</strong> → Reason: &laquo;Refused on arrival&raquo;, Type: <em>Refused</em> (auto). Same as above — high risk band, zero product signal.', 'ai-eshop-optimizer')); ?></li>
                <li>🟣 <?php echo wp_kses_post(__('<strong>Left the parcel at the courier pickup point and never came to collect it</strong> → Reason: &laquo;Never collected from pickup point&raquo;, Type: <em>Refused</em>. Statistically the riskiest customers — COD-block candidates.', 'ai-eshop-optimizer')); ?></li>
            </ul>

            <p style="background:#fef3c7;padding:10px;border-radius:4px;margin-top:14px;color:#78350f;">
                💡 <?php echo wp_kses_post(__('<strong>Tip:</strong> before you change the order status to &laquo;Cancelled&raquo; or &laquo;Refunded&raquo;, <strong>fill in the reason and type first</strong> in the metabox — that way the log row is captured with the right data from the start. Editing afterwards also works (the row updates in place).', 'ai-eshop-optimizer')); ?>
            </p>

            <p style="margin-top:14px;color:#6b7280;font-size:11px;">
                <?php
                
                echo wp_kses_post(sprintf(__('Need this in Greek for shop staff? See %s in the plugin folder.', 'ai-eshop-optimizer'), '<code>docs/CANCELLATIONS_OPERATOR_GUIDE_EL.md</code>'));
                ?>
            </p>
        </div>
        </details>
        <?php
        echo '</div>';
    }

    
    
    

    private function render_products($base_url) {
        $product_id = isset($_GET['rl_pid']) ? (int) $_GET['rl_pid'] : 0;
        if ($product_id > 0) {
            $this->render_product_detail($product_id, $base_url);
            return;
        }
        $this->render_products_list($base_url);
    }

    private function render_products_list($base_url) {
        global $wpdb;
        $pbm = $wpdb->prefix . 'aieo_dmm_product_behavior_metrics';
        $reasons_index = class_exists('AIEO_DMM_Cancellation_Reasons_Data') ? AIEO_DMM_Cancellation_Reasons_Data::all() : array();

        $sort = isset($_GET['rl_sort']) ? sanitize_key((string) $_GET['rl_sort']) : 'rate';
        $sort_map = array(
            'rate'   => 'cancellation_rate DESC, total_cancellations DESC',
            'count'  => 'total_cancellations DESC, cancellation_rate DESC',
            'last'   => 'last_cancelled_at DESC',
            'orders' => 'total_orders DESC',
        );
        $order_by = $sort_map[$sort] ?? $sort_map['rate'];

        $min_orders = isset($_GET['rl_min']) ? max(0, (int) $_GET['rl_min']) : 5;
        $reason     = isset($_GET['rl_reason']) ? sanitize_key((string) $_GET['rl_reason']) : '';

        $where = array('total_cancellations > 0', 'total_orders >= %d');
        $params = array($min_orders);
        if ($reason !== '') { $where[] = 'top_reason_slug = %s'; $params[] = $reason; }
        $where_sql = implode(' AND ', $where);

        $count_sql = "SELECT COUNT(*) FROM {$pbm} WHERE {$where_sql}";
        $total = (int) $wpdb->get_var($wpdb->prepare($count_sql, ...$params));

        $per_page = 50;
        $pageno   = isset($_GET['rl_p']) ? max(1, (int) $_GET['rl_p']) : 1;
        $offset   = ($pageno - 1) * $per_page;

        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT product_id, total_orders, total_cancellations, total_refund_lines,
                    cancellation_rate, refund_rate, last_cancelled_at, top_reason_slug
               FROM {$pbm}
              WHERE {$where_sql}
              ORDER BY {$order_by}
              LIMIT %d OFFSET %d",
            ...array_merge($params, array($per_page, $offset))
        ), ARRAY_A);

        
        echo '<form method="get" style="margin:6px 0 14px;display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap;">';
        echo '<input type="hidden" name="page" value="' . esc_attr(AIEO_DMM::MENU_SLUG) . '">';
        echo '<input type="hidden" name="dmm_tab" value="cancellations">';
        echo '<input type="hidden" name="cn_section" value="products">';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('Sort by', 'ai-eshop-optimizer');
        echo '<select name="rl_sort">';
        foreach (array(
            'rate'   => __('Cancellation rate (high → low)', 'ai-eshop-optimizer'),
            'count'  => __('Cancellation count (high → low)', 'ai-eshop-optimizer'),
            'last'   => __('Most recent cancellation', 'ai-eshop-optimizer'),
            'orders' => __('Most ordered (popularity)', 'ai-eshop-optimizer'),
        ) as $k => $label) {
            echo '<option value="' . esc_attr($k) . '" ' . selected($sort, $k, false) . '>' . esc_html($label) . '</option>';
        }
        echo '</select></label>';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('Min orders', 'ai-eshop-optimizer') . '<input type="number" min="0" name="rl_min" value="' . esc_attr((string) $min_orders) . '" style="width:80px;"></label>';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('Top reason', 'ai-eshop-optimizer');
        echo '<select name="rl_reason"><option value="">' . esc_html__('— any —', 'ai-eshop-optimizer') . '</option>';
        foreach ($reasons_index as $slug => $r) {
            echo '<option value="' . esc_attr($slug) . '" ' . selected($reason, $slug, false) . '>' . esc_html((string) $r['label']) . '</option>';
        }
        echo '</select></label>';
        echo '<button class="button">' . esc_html__('Apply', 'ai-eshop-optimizer') . '</button>';
        echo '</form>';

        echo '<div class="aieo-dmm-card">';
        
        echo '<h3>' . sprintf(esc_html__('Products with cancellations (%s)', 'ai-eshop-optimizer'), esc_html(number_format_i18n($total))) . '</h3>';
        if (empty($rows)) {
            echo '<p>' . esc_html__('No products match the current filter.', 'ai-eshop-optimizer') . '</p>';
            echo '</div>';
            return;
        }

        echo '<table class="wp-list-table widefat striped" style="font-size:12px;"><thead><tr>';
        echo '<th>' . esc_html__('Product', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Orders', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Cancels', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Refund lines', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Rate', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Top reason', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Last', 'ai-eshop-optimizer') . '</th>';
        echo '<th></th></tr></thead><tbody>';
        foreach ($rows as $r) {
            $title = get_the_title((int) $r['product_id']);
            if (!$title) { $title = '#' . (int) $r['product_id']; }
            $rate_pct  = round((float) $r['cancellation_rate'] * 100, 2);
            $rate_color = $rate_pct >= 15 ? '#b91c1c' : ($rate_pct >= 10 ? '#ca8a04' : '#374151');
            $reason_label = isset($r['top_reason_slug'], $reasons_index[$r['top_reason_slug']])
                ? $reasons_index[$r['top_reason_slug']]['label']
                : (string) ($r['top_reason_slug'] ?? '');
            $detail_url = add_query_arg(array('cn_section' => 'products', 'rl_pid' => (int) $r['product_id']), $base_url);
            echo '<tr>';
            echo '<td><a href="' . esc_url($detail_url) . '"><strong>' . esc_html($title) . '</strong></a> <span style="color:#9ca3af;">#' . (int) $r['product_id'] . '</span></td>';
            echo '<td>' . esc_html(number_format_i18n((int) $r['total_orders'])) . '</td>';
            echo '<td>' . esc_html(number_format_i18n((int) $r['total_cancellations'])) . '</td>';
            echo '<td>' . esc_html(number_format_i18n((int) $r['total_refund_lines'])) . '</td>';
            echo '<td><strong style="color:' . esc_attr($rate_color) . ';">' . esc_html($rate_pct) . '%</strong></td>';
            echo '<td>' . ($reason_label !== '' ? esc_html($reason_label) : '—') . '</td>';
            echo '<td>' . esc_html((string) ($r['last_cancelled_at'] ?? '—')) . '</td>';
            echo '<td><a class="button button-small" href="' . esc_url($detail_url) . '">' . esc_html__('Drill', 'ai-eshop-optimizer') . '</a></td>';
            echo '</tr>';
        }
        echo '</tbody></table>';

        $pages = (int) ceil($total / $per_page);
        if ($pages > 1) {
            echo '<p style="margin-top:10px;">';
            for ($i = 1; $i <= min($pages, 12); $i++) {
                $args = array('cn_section' => 'products', 'rl_p' => $i);
                if ($sort !== 'rate')   { $args['rl_sort']   = $sort; }
                if ($min_orders !== 5)  { $args['rl_min']    = $min_orders; }
                if ($reason !== '')     { $args['rl_reason'] = $reason; }
                $url = esc_url(add_query_arg($args, $base_url));
                $is_cur = ($i === $pageno);
                echo $is_cur
                    ? '<strong style="margin-right:6px;">' . (int) $i . '</strong>'
                    : '<a href="' . $url . '" style="margin-right:6px;">' . (int) $i . '</a>'; 
            }
            if ($pages > 12) { echo '<span style="color:#9ca3af;">… of ' . (int) $pages . '</span>'; }
            echo '</p>';
        }
        echo '</div>';
    }

    
    private function render_product_detail($product_id, $base_url) {
        global $wpdb;
        $pbm = $wpdb->prefix . 'aieo_dmm_product_behavior_metrics';
        $oc  = $wpdb->prefix . 'aieo_dmm_order_cancellations';
        $lr  = $wpdb->prefix . 'aieo_dmm_order_line_refunds';
        $reasons_index = class_exists('AIEO_DMM_Cancellation_Reasons_Data') ? AIEO_DMM_Cancellation_Reasons_Data::all() : array();

        $title = get_the_title($product_id);
        if (!$title) { $title = '#' . $product_id; }

        echo '<p><a href="' . esc_url(add_query_arg('cn_section', 'products', $base_url)) . '">&larr; ' . esc_html__('Back to products list', 'ai-eshop-optimizer') . '</a></p>';

        $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$pbm} WHERE product_id = %d", $product_id), ARRAY_A);
        if (!$row) {
            echo '<p>' . esc_html__('No metrics for this product yet.', 'ai-eshop-optimizer') . '</p>';
            return;
        }

        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html($title) . ' <a href="' . esc_url(get_edit_post_link($product_id)) . '" style="font-size:12px;font-weight:normal;">[' . esc_html__('edit product', 'ai-eshop-optimizer') . ']</a></h3>';
        echo '<table class="form-table"><tbody>';
        echo '<tr><th>' . esc_html__('Total orders', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n((int) $row['total_orders'])) . '</td></tr>';
        echo '<tr><th>' . esc_html__('Cancellations (full order)', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n((int) $row['total_cancellations'])) . '</td></tr>';
        echo '<tr><th>' . esc_html__('Refund lines (partial)', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n((int) $row['total_refund_lines'])) . '</td></tr>';
        echo '<tr><th>' . esc_html__('Cancellation rate', 'ai-eshop-optimizer') . '</th><td>' . esc_html(round((float) $row['cancellation_rate'] * 100, 2)) . '%</td></tr>';
        echo '<tr><th>' . esc_html__('Top reason', 'ai-eshop-optimizer') . '</th><td>' . esc_html(isset($reasons_index[$row['top_reason_slug']]) ? $reasons_index[$row['top_reason_slug']]['label'] : (string) ($row['top_reason_slug'] ?? '—')) . '</td></tr>';
        echo '<tr><th>' . esc_html__('Last cancelled', 'ai-eshop-optimizer') . '</th><td>' . esc_html((string) ($row['last_cancelled_at'] ?? '—')) . '</td></tr>';
        echo '</tbody></table>';
        echo '</div>';

        
        $events = $wpdb->get_results($wpdb->prepare(
            "(SELECT 'cancel' AS kind, cancelled_at AS event_at, order_id, user_id,
                     reason_slug, reason_note, order_total AS amount, NULL AS qty
                FROM {$oc}
               WHERE JSON_CONTAINS(product_ids, JSON_ARRAY(%d)))
              UNION ALL
             (SELECT 'refund' AS kind, refunded_at AS event_at, order_id, user_id,
                     reason_slug, reason_note, amount_refunded AS amount, qty_refunded AS qty
                FROM {$lr}
               WHERE product_id = %d)
             ORDER BY event_at DESC
             LIMIT 500",
            $product_id, $product_id
        ), ARRAY_A);

        echo '<div class="aieo-dmm-card" style="margin-top:14px;">';
        echo '<h3>' . esc_html__('Events (most recent first)', 'ai-eshop-optimizer') . '</h3>';
        if (empty($events)) {
            echo '<p>' . esc_html__('No cancellation or refund events for this product.', 'ai-eshop-optimizer') . '</p>';
        } else {
            echo '<table class="wp-list-table widefat striped" style="font-size:12px;"><thead><tr>';
            echo '<th>' . esc_html__('When', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Kind', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Order', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Customer', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Reason', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Qty', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Amount (€)', 'ai-eshop-optimizer') . '</th>';
            echo '</tr></thead><tbody>';
            foreach ($events as $e) {
                $u = (int) $e['user_id'] > 0 ? get_user_by('id', (int) $e['user_id']) : null;
                $cust = $u ? esc_html($u->display_name) : '<span style="color:#9ca3af;">guest</span>';
                $reason_label = isset($reasons_index[$e['reason_slug']]) ? $reasons_index[$e['reason_slug']]['label'] : (string) $e['reason_slug'];
                $kind_color = $e['kind'] === 'cancel' ? '#b91c1c' : '#ca8a04';
                $kind_label = $e['kind'] === 'cancel' ? __('Full cancel', 'ai-eshop-optimizer') : __('Line refund', 'ai-eshop-optimizer');
                echo '<tr>';
                echo '<td>' . esc_html((string) $e['event_at']) . '</td>';
                echo '<td><span style="color:' . esc_attr($kind_color) . ';font-weight:600;">' . esc_html($kind_label) . '</span></td>';
                echo '<td><a href="' . esc_url(admin_url('post.php?action=edit&post=' . (int) $e['order_id'])) . '">#' . (int) $e['order_id'] . '</a></td>';
                echo '<td>' . $cust . '</td>'; 
                echo '<td>' . esc_html($reason_label) . ($e['reason_note'] ? '<br><span style="color:#6b7280;font-size:11px;">' . esc_html(wp_trim_words((string) $e['reason_note'], 8)) . '</span>' : '') . '</td>';
                echo '<td>' . ($e['qty'] !== null ? esc_html((string) (int) $e['qty']) : '—') . '</td>';
                echo '<td>' . esc_html(number_format_i18n((float) $e['amount'], 2)) . '</td>';
                echo '</tr>';
            }
            echo '</tbody></table>';
        }
        echo '</div>';
    }

    
    
    

    private function render_customers($base_url) {
        global $wpdb;
        $ubm = $wpdb->prefix . 'aieo_dmm_user_behavior_metrics';

        $sort = isset($_GET['rl_sort']) ? sanitize_key((string) $_GET['rl_sort']) : 'risk';
        $sort_map = array(
            'risk'   => 'risk_band DESC, cancellation_rate DESC',
            'count'  => 'total_cancellations DESC, cancellation_rate DESC',
            'rate'   => 'cancellation_rate DESC, total_cancellations DESC',
            'orders' => 'total_orders DESC',
            'last'   => 'last_cancelled_at DESC',
        );
        $order_by = $sort_map[$sort] ?? $sort_map['risk'];

        $band       = isset($_GET['rl_band']) ? (int) $_GET['rl_band'] : -1;
        $min_orders = isset($_GET['rl_min'])  ? max(0, (int) $_GET['rl_min']) : 0;
        $where = array('total_cancellations > 0', 'total_orders >= %d');
        $params = array($min_orders);
        if ($band >= 0 && $band <= 2) { $where[] = 'risk_band = %d'; $params[] = $band; }
        $where_sql = implode(' AND ', $where);

        $total = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$ubm} WHERE {$where_sql}", ...$params));

        $per_page = 50;
        $pageno   = isset($_GET['rl_p']) ? max(1, (int) $_GET['rl_p']) : 1;
        $offset   = ($pageno - 1) * $per_page;

        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT user_id, total_orders, total_cancellations, cancellation_rate,
                    last_cancelled_at, top_reason_slug, risk_band
               FROM {$ubm}
              WHERE {$where_sql}
              ORDER BY {$order_by}
              LIMIT %d OFFSET %d",
            ...array_merge($params, array($per_page, $offset))
        ), ARRAY_A);

        
        echo '<form method="get" style="margin:6px 0 14px;display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap;">';
        echo '<input type="hidden" name="page" value="' . esc_attr(AIEO_DMM::MENU_SLUG) . '">';
        echo '<input type="hidden" name="dmm_tab" value="cancellations">';
        echo '<input type="hidden" name="cn_section" value="customers">';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('Sort by', 'ai-eshop-optimizer');
        echo '<select name="rl_sort">';
        foreach (array(
            'risk'   => __('Risk band (high → low)', 'ai-eshop-optimizer'),
            'count'  => __('Returns count (high → low)', 'ai-eshop-optimizer'),
            'rate'   => __('Return rate (high → low)', 'ai-eshop-optimizer'),
            'orders' => __('Total purchases (high → low)', 'ai-eshop-optimizer'),
            'last'   => __('Most recent return', 'ai-eshop-optimizer'),
        ) as $k => $label) {
            echo '<option value="' . esc_attr($k) . '" ' . selected($sort, $k, false) . '>' . esc_html($label) . '</option>';
        }
        echo '</select></label>';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('Risk band', 'ai-eshop-optimizer');
        echo '<select name="rl_band">';
        echo '<option value="-1" ' . selected($band, -1, false) . '>' . esc_html__('— any —', 'ai-eshop-optimizer') . '</option>';
        echo '<option value="0"  ' . selected($band,  0, false) . '>' . esc_html__('0 normal', 'ai-eshop-optimizer') . '</option>';
        echo '<option value="1"  ' . selected($band,  1, false) . '>' . esc_html__('1 elevated', 'ai-eshop-optimizer') . '</option>';
        echo '<option value="2"  ' . selected($band,  2, false) . '>' . esc_html__('2 high', 'ai-eshop-optimizer') . '</option>';
        echo '</select></label>';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('Min purchases', 'ai-eshop-optimizer') . '<input type="number" min="0" name="rl_min" value="' . esc_attr((string) $min_orders) . '" style="width:80px;"></label>';
        echo '<button class="button">' . esc_html__('Apply', 'ai-eshop-optimizer') . '</button>';
        echo '</form>';

        echo '<div class="aieo-dmm-card">';
        
        echo '<h3>' . sprintf(esc_html__('Customers with returns (%s)', 'ai-eshop-optimizer'), esc_html(number_format_i18n($total))) . '</h3>';
        if (empty($rows)) {
            echo '<p>' . esc_html__('No customers match the current filter.', 'ai-eshop-optimizer') . '</p>';
            echo '</div>';
            return;
        }

        echo '<table class="wp-list-table widefat striped" style="font-size:12px;"><thead><tr>';
        echo '<th>' . esc_html__('Customer', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Email', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Total purchases', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Returns', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Return rate', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Top reason', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Risk', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Last return', 'ai-eshop-optimizer') . '</th>';
        echo '</tr></thead><tbody>';

        $reasons_index = class_exists('AIEO_DMM_Cancellation_Reasons_Data') ? AIEO_DMM_Cancellation_Reasons_Data::all() : array();
        foreach ($rows as $r) {
            $u = get_user_by('id', (int) $r['user_id']);
            $name  = $u ? $u->display_name : ('#' . (int) $r['user_id']);
            $email = $u ? $u->user_email   : '';
            $rate_pct = round((float) $r['cancellation_rate'] * 100, 2);
            $rate_color = $rate_pct >= 15 ? '#b91c1c' : ($rate_pct >= 5 ? '#ca8a04' : '#374151');
            $band  = (int) $r['risk_band'];
            $band_label = $band === 2 ? 'high' : ($band === 1 ? 'elevated' : 'normal');
            $band_color = $band === 2 ? '#b91c1c' : ($band === 1 ? '#ca8a04' : '#6b7280');
            $reason_label = isset($r['top_reason_slug'], $reasons_index[$r['top_reason_slug']])
                ? $reasons_index[$r['top_reason_slug']]['label']
                : (string) ($r['top_reason_slug'] ?? '');
            echo '<tr>';
            echo '<td><a href="' . esc_url(get_edit_user_link((int) $r['user_id'])) . '">' . esc_html($name) . '</a></td>';
            echo '<td><span style="color:#6b7280;font-size:11px;">' . esc_html($email) . '</span></td>';
            echo '<td>' . esc_html(number_format_i18n((int) $r['total_orders'])) . '</td>';
            echo '<td>' . esc_html(number_format_i18n((int) $r['total_cancellations'])) . '</td>';
            echo '<td><strong style="color:' . esc_attr($rate_color) . ';">' . esc_html($rate_pct) . '%</strong></td>';
            echo '<td>' . ($reason_label !== '' ? esc_html($reason_label) : '—') . '</td>';
            echo '<td><span style="display:inline-block;font-weight:600;padding:2px 7px;border-radius:999px;background:' . esc_attr($band_color) . '22;color:' . esc_attr($band_color) . ';">' . esc_html($band_label) . '</span></td>';
            echo '<td>' . esc_html((string) ($r['last_cancelled_at'] ?? '—')) . '</td>';
            echo '</tr>';
        }
        echo '</tbody></table>';

        $pages = (int) ceil($total / $per_page);
        if ($pages > 1) {
            echo '<p style="margin-top:10px;">';
            for ($i = 1; $i <= min($pages, 12); $i++) {
                $args = array('cn_section' => 'customers', 'rl_p' => $i);
                if ($sort !== 'risk')  { $args['rl_sort'] = $sort; }
                if ($band  !== -1)     { $args['rl_band'] = $band; }
                if ($min_orders !== 0) { $args['rl_min']  = $min_orders; }
                $url = esc_url(add_query_arg($args, $base_url));
                $is_cur = ($i === $pageno);
                echo $is_cur
                    ? '<strong style="margin-right:6px;">' . (int) $i . '</strong>'
                    : '<a href="' . $url . '" style="margin-right:6px;">' . (int) $i . '</a>'; 
            }
            if ($pages > 12) { echo '<span style="color:#9ca3af;">… of ' . (int) $pages . '</span>'; }
            echo '</p>';
        }
        echo '</div>';
    }

    
    
    

    private function render_operations_card($pbm, $ubm) {
        global $wpdb;
        $oc         = $wpdb->prefix . 'aieo_dmm_order_cancellations';
        $pbm_n      = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$pbm}");
        $ubm_n      = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$ubm}");
        $pbm_stale  = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$pbm} WHERE computed_at < DATE_SUB(NOW(), INTERVAL 24 HOUR)");
        $ubm_stale  = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$ubm} WHERE computed_at < DATE_SUB(NOW(), INTERVAL 24 HOUR)");
        
        
        $oc_pending = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$oc}
              WHERE event_kind > 0
                AND (outbound_shipping_cost IS NULL OR outbound_shipping_cost = 0)"
        );
        $oc_postship = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$oc} WHERE event_kind > 0");
        $oc_total    = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$oc}");
        $oc_kinds    = $wpdb->get_results("SELECT event_kind, COUNT(*) AS n FROM {$oc} GROUP BY event_kind", ARRAY_A);
        $oc_kind_map = array(0 => 0, 1 => 0, 2 => 0);
        foreach ((array) $oc_kinds as $r) { $oc_kind_map[(int) $r['event_kind']] = (int) $r['n']; }
        $next_cron  = wp_next_scheduled('aieo_dmm_metrics_daily_recompute');
        $nonce      = wp_create_nonce(AIEO_DMM::NONCE_ACTION);
        $ajax       = admin_url('admin-ajax.php');

        echo '<div class="aieo-dmm-card" style="margin-bottom:14px;">';
        echo '<h3>' . esc_html__('Behavior metrics — operations', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin-top:0;">'
           . esc_html__('Per-product + per-user aggregates that feed the recommendation engine. Filled write-side as cancellations land; the daily cron catches any rows that drifted (e.g. operator edited a reason after capture).', 'ai-eshop-optimizer')
           . '</p>';

        echo '<table class="form-table"><tbody>';
        echo '<tr><th>' . esc_html__('Product metric rows', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n($pbm_n)) . ($pbm_stale > 0 ? ' <span style="color:#ca8a04;">(' . esc_html(number_format_i18n($pbm_stale)) . ' stale)</span>' : '') . '</td></tr>';
        echo '<tr><th>' . esc_html__('User metric rows', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n($ubm_n)) . ($ubm_stale > 0 ? ' <span style="color:#ca8a04;">(' . esc_html(number_format_i18n($ubm_stale)) . ' stale)</span>' : '') . '</td></tr>';
        echo '<tr><th>' . esc_html__('Cancellations log', 'ai-eshop-optimizer') . '</th>';
        echo '<td>' . esc_html(number_format_i18n($oc_total)) . ' '
           . '<span style="color:#6b7280;font-size:11px;">('
           . sprintf(
                
                esc_html__('%1$d cancellations · %2$d returns · %3$d refund-only', 'ai-eshop-optimizer'),
                (int) $oc_kind_map[0], (int) $oc_kind_map[1], (int) $oc_kind_map[2]
             )
           . ')</span></td></tr>';
        echo '<tr><th>' . esc_html__('Outbound shipping cost (post-shipment events)', 'ai-eshop-optimizer') . '</th>';
        echo '<td>' . sprintf(
                
                esc_html__('%1$s of %2$s post-shipment rows have a resolved carrier cost', 'ai-eshop-optimizer'),
                '<strong>' . esc_html(number_format_i18n($oc_postship - $oc_pending)) . '</strong>',
                esc_html(number_format_i18n($oc_postship))
            )
            . ($oc_pending > 0 ? ' <span style="color:#ca8a04;">(' . esc_html(number_format_i18n($oc_pending)) . ' pending)</span>' : '')
            . '</td></tr>';
        echo '<tr><th>' . esc_html__('Next daily sweep', 'ai-eshop-optimizer') . '</th><td>' . ($next_cron ? esc_html(date_i18n('Y-m-d H:i', $next_cron)) : '<span style="color:#dc2626;">' . esc_html__('not scheduled', 'ai-eshop-optimizer') . '</span>') . '</td></tr>';
        echo '</tbody></table>';

        echo '<p style="margin-top:10px;">';
        echo '<button type="button" class="button button-primary" id="aieo-bm-backfill">' . esc_html__('Run metrics backfill', 'ai-eshop-optimizer') . '</button> ';
        echo '<button type="button" class="button" id="aieo-bm-sweep">' . esc_html__('Run daily sweep now', 'ai-eshop-optimizer') . '</button> ';
        echo '<span class="description" id="aieo-bm-msg" style="margin-left:10px;"></span>';
        echo '</p>';

        
        
        echo '<div id="aieo-bm-progress" style="display:none;margin-top:10px;">';
        echo '  <div style="background:#f3f4f6;border-radius:4px;overflow:hidden;height:18px;width:100%;max-width:600px;">';
        echo '    <div id="aieo-bm-bar" style="background:#2563eb;height:100%;width:0%;transition:width .3s;"></div>';
        echo '  </div>';
        echo '  <p id="aieo-bm-progress-msg" class="description" style="margin-top:6px;"></p>';
        echo '</div>';

        
        
        
        
        echo '<p style="margin-top:14px;border-top:1px solid #e5e7eb;padding-top:12px;">';
        echo '<button type="button" class="button button-primary" id="aieo-oc-backfill"' . disabled($oc_pending === 0, true, false) . '>'
           . esc_html__('Backfill outbound shipping cost', 'ai-eshop-optimizer')
           . ($oc_pending > 0 ? ' <span style="opacity:.7;">(' . esc_html(number_format_i18n($oc_pending)) . ')</span>' : '')
           . '</button> ';
        echo '<span class="description" style="margin-left:10px;">' . esc_html__('Runs the carrier-cost resolver (same one used for free-shipping ROI) against every cancellation row that doesn&#8217;t have a resolved cost yet. Chunked — safe to leave running.', 'ai-eshop-optimizer') . '</span>';
        echo '</p>';
        echo '<div id="aieo-oc-progress" style="display:none;margin-top:10px;">';
        echo '  <div style="background:#f3f4f6;border-radius:4px;overflow:hidden;height:18px;width:100%;max-width:600px;">';
        echo '    <div id="aieo-oc-bar" style="background:#16a34a;height:100%;width:0%;transition:width .3s;"></div>';
        echo '  </div>';
        echo '  <p id="aieo-oc-msg" class="description" style="margin-top:6px;"></p>';
        echo '</div>';
        ?>
        <script>
        (function(){
            var NONCE = <?php echo wp_json_encode($nonce); ?>;
            var AJAX  = <?php echo wp_json_encode($ajax); ?>;
            function run(action, btn, doneText) {
                var msg = document.getElementById('aieo-bm-msg');
                btn.disabled = true;
                msg.textContent = '<?php echo esc_js(__('Working…', 'ai-eshop-optimizer')); ?>';
                var fd = new FormData();
                fd.append('action', action);
                fd.append('nonce', NONCE);
                fetch(AJAX, {method:'POST',credentials:'same-origin',body:fd}).then(function(r){return r.json();}).then(function(j){
                    btn.disabled = false;
                    if (j && j.success) {
                        msg.textContent = doneText + ' (' + (j.data && j.data.products || 0) + ' products, ' + (j.data && j.data.users || 0) + ' users in ' + (j.data && j.data.elapsed_ms || 0) + 'ms)';
                    } else {
                        msg.textContent = (j && j.data && j.data.message) || '<?php echo esc_js(__('Failed.', 'ai-eshop-optimizer')); ?>';
                    }
                });
            }
            // Chunked metrics-backfill driver. Replaces the old single-shot
            // synchronous run because on stores with 50k+ historical cancelled
            // / refunded / failed orders the unbounded loop hits PHP's
            // max_execution_time. Two phases:
            //
            //   1) ingest    — loop POST 'aieo_dmm_metrics_backfill_step'
            //                  with phase=ingest&cursor=<last_id> until
            //                  server returns has_more=false. ~3000 orders
            //                  per tick. Progress bar tracks processed/total.
            //   2) aggregate — single POST with phase=aggregate to run the
            //                  downstream Product/User behavior-metrics
            //                  aggregation (cheap — only touches our own
            //                  tables).
            (function(){
                var bmBtn  = document.getElementById('aieo-bm-backfill');
                var bmMsg  = document.getElementById('aieo-bm-msg');
                var prog   = document.getElementById('aieo-bm-progress');
                var bar    = document.getElementById('aieo-bm-bar');
                var pmsg   = document.getElementById('aieo-bm-progress-msg');
                if (!bmBtn) { return; }
                bmBtn.addEventListener('click', function(){
                    if (!confirm('<?php echo esc_js(__('Walk every historical cancelled / refunded / failed order into the AIEO cancellations log, then rebuild product + user behavior metrics? Runs in ~3000-order chunks — safe to leave running.', 'ai-eshop-optimizer')); ?>')) return;
                    bmBtn.disabled = true;
                    bmMsg.textContent = '<?php echo esc_js(__('Counting orders…', 'ai-eshop-optimizer')); ?>';
                    prog.style.display = 'block';
                    bar.style.width = '0%';
                    pmsg.textContent = '';
                    var totalProcessed = 0;
                    var totalAll = 0;
                    var t0 = Date.now();
                    function ingest(cursor) {
                        var fd = new FormData();
                        fd.append('action', 'aieo_dmm_metrics_backfill_step');
                        fd.append('nonce', NONCE);
                        fd.append('phase', 'ingest');
                        fd.append('cursor', cursor|0);
                        fd.append('limit', 3000);
                        fetch(AJAX, {method:'POST',credentials:'same-origin',body:fd}).then(function(r){return r.json();}).then(function(j){
                            if (!j || !j.success) {
                                pmsg.textContent = (j && j.data && j.data.message) || '<?php echo esc_js(__('Failed.', 'ai-eshop-optimizer')); ?>';
                                bmBtn.disabled = false;
                                return;
                            }
                            var d = j.data || {};
                            totalProcessed += (d.processed|0);
                            if (totalAll === 0) { totalAll = (d.total|0); }
                            var pct = totalAll > 0 ? Math.min(99, Math.round(totalProcessed * 100 / totalAll)) : 0;
                            bar.style.width = pct + '%';
                            pmsg.textContent = '<?php echo esc_js(__('Ingest:', 'ai-eshop-optimizer')); ?> ' + totalProcessed + ' / ' + totalAll + ' (' + pct + '%) · ' + (d.elapsed_ms|0) + 'ms this tick';
                            if (d.has_more) {
                                setTimeout(function(){ ingest(d.next_cursor|0); }, 50);
                            } else {
                                aggregate();
                            }
                        }).catch(function(e){
                            pmsg.textContent = 'Network error: ' + e;
                            bmBtn.disabled = false;
                        });
                    }
                    function aggregate() {
                        bar.style.width = '100%';
                        pmsg.textContent = '<?php echo esc_js(__('Ingest complete — aggregating per-product + per-user metrics…', 'ai-eshop-optimizer')); ?>';
                        var fd = new FormData();
                        fd.append('action', 'aieo_dmm_metrics_backfill_step');
                        fd.append('nonce', NONCE);
                        fd.append('phase', 'aggregate');
                        fetch(AJAX, {method:'POST',credentials:'same-origin',body:fd}).then(function(r){return r.json();}).then(function(j){
                            bmBtn.disabled = false;
                            if (!j || !j.success) {
                                pmsg.textContent = (j && j.data && j.data.message) || '<?php echo esc_js(__('Aggregation failed.', 'ai-eshop-optimizer')); ?>';
                                return;
                            }
                            var d = j.data || {};
                            var elapsed = Math.round((Date.now() - t0) / 1000);
                            bmMsg.textContent = '<?php echo esc_js(__('Backfill complete.', 'ai-eshop-optimizer')); ?> ' + totalProcessed + ' orders ingested · ' + (d.products|0) + ' product rows · ' + (d.users|0) + ' user rows · ' + elapsed + 's total';
                            pmsg.textContent = '';
                            setTimeout(function(){ location.reload(); }, 1800);
                        }).catch(function(e){
                            pmsg.textContent = 'Network error: ' + e;
                            bmBtn.disabled = false;
                        });
                    }
                    ingest(0);
                });
            })();
            document.getElementById('aieo-bm-sweep').addEventListener('click', function(){
                run('aieo_dmm_metrics_recompute_stale', this, '<?php echo esc_js(__('Sweep complete.', 'ai-eshop-optimizer')); ?>');
            });

            // Outbound-shipping-cost chunked backfill — loop until done.
            var ocBtn = document.getElementById('aieo-oc-backfill');
            if (ocBtn) {
                ocBtn.addEventListener('click', function(){
                    if (!confirm('<?php echo esc_js(__('Run the carrier-cost resolver on every cancellation row without a resolved cost? This calls WC\'s shipping calculator per order.', 'ai-eshop-optimizer')); ?>')) return;
                    var prog = document.getElementById('aieo-oc-progress');
                    var bar  = document.getElementById('aieo-oc-bar');
                    var msg  = document.getElementById('aieo-oc-msg');
                    prog.style.display = 'block';
                    ocBtn.disabled = true;
                    var startTotal = 0;
                    function tick() {
                        var fd = new FormData();
                        fd.append('action', 'aieo_dmm_outbound_cost_step');
                        fd.append('nonce', NONCE);
                        fd.append('batch', 100);
                        msg.textContent = '<?php echo esc_js(__('Working…', 'ai-eshop-optimizer')); ?>';
                        fetch(AJAX, {method:'POST',credentials:'same-origin',body:fd}).then(function(r){return r.json();}).then(function(j){
                            if (!j || !j.success) {
                                msg.textContent = (j && j.data && j.data.message) || '<?php echo esc_js(__('Failed.', 'ai-eshop-optimizer')); ?>';
                                ocBtn.disabled = false;
                                return;
                            }
                            var d = j.data || {};
                            var processed = (d.processed|0);
                            var remaining = (d.remaining|0);
                            if (startTotal === 0) { startTotal = processed + remaining; }
                            var done = startTotal - remaining;
                            var pct = startTotal > 0 ? Math.round(done * 100 / startTotal) : 100;
                            bar.style.width = pct + '%';
                            msg.textContent = (d.processed|0) + ' processed this tick · ' + remaining + ' remaining · ' + (d.elapsed_ms|0) + 'ms';
                            if (remaining > 0 && processed > 0) {
                                setTimeout(tick, 50);
                            } else {
                                msg.textContent = '<?php echo esc_js(__('Backfill complete.', 'ai-eshop-optimizer')); ?> ' + msg.textContent;
                                ocBtn.disabled = false;
                                setTimeout(function(){ location.reload(); }, 1200);
                            }
                        });
                    }
                    tick();
                });
            }
        })();
        </script>
        <?php
        echo '</div>';
    }

    
    
    

    private function render_returns_ledger($base_url) {
        global $wpdb;
        $oc = $wpdb->prefix . 'aieo_dmm_order_cancellations';
        $reasons_index = class_exists('AIEO_DMM_Cancellation_Reasons_Data') ? AIEO_DMM_Cancellation_Reasons_Data::all() : array();

        
        $f_reason = isset($_GET['rl_reason']) ? sanitize_key((string) $_GET['rl_reason']) : '';
        $f_from   = isset($_GET['rl_from'])   ? sanitize_text_field((string) wp_unslash( $_GET['rl_from'] )) : '';
        $f_to     = isset($_GET['rl_to'])     ? sanitize_text_field((string) wp_unslash( $_GET['rl_to'] ))   : '';
        $f_scope  = isset($_GET['rl_scope'])  ? sanitize_key((string) $_GET['rl_scope']) : '';
        $f_kind   = isset($_GET['rl_kind'])   ? (string) sanitize_text_field( wp_unslash( $_GET['rl_kind'] ) ) : '';

        $where  = array('1=1');
        $params = array();
        if ($f_reason !== '') { $where[] = 'reason_slug = %s'; $params[] = $f_reason; }
        if ($f_from   !== '') { $where[] = 'cancelled_at >= %s'; $params[] = $f_from . ' 00:00:00'; }
        if ($f_to     !== '') { $where[] = 'cancelled_at <= %s'; $params[] = $f_to   . ' 23:59:59'; }
        if ($f_scope === 'full')    { $where[] = 'is_full_cancel = 1'; }
        if ($f_scope === 'partial') { $where[] = 'is_full_cancel = 0'; }
        if ($f_kind !== '' && in_array((int) $f_kind, array(0, 1, 2, 3), true)) {
            $where[]  = 'event_kind = %d';
            $params[] = (int) $f_kind;
        }
        $where_sql = implode(' AND ', $where);

        
        $sum_sql = "SELECT COUNT(*) AS n,
                           SUM(CASE WHEN event_kind = 0 THEN 1 ELSE 0 END) AS n_cancel,
                           SUM(CASE WHEN event_kind = 1 THEN 1 ELSE 0 END) AS n_returned,
                           SUM(CASE WHEN event_kind = 2 THEN 1 ELSE 0 END) AS n_kept,
                           SUM(CASE WHEN event_kind = 3 THEN 1 ELSE 0 END) AS n_refused,
                           SUM(order_total) AS revenue_lost,
                           SUM(IFNULL(outbound_shipping_cost,0)) AS outbound_paid,
                           SUM(IFNULL(return_shipping_cost,0))   AS return_paid,
                           SUM(IFNULL(outbound_shipping_cost,0) + IFNULL(return_shipping_cost,0)) AS shipping_total
                      FROM {$oc} WHERE {$where_sql}";
        $sum = $params ? $wpdb->get_row($wpdb->prepare($sum_sql, ...$params), ARRAY_A) : $wpdb->get_row($sum_sql, ARRAY_A);

        
        echo '<form method="get" style="margin:6px 0 14px;display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap;">';
        echo '<input type="hidden" name="page" value="' . esc_attr(AIEO_DMM::MENU_SLUG) . '">';
        echo '<input type="hidden" name="dmm_tab" value="cancellations">';
        echo '<input type="hidden" name="cn_section" value="returns">';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('Reason', 'ai-eshop-optimizer');
        echo '<select name="rl_reason"><option value="">' . esc_html__('— all —', 'ai-eshop-optimizer') . '</option>';
        foreach ($reasons_index as $slug => $r) {
            echo '<option value="' . esc_attr($slug) . '" ' . selected($f_reason, $slug, false) . '>' . esc_html((string) $r['label']) . '</option>';
        }
        echo '</select></label>';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('From', 'ai-eshop-optimizer') . '<input type="date" name="rl_from" value="' . esc_attr($f_from) . '"></label>';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('To',   'ai-eshop-optimizer') . '<input type="date" name="rl_to"   value="' . esc_attr($f_to)   . '"></label>';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('Scope', 'ai-eshop-optimizer');
        echo '<select name="rl_scope">';
        echo '<option value=""        ' . selected($f_scope, '', false)        . '>' . esc_html__('All',     'ai-eshop-optimizer') . '</option>';
        echo '<option value="full"    ' . selected($f_scope, 'full', false)    . '>' . esc_html__('Full',    'ai-eshop-optimizer') . '</option>';
        echo '<option value="partial" ' . selected($f_scope, 'partial', false) . '>' . esc_html__('Partial', 'ai-eshop-optimizer') . '</option>';
        echo '</select></label>';
        echo '<label style="display:flex;flex-direction:column;font-size:11px;color:#4b5563;">' . esc_html__('Type', 'ai-eshop-optimizer');
        echo '<select name="rl_kind">';
        echo '<option value=""  ' . selected($f_kind, '',  false) . '>' . esc_html__('Any',                       'ai-eshop-optimizer') . '</option>';
        echo '<option value="0" ' . selected($f_kind, '0', false) . '>' . esc_html__('Cancellation',              'ai-eshop-optimizer') . '</option>';
        echo '<option value="1" ' . selected($f_kind, '1', false) . '>' . esc_html__('Returned (after receipt)',  'ai-eshop-optimizer') . '</option>';
        echo '<option value="2" ' . selected($f_kind, '2', false) . '>' . esc_html__('Kept (refund only)',        'ai-eshop-optimizer') . '</option>';
        echo '<option value="3" ' . selected($f_kind, '3', false) . '>' . esc_html__('Refused (never received)',  'ai-eshop-optimizer') . '</option>';
        echo '</select></label>';
        echo '<button class="button">' . esc_html__('Filter', 'ai-eshop-optimizer') . '</button>';
        if ($f_reason || $f_from || $f_to || $f_scope || $f_kind !== '') {
            echo ' <a class="button-link" href="' . esc_url($base_url . '&cn_section=returns') . '">' . esc_html__('clear', 'ai-eshop-optimizer') . '</a>';
        }
        echo '</form>';

        
        echo '<div class="aieo-dmm-card" style="margin-bottom:14px;">';
        echo '<table class="form-table" style="margin-top:0;"><tbody>';
        echo '<tr><th>' . esc_html__('Events matching filter', 'ai-eshop-optimizer') . '</th>';
        echo '<td>' . esc_html(number_format_i18n((int) $sum['n']))
           . ' <span style="color:#6b7280;font-size:11px;">('
           
           . esc_html(sprintf(__('%1$d cancellations · %2$d returned · %3$d kept · %4$d refused', 'ai-eshop-optimizer'),
               (int) $sum['n_cancel'], (int) $sum['n_returned'], (int) $sum['n_kept'], (int) $sum['n_refused']))
           . ')</span></td></tr>';
        echo '<tr><th>' . esc_html__('Revenue lost (€)', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n((float) $sum['revenue_lost'], 2)) . '</td></tr>';
        echo '<tr><th>' . esc_html__('Outbound shipping paid (€)', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n((float) $sum['outbound_paid'], 2)) . '</td></tr>';
        echo '<tr><th>' . esc_html__('Return shipping paid (€)', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n((float) $sum['return_paid'], 2)) . '</td></tr>';
        echo '<tr><th>' . esc_html__('Total shipping cost (€)', 'ai-eshop-optimizer') . '</th><td><strong>' . esc_html(number_format_i18n((float) $sum['shipping_total'], 2)) . '</strong></td></tr>';
        echo '</tbody></table>';
        echo '<p class="description" style="margin-top:0;">' . esc_html__('Cancellations: no shipping cost (voided pre-shipment). Returned: outbound + return cost (customer received + sent back). Kept: outbound only (customer received + kept). Refused: outbound + return (carrier returned the parcel — customer never opened it).', 'ai-eshop-optimizer') . '</p>';
        echo '</div>';

        
        $per_page = 50;
        $pageno   = isset($_GET['rl_p']) ? max(1, (int) $_GET['rl_p']) : 1;
        $offset   = ($pageno - 1) * $per_page;

        $list_sql = "SELECT id, order_id, user_id, reason_slug, reason_note, status_after,
                            order_total, product_ids, is_full_cancel, event_kind,
                            entry_channel, entry_branch_id,
                            outbound_shipping_cost, return_shipping_cost, cancelled_at
                       FROM {$oc} WHERE {$where_sql}
                      ORDER BY cancelled_at DESC
                      LIMIT %d OFFSET %d";
        $list_args = array_merge($params, array($per_page, $offset));
        $rows = $wpdb->get_results($wpdb->prepare($list_sql, ...$list_args), ARRAY_A);

        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Returns', 'ai-eshop-optimizer') . '</h3>';

        if (empty($rows)) {
            echo '<p>' . esc_html__('No returns match the current filter.', 'ai-eshop-optimizer') . '</p>';
            echo '</div>';
            return;
        }

        echo '<table class="wp-list-table widefat striped" style="font-size:12px;"><thead><tr>';
        echo '<th>' . esc_html__('When', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Order', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Customer', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Type', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Reason', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Scope', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Affected items', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Order total (€)', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Outbound (€)', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Return (€)', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Total cost (€)', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Channel', 'ai-eshop-optimizer') . '</th>';
        echo '</tr></thead><tbody>';

        $kind_meta = array(
            0 => array('label' => __('Cancellation', 'ai-eshop-optimizer'), 'color' => '#6b7280'),
            1 => array('label' => __('Returned',     'ai-eshop-optimizer'), 'color' => '#b91c1c'),
            2 => array('label' => __('Kept',         'ai-eshop-optimizer'), 'color' => '#ca8a04'),
            3 => array('label' => __('Refused',      'ai-eshop-optimizer'), 'color' => '#7c3aed'),
        );

        foreach ($rows as $r) {
            $kind         = (int) ($r['event_kind'] ?? 0);
            $km           = $kind_meta[$kind] ?? $kind_meta[0];
            $reason_label = isset($reasons_index[$r['reason_slug']]) ? $reasons_index[$r['reason_slug']]['label'] : $r['reason_slug'];
            $is_full      = (int) $r['is_full_cancel'] === 1;
            $product_ids  = json_decode((string) $r['product_ids'], true);
            $product_ids  = is_array($product_ids) ? $product_ids : array();
            $items_html   = '';
            if (!empty($product_ids)) {
                $names = array();
                foreach (array_slice($product_ids, 0, 4) as $pid) {
                    $title = get_the_title((int) $pid);
                    $names[] = $title ? esc_html($title) : '#' . (int) $pid;
                }
                $items_html = implode('<br>', $names);
                if (count($product_ids) > 4) {
                    $items_html .= '<br><span style="color:#6b7280;">+' . (count($product_ids) - 4) . ' more</span>';
                }
            }
            $outbound = $r['outbound_shipping_cost'] !== null ? (float) $r['outbound_shipping_cost'] : null;
            $returnc  = $r['return_shipping_cost']   !== null ? (float) $r['return_shipping_cost']   : null;
            $total_cost = ($outbound ?? 0) + ($returnc ?? 0);

            $u = (int) $r['user_id'] > 0 ? get_user_by('id', (int) $r['user_id']) : null;
            $cust = $u ? esc_html($u->display_name) : '<span style="color:#9ca3af;">guest</span>';

            echo '<tr>';
            echo '<td>' . esc_html((string) $r['cancelled_at']) . '</td>';
            echo '<td><a href="' . esc_url(admin_url('post.php?action=edit&post=' . (int) $r['order_id'])) . '">#' . (int) $r['order_id'] . '</a><br><span style="color:#9ca3af;font-size:10px;">' . esc_html((string) $r['status_after']) . '</span></td>';
            echo '<td>' . $cust . '</td>'; 
            echo '<td><span style="display:inline-block;font-weight:600;padding:2px 7px;border-radius:999px;background:' . esc_attr($km['color']) . '22;color:' . esc_attr($km['color']) . ';">' . esc_html($km['label']) . '</span></td>';
            echo '<td><strong>' . esc_html((string) $reason_label) . '</strong>' . ($r['reason_note'] ? '<br><span style="color:#6b7280;font-size:11px;">' . esc_html(wp_trim_words((string) $r['reason_note'], 8)) . '</span>' : '') . '</td>';
            echo '<td>' . ($is_full ? esc_html__('Full', 'ai-eshop-optimizer') : '<span style="color:#ca8a04;">' . esc_html__('Partial', 'ai-eshop-optimizer') . '</span>') . '</td>';
            echo '<td>' . $items_html . '</td>'; 
            echo '<td>' . esc_html(number_format_i18n((float) $r['order_total'], 2)) . '</td>';
            echo '<td>' . ($outbound !== null ? esc_html(number_format_i18n($outbound, 2)) : '<span style="color:#9ca3af;">—</span>') . '</td>';
            echo '<td>' . ($returnc  !== null ? esc_html(number_format_i18n($returnc,  2)) : '<span style="color:#9ca3af;">—</span>') . '</td>';
            echo '<td>' . ($total_cost > 0 ? '<strong>' . esc_html(number_format_i18n($total_cost, 2)) . '</strong>' : '<span style="color:#9ca3af;">—</span>') . '</td>';
            echo '<td>' . esc_html((string) ($r['entry_channel'] ?? '—')) . '</td>';
            echo '</tr>';
        }
        echo '</tbody></table>';

        
        $total = (int) $sum['n'];
        $pages = (int) ceil($total / $per_page);
        if ($pages > 1) {
            echo '<p style="margin-top:10px;">';
            $qs = function ($p) use ($f_reason, $f_from, $f_to, $f_scope, $base_url) {
                $args = array('cn_section' => 'returns', 'rl_p' => $p);
                if ($f_reason !== '') { $args['rl_reason'] = $f_reason; }
                if ($f_from !== '')   { $args['rl_from']   = $f_from; }
                if ($f_to !== '')     { $args['rl_to']     = $f_to; }
                if ($f_scope !== '')  { $args['rl_scope']  = $f_scope; }
                return esc_url(add_query_arg($args, $base_url));
            };
            for ($i = 1; $i <= min($pages, 12); $i++) {
                $is_cur = ($i === $pageno);
                echo $is_cur
                    ? '<strong style="margin-right:6px;">' . (int) $i . '</strong>'
                    : '<a href="' . $qs($i) . '" style="margin-right:6px;">' . (int) $i . '</a>'; 
            }
            if ($pages > 12) { echo '<span style="color:#9ca3af;">… of ' . (int) $pages . '</span>'; }
            echo '</p>';
        }
        echo '</div>';
    }

    
    
    

    private function render_overview() {
        global $wpdb;
        $oc  = $wpdb->prefix . 'aieo_dmm_order_cancellations';
        $pbm = $wpdb->prefix . 'aieo_dmm_product_behavior_metrics';
        $ubm = $wpdb->prefix . 'aieo_dmm_user_behavior_metrics';

        $this->render_operator_guide_card();
        $this->render_operations_card($pbm, $ubm);
        $this->render_reco_integration_card();

        $total_cancellations    = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$oc}");
        $cancellations_30d      = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$oc} WHERE cancelled_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)");
        $revenue_lost_30d       = (float) $wpdb->get_var("SELECT IFNULL(SUM(order_total), 0) FROM {$oc} WHERE cancelled_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)");
        $high_risk_users        = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$ubm} WHERE risk_band = 2");
        $high_cancel_products   = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$pbm} WHERE cancellation_rate >= 0.10 AND total_orders >= 5");

        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Headline metrics', 'ai-eshop-optimizer') . '</h3>';
        echo '<table class="form-table"><tbody>';
        echo '<tr><th>' . esc_html__('Total cancellations logged', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n($total_cancellations)) . '</td></tr>';
        echo '<tr><th>' . esc_html__('Cancellations (last 30 days)', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n($cancellations_30d)) . '</td></tr>';
        echo '<tr><th>' . esc_html__('Revenue lost (last 30 days)', 'ai-eshop-optimizer') . '</th><td>€ ' . esc_html(number_format_i18n($revenue_lost_30d, 2)) . '</td></tr>';
        echo '<tr><th>' . esc_html__('High-risk users (risk_band = 2)', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n($high_risk_users)) . '</td></tr>';
        echo '<tr><th>' . esc_html__('Products with ≥10% cancellation rate (5+ orders)', 'ai-eshop-optimizer') . '</th><td>' . esc_html(number_format_i18n($high_cancel_products)) . '</td></tr>';
        echo '</tbody></table>';
        echo '</div>';

        
        echo '<div class="aieo-dmm-card" style="margin-top:14px;">';
        echo '<h3>' . esc_html__('Top reasons (last 30 days)', 'ai-eshop-optimizer') . '</h3>';
        $rows = $wpdb->get_results(
            "SELECT reason_slug, COUNT(*) AS n, ROUND(SUM(order_total), 2) AS revenue_lost
               FROM {$oc}
              WHERE cancelled_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
              GROUP BY reason_slug
              ORDER BY n DESC
              LIMIT 10",
            ARRAY_A
        );
        $reasons_index = class_exists('AIEO_DMM_Cancellation_Reasons_Data') ? AIEO_DMM_Cancellation_Reasons_Data::all() : array();
        if (!empty($rows)) {
            echo '<table class="wp-list-table widefat striped"><thead><tr>';
            echo '<th>' . esc_html__('Reason', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Count', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Revenue lost (€)', 'ai-eshop-optimizer') . '</th>';
            echo '</tr></thead><tbody>';
            foreach ($rows as $r) {
                $slug = (string) $r['reason_slug'];
                $label = isset($reasons_index[$slug]) ? $reasons_index[$slug]['label'] : $slug;
                echo '<tr>';
                echo '<td><strong>' . esc_html($label) . '</strong> <code style="font-size:10px;">' . esc_html($slug) . '</code></td>';
                echo '<td>' . esc_html(number_format_i18n((int) $r['n'])) . '</td>';
                echo '<td>' . esc_html(number_format_i18n((float) $r['revenue_lost'], 2)) . '</td>';
                echo '</tr>';
            }
            echo '</tbody></table>';
        } else {
            echo '<p>' . esc_html__('No cancellations in the last 30 days.', 'ai-eshop-optimizer') . '</p>';
        }
        echo '</div>';

        
        
        
        
        
        
        $base_url = admin_url('admin.php?page=' . AIEO_DMM::MENU_SLUG . '&dmm_tab=cancellations');
        $p_order_map = array(
            'count'  => 'total_cancellations',
            'rate'   => 'cancellation_rate',
            'orders' => 'total_orders',
            'last'   => 'last_cancelled_at',
            'reason' => 'top_reason_slug',
        );
        $p_orderby = isset($_GET['cn_porderby']) ? sanitize_key((string) wp_unslash($_GET['cn_porderby'])) : 'count'; 
        if (!isset($p_order_map[$p_orderby])) { $p_orderby = 'count'; }
        $p_order = isset($_GET['cn_porder']) ? strtolower(sanitize_key((string) wp_unslash($_GET['cn_porder']))) : 'desc'; 
        if (!in_array($p_order, array('asc', 'desc'), true)) { $p_order = 'desc'; }
        $p_order_sql = $p_order === 'asc' ? 'ASC' : 'DESC';
        $p_order_col = $p_order_map[$p_orderby];

        
        $sort_h = function ($label, $key) use ($base_url, $p_orderby, $p_order) {
            $next   = ($p_orderby === $key && $p_order === 'desc') ? 'asc' : 'desc';
            $arrow  = '';
            if ($p_orderby === $key) {
                $arrow = $p_order === 'desc' ? ' <span aria-hidden="true">▼</span>' : ' <span aria-hidden="true">▲</span>';
            }
            $url = add_query_arg(array('cn_porderby' => $key, 'cn_porder' => $next), $base_url);
            
            return '<a href="' . esc_url($url) . '" style="text-decoration:none;color:inherit;">' . esc_html($label) . $arrow . '</a>';
        };

        echo '<div class="aieo-dmm-card" style="margin-top:14px;">';
        echo '<h3>' . esc_html__('Cancellation hotspots (10+ orders)', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin:-4px 0 10px;font-size:12px;">'
            . esc_html__('Click any column header to re-sort. Default sort = highest absolute cancellation count, so you see the products bleeding most volume first. Switch to "Rate" to find the proportionally-worst offenders that still pass the 10-order signal threshold.', 'ai-eshop-optimizer')
            . '</p>';

        
        $rows = $wpdb->get_results(
            "SELECT product_id, total_orders, total_cancellations, cancellation_rate, last_cancelled_at, top_reason_slug
               FROM {$pbm}
              WHERE total_orders >= 10
              ORDER BY {$p_order_col} {$p_order_sql}, total_cancellations DESC
              LIMIT 50",
            ARRAY_A
        );
        if (!empty($rows)) {
            echo '<table class="wp-list-table widefat striped"><thead><tr>';
            echo '<th>' . esc_html__('Product', 'ai-eshop-optimizer') . '</th>'; 
            
            echo '<th>' . $sort_h(__('Orders', 'ai-eshop-optimizer'), 'orders') . '</th>';
            
            echo '<th>' . $sort_h(__('Cancellations', 'ai-eshop-optimizer'), 'count') . '</th>';
            
            echo '<th>' . $sort_h(__('Rate', 'ai-eshop-optimizer'), 'rate') . '</th>';
            
            echo '<th>' . $sort_h(__('Top reason', 'ai-eshop-optimizer'), 'reason') . '</th>';
            
            echo '<th>' . $sort_h(__('Last', 'ai-eshop-optimizer'), 'last') . '</th>';
            echo '</tr></thead><tbody>';
            foreach ($rows as $r) {
                $title = get_the_title((int) $r['product_id']);
                if (!$title) { $title = '#' . (int) $r['product_id']; }
                $rate_pct = round((float) $r['cancellation_rate'] * 100, 2);
                $rate_color = $rate_pct >= 15 ? '#b91c1c' : ($rate_pct >= 10 ? '#ca8a04' : '#374151');
                echo '<tr>';
                echo '<td><a href="' . esc_url(get_edit_post_link((int) $r['product_id'])) . '">' . esc_html($title) . '</a></td>';
                echo '<td>' . esc_html(number_format_i18n((int) $r['total_orders'])) . '</td>';
                echo '<td>' . esc_html(number_format_i18n((int) $r['total_cancellations'])) . '</td>';
                echo '<td><strong style="color:' . esc_attr($rate_color) . ';">' . esc_html($rate_pct) . '%</strong></td>';
                echo '<td>' . ($r['top_reason_slug'] ? '<code style="font-size:11px;">' . esc_html((string) $r['top_reason_slug']) . '</code>' : '—') . '</td>';
                echo '<td>' . esc_html((string) ($r['last_cancelled_at'] ?? '—')) . '</td>';
                echo '</tr>';
            }
            echo '</tbody></table>';
        } else {
            echo '<p>' . esc_html__('No products yet meet the 10-order threshold.', 'ai-eshop-optimizer') . '</p>';
        }
        echo '</div>';

        
        echo '<div class="aieo-dmm-card" style="margin-top:14px;">';
        echo '<h3>' . esc_html__('High-risk users (risk_band ≥ 1)', 'ai-eshop-optimizer') . '</h3>';

        
        $u_order_map = array(
            'count'  => 'total_cancellations',
            'rate'   => 'cancellation_rate',
            'orders' => 'total_orders',
            'band'   => 'risk_band',
            'last'   => 'last_cancelled_at',
        );
        $u_orderby = isset($_GET['cn_uorderby']) ? sanitize_key((string) wp_unslash($_GET['cn_uorderby'])) : 'band'; 
        if (!isset($u_order_map[$u_orderby])) { $u_orderby = 'band'; }
        $u_order = isset($_GET['cn_uorder']) ? strtolower(sanitize_key((string) wp_unslash($_GET['cn_uorder']))) : 'desc'; 
        if (!in_array($u_order, array('asc', 'desc'), true)) { $u_order = 'desc'; }
        $u_order_sql = $u_order === 'asc' ? 'ASC' : 'DESC';
        $u_order_col = $u_order_map[$u_orderby];

        $u_sort_h = function ($label, $key) use ($base_url, $u_orderby, $u_order) {
            $next   = ($u_orderby === $key && $u_order === 'desc') ? 'asc' : 'desc';
            $arrow  = '';
            if ($u_orderby === $key) {
                $arrow = $u_order === 'desc' ? ' <span aria-hidden="true">▼</span>' : ' <span aria-hidden="true">▲</span>';
            }
            $url = add_query_arg(array('cn_uorderby' => $key, 'cn_uorder' => $next), $base_url);
            
            return '<a href="' . esc_url($url) . '" style="text-decoration:none;color:inherit;">' . esc_html($label) . $arrow . '</a>';
        };

        
        $rows = $wpdb->get_results(
            "SELECT user_id, total_orders, total_cancellations, cancellation_rate, risk_band, last_cancelled_at
               FROM {$ubm}
              WHERE risk_band >= 1
              ORDER BY {$u_order_col} {$u_order_sql}, cancellation_rate DESC
              LIMIT 50",
            ARRAY_A
        );
        if (!empty($rows)) {
            echo '<table class="wp-list-table widefat striped"><thead><tr>';
            echo '<th>' . esc_html__('User', 'ai-eshop-optimizer') . '</th>'; 
            
            echo '<th>' . $u_sort_h(__('Orders', 'ai-eshop-optimizer'), 'orders') . '</th>';
            
            echo '<th>' . $u_sort_h(__('Cancellations', 'ai-eshop-optimizer'), 'count') . '</th>';
            
            echo '<th>' . $u_sort_h(__('Rate', 'ai-eshop-optimizer'), 'rate') . '</th>';
            
            echo '<th>' . $u_sort_h(__('Band', 'ai-eshop-optimizer'), 'band') . '</th>';
            
            echo '<th>' . $u_sort_h(__('Last', 'ai-eshop-optimizer'), 'last') . '</th>';
            echo '</tr></thead><tbody>';
            foreach ($rows as $r) {
                $u = get_user_by('id', (int) $r['user_id']);
                $label = $u ? ($u->display_name . ' <' . $u->user_email . '>') : ('#' . (int) $r['user_id']);
                $band  = (int) $r['risk_band'];
                $band_color = $band === 2 ? '#b91c1c' : '#ca8a04';
                $band_label = $band === 2 ? 'high' : 'elevated';
                echo '<tr>';
                echo '<td><a href="' . esc_url(get_edit_user_link((int) $r['user_id'])) . '">' . esc_html($label) . '</a></td>';
                echo '<td>' . esc_html(number_format_i18n((int) $r['total_orders'])) . '</td>';
                echo '<td>' . esc_html(number_format_i18n((int) $r['total_cancellations'])) . '</td>';
                echo '<td>' . esc_html(round((float) $r['cancellation_rate'] * 100, 2)) . '%</td>';
                echo '<td><span style="display:inline-block;font-weight:600;padding:2px 7px;border-radius:999px;background:' . esc_attr($band_color) . '22;color:' . esc_attr($band_color) . ';">' . esc_html($band_label) . '</span></td>';
                echo '<td>' . esc_html((string) ($r['last_cancelled_at'] ?? '—')) . '</td>';
                echo '</tr>';
            }
            echo '</tbody></table>';
        } else {
            echo '<p>' . esc_html__('No users currently flagged as elevated or high risk.', 'ai-eshop-optimizer') . '</p>';
        }
        echo '</div>';

        
        echo '<div class="aieo-dmm-card" style="margin-top:14px;">';
        echo '<h3>' . esc_html__('Recent cancellations (last 25)', 'ai-eshop-optimizer') . '</h3>';
        $rows = $wpdb->get_results(
            "SELECT order_id, user_id, reason_slug, reason_note, order_total, entry_channel, cancelled_at
               FROM {$oc}
              ORDER BY cancelled_at DESC
              LIMIT 25",
            ARRAY_A
        );
        if (!empty($rows)) {
            echo '<table class="wp-list-table widefat striped" style="font-size:12px;"><thead><tr>';
            echo '<th>' . esc_html__('When', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Order', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('User', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Reason', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Total (€)', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Channel', 'ai-eshop-optimizer') . '</th>';
            echo '</tr></thead><tbody>';
            foreach ($rows as $r) {
                $reason_label = isset($reasons_index[$r['reason_slug']]) ? $reasons_index[$r['reason_slug']]['label'] : $r['reason_slug'];
                echo '<tr>';
                echo '<td>' . esc_html((string) $r['cancelled_at']) . '</td>';
                echo '<td><a href="' . esc_url(admin_url('post.php?action=edit&post=' . (int) $r['order_id'])) . '">#' . (int) $r['order_id'] . '</a></td>';
                echo '<td>' . ((int) $r['user_id'] > 0 ? '#' . (int) $r['user_id'] : '<span style="color:#9ca3af;">guest</span>') . '</td>';
                echo '<td>' . esc_html((string) $reason_label) . ($r['reason_note'] ? ' <span style="color:#6b7280;font-size:11px;">— ' . esc_html(wp_trim_words((string) $r['reason_note'], 8)) . '</span>' : '') . '</td>';
                echo '<td>' . esc_html(number_format_i18n((float) $r['order_total'], 2)) . '</td>';
                echo '<td>' . esc_html((string) ($r['entry_channel'] ?? '—')) . '</td>';
                echo '</tr>';
            }
            echo '</tbody></table>';
        } else {
            echo '<p>' . esc_html__('No cancellations logged yet.', 'ai-eshop-optimizer') . '</p>';
        }
        echo '</div>';
    }

    
    
    

    private function render_vocabulary() {
        $reasons = class_exists('AIEO_DMM_Cancellation_Reasons_Data') ? AIEO_DMM_Cancellation_Reasons_Data::all() : array();
        $nonce   = wp_create_nonce(AIEO_DMM::NONCE_ACTION);
        $ajax    = admin_url('admin-ajax.php');

        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Cancellation reasons', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description">' . esc_html__('The dropdown the operator picks from on the order edit screen. Disabled rows are hidden from new picks but kept in the table for historical reports.', 'ai-eshop-optimizer') . '</p>';

        echo '<table class="wp-list-table widefat striped" id="aieo-dmm-cn-vocab"><thead><tr>';
        echo '<th>' . esc_html__('Slug', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Label', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Default type', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Customer-visible', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Sort', 'ai-eshop-optimizer') . '</th>';
        echo '<th>' . esc_html__('Enabled', 'ai-eshop-optimizer') . '</th>';
        echo '<th></th></tr></thead><tbody>';
        foreach ($reasons as $slug => $r) {
            $cur_kind = isset($r['default_event_kind']) && $r['default_event_kind'] !== null ? (string) $r['default_event_kind'] : '';
            echo '<tr data-slug="' . esc_attr($slug) . '" style="' . ((int) $r['enabled'] === 0 ? 'opacity:0.55;' : '') . '">';
            echo '<td><code>' . esc_html($slug) . '</code></td>';
            echo '<td><input type="text" class="regular-text aieo-cn-label" value="' . esc_attr((string) $r['label']) . '"></td>';
            echo '<td><select class="aieo-cn-kind">';
            echo '<option value=""  ' . selected($cur_kind, '',  false) . '>' . esc_html__('— ask operator —', 'ai-eshop-optimizer') . '</option>';
            echo '<option value="0" ' . selected($cur_kind, '0', false) . '>' . esc_html__('0 Cancellation (pre-shipment)', 'ai-eshop-optimizer') . '</option>';
            echo '<option value="1" ' . selected($cur_kind, '1', false) . '>' . esc_html__('1 Returned (after receipt)',   'ai-eshop-optimizer') . '</option>';
            echo '<option value="2" ' . selected($cur_kind, '2', false) . '>' . esc_html__('2 Kept (refund only)',         'ai-eshop-optimizer') . '</option>';
            echo '<option value="3" ' . selected($cur_kind, '3', false) . '>' . esc_html__('3 Refused (never received)',   'ai-eshop-optimizer') . '</option>';
            echo '</select></td>';
            echo '<td><input type="checkbox" class="aieo-cn-visible" ' . checked((int) $r['is_customer_visible'], 1, false) . '></td>';
            echo '<td><input type="number" class="small-text aieo-cn-sort" value="' . (int) $r['sort_order'] . '"></td>';
            echo '<td><input type="checkbox" class="aieo-cn-enabled" ' . checked((int) $r['enabled'], 1, false) . '></td>';
            echo '<td><button type="button" class="button button-small aieo-cn-save">' . esc_html__('Save', 'ai-eshop-optimizer') . '</button> '
                . '<button type="button" class="button-link aieo-cn-disable" style="color:#b91c1c;">' . esc_html__('Disable', 'ai-eshop-optimizer') . '</button></td>';
            echo '</tr>';
        }
        echo '</tbody></table>';

        echo '<h4 style="margin-top:18px;">' . esc_html__('Add new reason', 'ai-eshop-optimizer') . '</h4>';
        echo '<p>';
        echo '<input type="text" id="aieo-cn-new-slug" class="regular-text" placeholder="' . esc_attr__('slug (lowercase + underscores)', 'ai-eshop-optimizer') . '">';
        echo ' <input type="text" id="aieo-cn-new-label" class="regular-text" placeholder="' . esc_attr__('Human label', 'ai-eshop-optimizer') . '">';
        echo ' <button type="button" class="button button-primary" id="aieo-cn-new-add">' . esc_html__('Add', 'ai-eshop-optimizer') . '</button>';
        echo '</p>';

        ?>
        <script>
        (function(){
            var NONCE = <?php echo wp_json_encode($nonce); ?>;
            var AJAX  = <?php echo wp_json_encode($ajax); ?>;
            function post(action, data) {
                var fd = new FormData();
                fd.append('action', action);
                fd.append('nonce', NONCE);
                Object.keys(data).forEach(function(k){ fd.append(k, data[k]); });
                return fetch(AJAX, {method:'POST',credentials:'same-origin',body:fd}).then(function(r){return r.json();});
            }
            document.querySelectorAll('.aieo-cn-save').forEach(function(btn){
                btn.addEventListener('click', function(){
                    var tr = btn.closest('tr');
                    var slug = tr.dataset.slug;
                    post('aieo_dmm_cancel_reason_save', {
                        slug: slug,
                        label: tr.querySelector('.aieo-cn-label').value,
                        default_event_kind: tr.querySelector('.aieo-cn-kind').value,
                        is_customer_visible: tr.querySelector('.aieo-cn-visible').checked ? 1 : 0,
                        sort_order: tr.querySelector('.aieo-cn-sort').value,
                        enabled: tr.querySelector('.aieo-cn-enabled').checked ? 1 : 0,
                    }).then(function(j){
                        if (j && j.success) { btn.textContent = '✓'; setTimeout(function(){ btn.textContent = '<?php echo esc_js(__('Save', 'ai-eshop-optimizer')); ?>'; }, 1200); }
                        else { alert(j && j.data && j.data.message || 'Save failed'); }
                    });
                });
            });
            document.querySelectorAll('.aieo-cn-disable').forEach(function(btn){
                btn.addEventListener('click', function(){
                    if (!confirm('<?php echo esc_js(__('Disable this reason?', 'ai-eshop-optimizer')); ?>')) return;
                    var tr = btn.closest('tr');
                    post('aieo_dmm_cancel_reason_disable', { slug: tr.dataset.slug }).then(function(){ location.reload(); });
                });
            });
            document.getElementById('aieo-cn-new-add').addEventListener('click', function(){
                var slug  = document.getElementById('aieo-cn-new-slug').value.trim();
                var label = document.getElementById('aieo-cn-new-label').value.trim();
                if (!slug || !label) { alert('Slug and label required'); return; }
                post('aieo_dmm_cancel_reason_save', {
                    slug: slug, label: label, is_customer_visible: 1, sort_order: 999, enabled: 1
                }).then(function(j){
                    if (j && j.success) { location.reload(); }
                    else { alert(j && j.data && j.data.message || 'Add failed'); }
                });
            });
        })();
        </script>
        <?php
        echo '</div>';
    }

    
    
    

    private function render_reco_integration_card() {
        $penalty_threshold = (float) apply_filters('aieo_dmm_pbm_penalty_threshold', 0.10);
        $penalty_enabled   = (bool)  apply_filters('aieo_dmm_reco_penalty_enabled', true);
        $drop_threshold    = (float) apply_filters('aieo_dmm_reco_drop_threshold', 0.0);

        $notice_on = (bool) get_option(AIEO_DMM_Risk_Gating::OPT_NOTICE, false);
        $cod_on    = (bool) get_option(AIEO_DMM_Risk_Gating::OPT_BLOCK_COD, false);
        $nonce     = wp_create_nonce(AIEO_DMM::NONCE_ACTION);
        $ajax      = admin_url('admin-ajax.php');

        echo '<div class="aieo-dmm-card" style="margin-bottom:14px;">';
        echo '<h3>' . esc_html__('Recommendation engine integration', 'ai-eshop-optimizer') . '</h3>';
        echo '<p class="description" style="margin-top:0;">'
           . esc_html__('Cancellation signals are wired into both the product reco engine (high-cancellation products are down-ranked or dropped) and the per-user risk band (band 0 normal / 1 elevated / 2 high). Toggles below opt in to the user-side checkout integrations.', 'ai-eshop-optimizer')
           . '</p>';

        echo '<table class="form-table"><tbody>';
        echo '<tr><th>' . esc_html__('Product reco penalty', 'ai-eshop-optimizer') . '</th><td>';
        echo $penalty_enabled
            ? '<strong style="color:#16a34a;">' . esc_html__('Active', 'ai-eshop-optimizer') . '</strong>'
            : '<strong style="color:#9ca3af;">' . esc_html__('Disabled by filter', 'ai-eshop-optimizer') . '</strong>';
        echo ' <span style="color:#6b7280;font-size:11px;">' . sprintf(
            
            esc_html__('threshold %1$s%%, drop ≤ %2$s', 'ai-eshop-optimizer'),
            esc_html(round($penalty_threshold * 100, 0)),
            esc_html(round($drop_threshold, 2))
        ) . '</span>';
        echo '</td></tr>';

        echo '<tr><th><label>' . esc_html__('Show checkout warning to elevated/high risk users', 'ai-eshop-optimizer') . '</label></th>';
        echo '<td><label><input type="checkbox" id="aieo-rg-notice" ' . checked($notice_on, true, false) . '> ' . esc_html__('Render a polite review-your-order banner above checkout', 'ai-eshop-optimizer') . '</label></td></tr>';

        echo '<tr><th><label>' . esc_html__('Block COD for high-risk (band 2) users', 'ai-eshop-optimizer') . '</label></th>';
        echo '<td><label><input type="checkbox" id="aieo-rg-cod" ' . checked($cod_on, true, false) . '> ' . esc_html__('Remove cash-on-delivery from the gateway list at checkout for band-2 customers', 'ai-eshop-optimizer') . '</label></td></tr>';
        echo '</tbody></table>';

        echo '<p><span class="description" id="aieo-rg-msg"></span></p>';

        echo '<details style="margin-top:10px;"><summary><strong>' . esc_html__('Theme integration cheat-sheet', 'ai-eshop-optimizer') . '</strong></summary>';
        echo '<pre style="background:#0f172a;color:#e2e8f0;padding:12px;border-radius:4px;font-size:11px;overflow:auto;">';
        echo "// In your theme/plugin, gate UX by risk band:\n";
        echo "\$band = AIEO_DMM_User_Behavior_Metrics::risk_band(\$user_id); // 0|1|2\n\n";
        echo "// Adjust the bands per your policy (e.g. tighter on COD-eligible regions):\n";
        echo "add_filter('aieo_dmm_user_risk_thresholds', function (\$t) {\n";
        echo "    return array_merge(\$t, ['min_orders' => 5, 'high_rate' => 0.20]);\n";
        echo "});\n\n";
        echo "// Force an override for a specific user (e.g. VIP whitelisting):\n";
        echo "add_filter('aieo_dmm_user_risk_band', function (\$band, \$uid) {\n";
        echo "    if (in_array(\$uid, [42, 1337], true)) { return 0; }\n";
        echo "    return \$band;\n";
        echo "}, 10, 2);\n\n";
        echo "// Down-rank thresholds for the reco penalty:\n";
        echo "add_filter('aieo_dmm_pbm_penalty_threshold', fn() => 0.15);  // start penalising at 15% rate\n";
        echo "add_filter('aieo_dmm_reco_drop_threshold',   fn() => 0.40);  // drop products with penalty ≤ 0.40\n\n";
        echo "// Per-kind weights for the PRODUCT reco penalty.\n";
        echo "// Defaults: returned=1.0, kept=0.5, refused=0.1\n";
        echo "// (refused tells us nothing about the product — parcel never opened).\n";
        echo "add_filter('aieo_dmm_pbm_kind_weights', fn() => [1 => 1.0, 2 => 0.4, 3 => 0.05]);\n\n";
        echo "// Per-kind weights for the USER risk band.\n";
        echo "// Defaults: returned=0.7, kept=1.0, refused=1.5\n";
        echo "// (refused is the strongest customer-friction signal — they bailed without opening).\n";
        echo "add_filter('aieo_dmm_ubm_kind_weights', fn() => [1 => 0.7, 2 => 1.0, 3 => 2.0]);\n";
        echo '</pre></details>';

        ?>
        <script>
        (function(){
            var NONCE = <?php echo wp_json_encode($nonce); ?>;
            var AJAX  = <?php echo wp_json_encode($ajax); ?>;
            function bind(id, optionKey) {
                var el = document.getElementById(id);
                if (!el) return;
                el.addEventListener('change', function(){
                    var msg = document.getElementById('aieo-rg-msg');
                    var fd = new FormData();
                    fd.append('action', 'aieo_dmm_risk_gating_toggle');
                    fd.append('nonce', NONCE);
                    fd.append('option', optionKey);
                    fd.append('value', el.checked ? 1 : 0);
                    msg.textContent = '<?php echo esc_js(__('Saving…', 'ai-eshop-optimizer')); ?>';
                    fetch(AJAX, {method:'POST',credentials:'same-origin',body:fd}).then(function(r){return r.json();}).then(function(j){
                        msg.textContent = (j && j.success) ? '<?php echo esc_js(__('Saved.', 'ai-eshop-optimizer')); ?>' : '<?php echo esc_js(__('Save failed.', 'ai-eshop-optimizer')); ?>';
                    });
                });
            }
            bind('aieo-rg-notice', '<?php echo esc_js(AIEO_DMM_Risk_Gating::OPT_NOTICE); ?>');
            bind('aieo-rg-cod',    '<?php echo esc_js(AIEO_DMM_Risk_Gating::OPT_BLOCK_COD); ?>');
        })();
        </script>
        <?php
        echo '</div>';
    }

    
    
    

    public function ajax_save_reason() {
        AIEO_DMM::verify_ajax_request();
        $slug = isset($_POST['slug']) ? sanitize_key(wp_unslash($_POST['slug'])) : '';
        if ($slug === '') {
            wp_send_json_error(array('message' => __('Slug required.', 'ai-eshop-optimizer')));
        }
        $patch = array(
            'label'               => isset($_POST['label']) ? sanitize_text_field(wp_unslash($_POST['label'])) : '',
            'is_customer_visible' => !empty($_POST['is_customer_visible']) ? 1 : 0,
            'sort_order'          => isset($_POST['sort_order']) ? (int) $_POST['sort_order'] : 0,
            'enabled'             => !empty($_POST['enabled']) ? 1 : 0,
        );
        if (isset($_POST['default_event_kind'])) {
            $patch['default_event_kind'] = (string) wp_unslash($_POST['default_event_kind']);
        }
        $id = AIEO_DMM_Cancellation_Reasons_Data::save($slug, $patch);
        wp_send_json_success(array('id' => (int) $id));
    }

    public function ajax_disable_reason() {
        AIEO_DMM::verify_ajax_request();
        $slug = isset($_POST['slug']) ? sanitize_key(wp_unslash($_POST['slug'])) : '';
        if ($slug === '') {
            wp_send_json_error(array('message' => __('Slug required.', 'ai-eshop-optimizer')));
        }
        AIEO_DMM_Cancellation_Reasons_Data::disable($slug);
        wp_send_json_success();
    }

    public function ajax_metrics_backfill() {
        AIEO_DMM::verify_ajax_request();
        @set_time_limit(300); 
        $t0 = microtime(true);

        
        
        
        
        
        
        
        
        
        $historical = function_exists('aieo_dmm_backfill_order_cancellations')
            ? (int) aieo_dmm_backfill_order_cancellations()
            : 0;

        
        
        
        $products = class_exists('AIEO_DMM_Product_Behavior_Metrics') ? (int) AIEO_DMM_Product_Behavior_Metrics::backfill_all() : 0;
        $users    = class_exists('AIEO_DMM_User_Behavior_Metrics')    ? (int) AIEO_DMM_User_Behavior_Metrics::backfill_all()    : 0;
        wp_send_json_success(array(
            'historical' => $historical,
            'products'   => $products,
            'users'      => $users,
            'elapsed_ms' => (int) round((microtime(true) - $t0) * 1000),
        ));
    }

    
    public function ajax_metrics_backfill_step() {
        AIEO_DMM::verify_ajax_request();
        @set_time_limit(120); 

        $phase = isset($_POST['phase']) ? sanitize_key(wp_unslash($_POST['phase'])) : 'ingest';

        if ($phase === 'aggregate') {
            $t0 = microtime(true);
            $products = class_exists('AIEO_DMM_Product_Behavior_Metrics') ? (int) AIEO_DMM_Product_Behavior_Metrics::backfill_all() : 0;
            $users    = class_exists('AIEO_DMM_User_Behavior_Metrics')    ? (int) AIEO_DMM_User_Behavior_Metrics::backfill_all()    : 0;
            wp_send_json_success(array(
                'phase'      => 'aggregate',
                'products'   => $products,
                'users'      => $users,
                'elapsed_ms' => (int) round((microtime(true) - $t0) * 1000),
                'done'       => true,
            ));
        }

        
        if (!function_exists('aieo_dmm_backfill_order_cancellations_chunked')) {
            wp_send_json_error(array('message' => __('Chunked backfill function not loaded.', 'ai-eshop-optimizer')));
        }
        $cursor = isset($_POST['cursor']) ? (int) $_POST['cursor'] : 0;
        $limit  = isset($_POST['limit'])  ? (int) $_POST['limit']  : 3000;
        $t0 = microtime(true);
        $r  = aieo_dmm_backfill_order_cancellations_chunked($cursor, $limit);
        wp_send_json_success(array(
            'phase'       => 'ingest',
            'processed'   => (int) $r['processed'],
            'chunk_size'  => (int) $r['chunk_size'],
            'next_cursor' => (int) $r['next_cursor'],
            'total'       => (int) $r['total'],
            'has_more'    => (bool) $r['has_more'],
            'elapsed_ms'  => (int) round((microtime(true) - $t0) * 1000),
        ));
    }

    public function ajax_metrics_recompute_stale() {
        AIEO_DMM::verify_ajax_request();
        $t0 = microtime(true);
        $products = class_exists('AIEO_DMM_Product_Behavior_Metrics') ? (int) AIEO_DMM_Product_Behavior_Metrics::recompute_stale(24, 200) : 0;
        $users    = class_exists('AIEO_DMM_User_Behavior_Metrics')    ? (int) AIEO_DMM_User_Behavior_Metrics::recompute_stale(24, 200)    : 0;
        wp_send_json_success(array(
            'products'   => $products,
            'users'      => $users,
            'elapsed_ms' => (int) round((microtime(true) - $t0) * 1000),
        ));
    }

    
    public function ajax_outbound_cost_step() {
        AIEO_DMM::verify_ajax_request();
        if (!class_exists('AIEO_DMM_Order_Cancellations')) {
            wp_send_json_error(array('message' => __('Cancellations data class not loaded.', 'ai-eshop-optimizer')));
        }
        @set_time_limit(120); 
        $batch = isset($_POST['batch']) ? max(10, min(200, (int) $_POST['batch'])) : 100;
        $r = AIEO_DMM_Order_Cancellations::backfill_outbound_costs_batch($batch);
        wp_send_json_success($r);
    }

    public function ajax_risk_gating_toggle() {
        AIEO_DMM::verify_ajax_request();
        $option = isset($_POST['option']) ? sanitize_key(wp_unslash($_POST['option'])) : '';
        $allowed = array(AIEO_DMM_Risk_Gating::OPT_NOTICE, AIEO_DMM_Risk_Gating::OPT_BLOCK_COD);
        if (!in_array($option, $allowed, true)) {
            wp_send_json_error(array('message' => __('Unknown option.', 'ai-eshop-optimizer')));
        }
        $value = !empty($_POST['value']);
        update_option($option, $value ? 1 : 0);
        wp_send_json_success(array('option' => $option, 'value' => $value ? 1 : 0));
    }
}
