<?php

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

class AIEO_DMM_Order_Cancellations {

    
    const META_REASON_SLUG       = '_aieo_cancel_reason_slug';
    const META_REASON_NOTE       = '_aieo_cancel_reason_note';
    
    const META_RETURNED_PRODUCTS = '_aieo_cancel_returned_product_ids';
    
    const META_RETURN_SHIP_COST  = '_aieo_cancel_return_shipping_cost';
    
    const META_OUTBOUND_SHIP_COST = '_aieo_cancel_outbound_shipping_cost';
    
    const META_EVENT_KIND        = '_aieo_cancel_event_kind';

    
    const KIND_CANCELLATION = 0;
    const KIND_RETURNED     = 1;   
    const KIND_KEPT         = 2;   
    const KIND_REFUSED      = 3;   
    
    const KIND_RETURN       = 1;
    
    const KIND_REFUND_ONLY  = 2;

    
    private static $cancel_statuses = array('cancelled', 'refunded', 'failed');

    public static function table_orders() {
        global $wpdb;
        return $wpdb->prefix . 'aieo_dmm_order_cancellations';
    }

    public static function table_lines() {
        global $wpdb;
        return $wpdb->prefix . 'aieo_dmm_order_line_refunds';
    }

    
    const CRON_DAILY_RECOMPUTE = 'aieo_dmm_metrics_daily_recompute';

    
    public static function bootstrap() {
        static $done = false;
        if ($done) { return; }
        $done = true;

        
        add_action('woocommerce_order_status_changed', array(__CLASS__, 'on_order_status_changed'), 20, 4);
        
        add_action('woocommerce_order_refunded',       array(__CLASS__, 'on_order_refunded'),       20, 2);

        
        
        
        
        add_action(self::CRON_DAILY_RECOMPUTE, array(__CLASS__, 'cron_daily_recompute'));
        if (!wp_next_scheduled(self::CRON_DAILY_RECOMPUTE)) {
            wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', self::CRON_DAILY_RECOMPUTE);
        }

        if (!is_admin()) { return; }

        
        
        
        
        
        add_action('add_meta_boxes_woocommerce_page_wc-orders', array(__CLASS__, 'register_metabox'));
        add_action('add_meta_boxes_shop_order',                 array(__CLASS__, 'register_metabox'));
        
        add_action('woocommerce_process_shop_order_meta',       array(__CLASS__, 'save_metabox_legacy'), 20, 2);
        add_action('woocommerce_update_order',                  array(__CLASS__, 'save_metabox_hpos'), 20, 1);
    }

    
    
    

    public static function on_order_status_changed($order_id, $status_before, $status_after, $order = null) {
        if (!in_array((string) $status_after, self::$cancel_statuses, true)) { return; }
        if (!$order) { $order = wc_get_order($order_id); }
        if (!$order || !is_object($order)) { return; }

        global $wpdb;
        $order_id = (int) $order->get_id();

        
        $reason_slug = (string) $order->get_meta(self::META_REASON_SLUG, true);
        $reason_note = (string) $order->get_meta(self::META_REASON_NOTE, true);
        if ($reason_slug === '') { $reason_slug = 'other'; }

        
        
        if (class_exists('AIEO_DMM_Cancellation_Reasons_Data')) {
            $found = AIEO_DMM_Cancellation_Reasons_Data::get($reason_slug);
            if (!$found || (int) $found['enabled'] === 0) { $reason_slug = 'other'; }
        }

        
        
        
        
        
        $entry_channel     = (string) $order->get_meta('_aieo_entry_channel', true);
        $entry_branch_id   = self::resolve_branch_id_for_order($order);
        $entry_campaign_id = self::resolve_campaign_id_for_order($order);

        
        
        
        
        
        
        
        $all_product_ids = array();
        foreach ($order->get_items() as $line) {
            $pid = (int) $line->get_product_id();
            if ($pid > 0) { $all_product_ids[] = $pid; }
        }
        $all_product_ids = array_values(array_unique($all_product_ids));

        $returned_meta = (string) $order->get_meta(self::META_RETURNED_PRODUCTS, true);
        $returned_ids  = array();
        if ($returned_meta !== '') {
            $parsed = array_filter(array_map('intval', explode(',', $returned_meta)));
            
            $returned_ids = array_values(array_intersect($all_product_ids, $parsed));
        }
        if (empty($returned_ids)) {
            $returned_ids = $all_product_ids;
        }
        $is_full_cancel = (count($returned_ids) === count($all_product_ids)) ? 1 : 0;

        
        
        
        
        
        
        
        
        $event_kind = self::resolve_event_kind($order, $status_after, $reason_slug);

        
        
        
        
        
        
        
        
        $outbound_cost = null;
        $return_cost   = null;
        if ($event_kind !== self::KIND_CANCELLATION) {
            $outbound_meta = (string) $order->get_meta(self::META_OUTBOUND_SHIP_COST, true);
            $outbound_cost = ($outbound_meta !== '' && is_numeric($outbound_meta))
                ? round((float) $outbound_meta, 2)
                : self::compute_outbound_cost_for_order($order);
        }
        if ($event_kind === self::KIND_RETURNED || $event_kind === self::KIND_REFUSED) {
            $return_meta = (string) $order->get_meta(self::META_RETURN_SHIP_COST, true);
            if ($return_meta !== '' && is_numeric($return_meta)) {
                $return_cost = round((float) $return_meta, 2);
            } elseif ($event_kind === self::KIND_REFUSED) {
                
                
                
                
                $return_cost = $outbound_cost;
            }
        }

        $row = array(
            'order_id'               => $order_id,
            'user_id'                => (int) $order->get_user_id(),
            'reason_slug'            => $reason_slug,
            'reason_note'            => $reason_note !== '' ? $reason_note : null,
            'status_before'          => (string) $status_before,
            'status_after'           => (string) $status_after,
            'order_total'            => (float) $order->get_total(),
            'product_ids'            => wp_json_encode($returned_ids),
            'is_full_cancel'         => $is_full_cancel,
            'entry_channel'          => $entry_channel !== '' ? $entry_channel : null,
            'entry_campaign_id'      => $entry_campaign_id,
            'entry_branch_id'        => $entry_branch_id,
            'event_kind'             => (int) $event_kind,
            'outbound_shipping_cost' => $outbound_cost,
            'return_shipping_cost'   => $return_cost,
            'cancelled_by_user_id'   => (int) get_current_user_id(),
            'cancelled_at'           => current_time('mysql', true),
        );

        $tbl = self::table_orders();
        $existing_id = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM {$tbl} WHERE order_id = %d LIMIT 1",
            $order_id
        ));
        if ($existing_id) {
            
            
            $wpdb->update($tbl, $row, array('id' => (int) $existing_id));
        } else {
            $wpdb->insert($tbl, $row);
        }

        do_action('aieo_dmm_order_cancellation_logged', $order_id, $row);

        
        
        
        do_action('aieo_dmm_recompute_behavior_metrics_for_user', (int) $row['user_id']);
        foreach ($returned_ids as $pid) {
            do_action('aieo_dmm_recompute_behavior_metrics_for_product', (int) $pid);
        }
    }

    
    
    

    public static function on_order_refunded($order_id, $refund_id) {
        $order_id  = (int) $order_id;
        $refund_id = (int) $refund_id;
        if ($order_id <= 0 || $refund_id <= 0) { return; }

        $refund = wc_get_order($refund_id);
        if (!$refund) { return; }
        $order = wc_get_order($order_id);
        if (!$order) { return; }

        global $wpdb;
        $tbl = self::table_lines();

        
        
        
        
        $refund_reason = method_exists($refund, 'get_reason') ? (string) $refund->get_reason() : '';
        $reason_slug = 'other';
        $reason_note = $refund_reason !== '' ? $refund_reason : null;
        if ($refund_reason !== '' && class_exists('AIEO_DMM_Cancellation_Reasons_Data')) {
            
            $candidate = sanitize_key($refund_reason);
            $found = AIEO_DMM_Cancellation_Reasons_Data::get($candidate);
            if ($found && (int) $found['enabled'] === 1) {
                $reason_slug = $candidate;
                $reason_note = null;
            }
        }

        $now      = current_time('mysql', true);
        $user_id  = (int) $order->get_user_id();
        $touched_products = array();

        foreach ($refund->get_items() as $line) {
            $product_id = (int) $line->get_product_id();
            if ($product_id <= 0) { continue; }
            $variation_id   = (int) $line->get_variation_id();
            
            $qty            = abs((int) $line->get_quantity());
            $amount         = abs((float) $line->get_total());
            if ($qty <= 0 && $amount <= 0) { continue; }

            $wpdb->query($wpdb->prepare(
                "INSERT INTO {$tbl}
                  (order_id, refund_id, user_id, product_id, variation_id, qty_refunded, amount_refunded, reason_slug, reason_note, refunded_at)
                 VALUES (%d, %d, %d, %d, %d, %d, %f, %s, %s, %s)
                 ON DUPLICATE KEY UPDATE
                   qty_refunded    = VALUES(qty_refunded),
                   amount_refunded = VALUES(amount_refunded),
                   reason_slug     = VALUES(reason_slug),
                   reason_note     = VALUES(reason_note)",
                $order_id, $refund_id, $user_id,
                $product_id, $variation_id,
                $qty, $amount,
                $reason_slug, $reason_note,
                $now
            ));
            $touched_products[] = $product_id;
        }

        do_action('aieo_dmm_order_refund_logged', $order_id, $refund_id);
        if ($user_id > 0) {
            do_action('aieo_dmm_recompute_behavior_metrics_for_user', $user_id);
        }
        foreach (array_unique($touched_products) as $pid) {
            do_action('aieo_dmm_recompute_behavior_metrics_for_product', (int) $pid);
        }
    }

    
    
    

    public static function register_metabox($post_or_order) {
        
        
        
        
        $is_hpos = class_exists('\Automattic\WooCommerce\Utilities\OrderUtil')
            && method_exists('\Automattic\WooCommerce\Utilities\OrderUtil', 'custom_orders_table_usage_is_enabled')
            && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();
        $screen = $is_hpos
            ? wc_get_page_screen_id('shop-order')   
            : 'shop_order';
        add_meta_box(
            'aieo_dmm_cancellation_reason',
            __('AIEO Cancellation Reason', 'ai-eshop-optimizer'),
            array(__CLASS__, 'render_metabox'),
            $screen,
            'side',
            'default'
        );
    }

    public static function render_metabox($post_or_order) {
        $order = $post_or_order instanceof WC_Order
            ? $post_or_order
            : wc_get_order(is_object($post_or_order) ? $post_or_order->ID : 0);
        if (!$order) {
            echo '<p>' . esc_html__('Order not loaded.', 'ai-eshop-optimizer') . '</p>';
            return;
        }
        $current_slug    = (string) $order->get_meta(self::META_REASON_SLUG, true);
        $current_note    = (string) $order->get_meta(self::META_REASON_NOTE, true);
        $current_returned = (string) $order->get_meta(self::META_RETURNED_PRODUCTS, true);
        $current_returned_ids = array_filter(array_map('intval', explode(',', $current_returned)));
        $current_outbound = (string) $order->get_meta(self::META_OUTBOUND_SHIP_COST, true);
        $current_return   = (string) $order->get_meta(self::META_RETURN_SHIP_COST, true);
        $current_kind_meta = (string) $order->get_meta(self::META_EVENT_KIND, true);
        
        
        $resolved_kind = self::resolve_event_kind($order, $order->get_status(), $current_slug);

        
        
        $lines = array();
        foreach ($order->get_items() as $item_id => $line) {
            $pid = (int) $line->get_product_id();
            if ($pid <= 0) { continue; }
            $lines[] = array(
                'product_id' => $pid,
                'name'       => $line->get_name(),
                'qty'        => max(1, (int) $line->get_quantity()),
                'total'      => (float) $line->get_total(),
            );
        }
        $is_multi_line = count($lines) > 1;

        
        
        $outbound_default = ($current_outbound !== '' && is_numeric($current_outbound))
            ? (float) $current_outbound
            : (float) $order->get_shipping_total();

        wp_nonce_field('aieo_dmm_cancel_meta_' . $order->get_id(), 'aieo_dmm_cancel_nonce');

        $reasons = class_exists('AIEO_DMM_Cancellation_Reasons_Data')
            ? AIEO_DMM_Cancellation_Reasons_Data::enabled()
            : array();

        
        $reason_kind_map = array();
        foreach ($reasons as $slug => $r) {
            $reason_kind_map[$slug] = isset($r['default_event_kind']) && $r['default_event_kind'] !== null
                ? (int) $r['default_event_kind']
                : null;
        }
        ?>
        <p>
            <label for="aieo_dmm_cancel_reason"><?php esc_html_e('Reason', 'ai-eshop-optimizer'); ?></label>
            <select id="aieo_dmm_cancel_reason" name="aieo_dmm_cancel_reason" style="width:100%;">
                <option value=""><?php esc_html_e('— select —', 'ai-eshop-optimizer'); ?></option>
                <?php foreach ($reasons as $slug => $r): ?>
                    <option value="<?php echo esc_attr($slug); ?>" <?php selected($current_slug, $slug); ?>>
                        <?php echo esc_html($r['label']); ?>
                    </option>
                <?php endforeach; ?>
            </select>
        </p>
        <p>
            <label for="aieo_dmm_cancel_note"><?php esc_html_e('Note (optional)', 'ai-eshop-optimizer'); ?></label>
            <textarea id="aieo_dmm_cancel_note" name="aieo_dmm_cancel_note" rows="3" style="width:100%;"><?php echo esc_textarea($current_note); ?></textarea>
        </p>

        <p style="margin-bottom:2px;margin-top:14px;"><strong><?php esc_html_e('Type', 'ai-eshop-optimizer'); ?></strong></p>
        <p class="description" style="margin:0 0 6px 0;">
            <?php esc_html_e('Auto-set from the reason; override if needed. Drives shipping-cost capture and the recommendation-engine signal weight.', 'ai-eshop-optimizer'); ?>
        </p>
        <ul style="margin:0 0 12px 0;list-style:none;padding-left:0;">
            <li style="margin:3px 0;">
                <label style="display:flex;align-items:flex-start;gap:6px;">
                    <input type="radio" name="aieo_dmm_cancel_kind" value="0" <?php checked($resolved_kind, self::KIND_CANCELLATION); ?>>
                    <span style="font-size:12px;"><strong><?php esc_html_e('Cancellation', 'ai-eshop-optimizer'); ?></strong> — <?php esc_html_e('voided before shipping (no shipping cost, no signal)', 'ai-eshop-optimizer'); ?></span>
                </label>
            </li>
            <li style="margin:3px 0;">
                <label style="display:flex;align-items:flex-start;gap:6px;">
                    <input type="radio" name="aieo_dmm_cancel_kind" value="1" <?php checked($resolved_kind, self::KIND_RETURNED); ?>>
                    <span style="font-size:12px;"><strong><?php esc_html_e('Returned (after receipt)', 'ai-eshop-optimizer'); ?></strong> — <?php esc_html_e('customer received + sent back. Strong product signal.', 'ai-eshop-optimizer'); ?></span>
                </label>
            </li>
            <li style="margin:3px 0;">
                <label style="display:flex;align-items:flex-start;gap:6px;">
                    <input type="radio" name="aieo_dmm_cancel_kind" value="2" <?php checked($resolved_kind, self::KIND_KEPT); ?>>
                    <span style="font-size:12px;"><strong><?php esc_html_e('Kept (refund only)', 'ai-eshop-optimizer'); ?></strong> — <?php esc_html_e('customer received + kept; outbound cost only.', 'ai-eshop-optimizer'); ?></span>
                </label>
            </li>
            <li style="margin:3px 0;">
                <label style="display:flex;align-items:flex-start;gap:6px;">
                    <input type="radio" name="aieo_dmm_cancel_kind" value="3" <?php checked($resolved_kind, self::KIND_REFUSED); ?>>
                    <span style="font-size:12px;"><strong><?php esc_html_e('Refused (never received)', 'ai-eshop-optimizer'); ?></strong> — <?php esc_html_e('refused at door / never collected. Strong customer signal — product never opened.', 'ai-eshop-optimizer'); ?></span>
                </label>
            </li>
        </ul>

        <?php if ($is_multi_line): ?>
            <p style="margin-bottom:4px;"><strong><?php esc_html_e('Affected items', 'ai-eshop-optimizer'); ?></strong></p>
            <p class="description" style="margin-top:0;">
                <?php esc_html_e('Tick only the items affected. Leave all blank to apply to the whole order.', 'ai-eshop-optimizer'); ?>
            </p>
            <ul style="margin:6px 0 12px 0;list-style:none;padding-left:0;">
                <?php foreach ($lines as $ln): ?>
                    <li style="margin:3px 0;">
                        <label style="display:flex;align-items:flex-start;gap:6px;">
                            <input type="checkbox" name="aieo_dmm_cancel_returned[]" value="<?php echo (int) $ln['product_id']; ?>"
                                <?php checked(in_array((int) $ln['product_id'], $current_returned_ids, true)); ?>>
                            <span style="font-size:12px;line-height:1.3;">
                                <?php echo esc_html($ln['name']); ?>
                                <span style="color:#6b7280;">× <?php echo (int) $ln['qty']; ?></span>
                            </span>
                        </label>
                    </li>
                <?php endforeach; ?>
            </ul>
        <?php else: ?>
            <p class="description" style="margin-top:0;">
                <?php esc_html_e('Single-item order — the entire order is the affected item.', 'ai-eshop-optimizer'); ?>
            </p>
        <?php endif; ?>

        <div id="aieo-dmm-cost-block">
            <p style="margin-bottom:4px;"><strong><?php esc_html_e('Shipping cost (€)', 'ai-eshop-optimizer'); ?></strong></p>
            <p style="margin:2px 0;">
                <label for="aieo_dmm_cancel_outbound" style="display:block;font-size:11px;color:#4b5563;">
                    <?php esc_html_e('Outbound (what we paid to send)', 'ai-eshop-optimizer'); ?>
                </label>
                <input type="number" step="0.01" min="0" id="aieo_dmm_cancel_outbound" name="aieo_dmm_cancel_outbound"
                       value="<?php echo esc_attr($current_outbound !== '' ? number_format((float) $current_outbound, 2, '.', '') : ''); ?>"
                       placeholder="<?php esc_attr_e('auto-resolved from carrier rate if blank', 'ai-eshop-optimizer'); ?>" style="width:100%;">
            </p>
            <p style="margin:2px 0;" id="aieo-dmm-return-cost-row">
                <label for="aieo_dmm_cancel_return" style="display:block;font-size:11px;color:#4b5563;">
                    <?php esc_html_e('Return (what we paid to fetch)', 'ai-eshop-optimizer'); ?>
                </label>
                <input type="number" step="0.01" min="0" id="aieo_dmm_cancel_return" name="aieo_dmm_cancel_return"
                       value="<?php echo esc_attr($current_return !== '' ? number_format((float) $current_return, 2, '.', '') : ''); ?>"
                       placeholder="<?php esc_attr_e('e.g. 3.50 — manually entered', 'ai-eshop-optimizer'); ?>" style="width:100%;">
            </p>
        </div>

        <p class="description" style="margin-top:8px;">
            <?php esc_html_e('Tip: pre-fill the reason BEFORE flipping the order status — the cancellation row picks it up automatically. Editing after the status change also works (the row updates in place).', 'ai-eshop-optimizer'); ?>
        </p>

        <script>
        (function(){
            var REASON_KIND = <?php echo wp_json_encode($reason_kind_map); ?>;
            var reasonSel = document.getElementById('aieo_dmm_cancel_reason');
            var kindRadios = document.getElementsByName('aieo_dmm_cancel_kind');
            var costBlock = document.getElementById('aieo-dmm-cost-block');
            var retRow    = document.getElementById('aieo-dmm-return-cost-row');
            function getKind() {
                for (var i = 0; i < kindRadios.length; i++) { if (kindRadios[i].checked) return parseInt(kindRadios[i].value, 10); }
                return 0;
            }
            function setKind(v) {
                for (var i = 0; i < kindRadios.length; i++) { kindRadios[i].checked = (parseInt(kindRadios[i].value, 10) === v); }
                applyVisibility();
            }
            function applyVisibility() {
                var k = getKind();
                // kind 0 → hide all costs.
                // kind 1 → outbound + return (manual return cost).
                // kind 2 → outbound only (no return leg).
                // kind 3 → outbound + return (courier return leg, often
                //          equal to outbound; operator may override).
                costBlock.style.display = (k === 0) ? 'none' : 'block';
                retRow.style.display    = (k === 1 || k === 3) ? 'block' : 'none';
            }
            reasonSel.addEventListener('change', function(){
                var slug = reasonSel.value;
                if (slug && REASON_KIND[slug] !== undefined && REASON_KIND[slug] !== null) {
                    setKind(REASON_KIND[slug]);
                }
            });
            for (var i = 0; i < kindRadios.length; i++) {
                kindRadios[i].addEventListener('change', applyVisibility);
            }
            applyVisibility();
        })();
        </script>
        <?php
    }

    
    public static function save_metabox_legacy($post_id, $post) {
        if (!isset($_POST['aieo_dmm_cancel_nonce']) || !wp_verify_nonce(
            sanitize_key(wp_unslash($_POST['aieo_dmm_cancel_nonce'])),
            'aieo_dmm_cancel_meta_' . (int) $post_id
        )) { return; }
        $order = wc_get_order((int) $post_id);
        if (!$order) { return; }
        self::persist_metabox_values($order);
        
        
        if (in_array($order->get_status(), self::$cancel_statuses, true)) {
            self::on_order_status_changed($order->get_id(), $order->get_status(), $order->get_status(), $order);
        }
    }

    
    public static function save_metabox_hpos($order_id) {
        if (!isset($_POST['aieo_dmm_cancel_nonce']) || !wp_verify_nonce(
            sanitize_key(wp_unslash($_POST['aieo_dmm_cancel_nonce'])),
            'aieo_dmm_cancel_meta_' . (int) $order_id
        )) { return; }
        $order = wc_get_order((int) $order_id);
        if (!$order) { return; }
        self::persist_metabox_values($order);
        if (in_array($order->get_status(), self::$cancel_statuses, true)) {
            self::on_order_status_changed($order->get_id(), $order->get_status(), $order->get_status(), $order);
        }
    }

    private static function persist_metabox_values($order) {
        $slug = isset($_POST['aieo_dmm_cancel_reason']) ? sanitize_key(wp_unslash($_POST['aieo_dmm_cancel_reason'])) : '';
        $note = isset($_POST['aieo_dmm_cancel_note'])   ? sanitize_textarea_field(wp_unslash($_POST['aieo_dmm_cancel_note'])) : '';
        if ($slug === '') {
            $order->delete_meta_data(self::META_REASON_SLUG);
        } else {
            $order->update_meta_data(self::META_REASON_SLUG, $slug);
        }
        if ($note === '') {
            $order->delete_meta_data(self::META_REASON_NOTE);
        } else {
            $order->update_meta_data(self::META_REASON_NOTE, substr($note, 0, 500));
        }

        
        if (isset($_POST['aieo_dmm_cancel_kind'])) {
            $kind = (int) $_POST['aieo_dmm_cancel_kind'];
            if (in_array($kind, array(0, 1, 2), true)) {
                $order->update_meta_data(self::META_EVENT_KIND, (string) $kind);
            } else {
                $order->delete_meta_data(self::META_EVENT_KIND);
            }
        }

        
        
        
        $returned = isset($_POST['aieo_dmm_cancel_returned']) && is_array($_POST['aieo_dmm_cancel_returned'])
            ? array_filter(array_map('intval', wp_unslash($_POST['aieo_dmm_cancel_returned'])))
            : array();
        if (empty($returned)) {
            $order->delete_meta_data(self::META_RETURNED_PRODUCTS);
        } else {
            $order->update_meta_data(self::META_RETURNED_PRODUCTS, implode(',', array_unique($returned)));
        }

        
        if (isset($_POST['aieo_dmm_cancel_return'])) {
            $val = trim((string) wp_unslash($_POST['aieo_dmm_cancel_return']));
            if ($val === '' || !is_numeric($val)) {
                $order->delete_meta_data(self::META_RETURN_SHIP_COST);
            } else {
                $order->update_meta_data(self::META_RETURN_SHIP_COST, (string) round((float) $val, 2));
            }
        }
        if (isset($_POST['aieo_dmm_cancel_outbound'])) {
            $val = trim((string) wp_unslash($_POST['aieo_dmm_cancel_outbound']));
            if ($val === '' || !is_numeric($val)) {
                $order->delete_meta_data(self::META_OUTBOUND_SHIP_COST);
            } else {
                $order->update_meta_data(self::META_OUTBOUND_SHIP_COST, (string) round((float) $val, 2));
            }
        }

        $order->save_meta_data();
    }

    
    public static function cron_daily_recompute() {
        
        
        if (class_exists('AIEO_DMM_Module_Base') && !AIEO_DMM_Module_Base::is_aieo_pro_user()) {
            return;
        }
        $cap = (int) apply_filters('aieo_dmm_metrics_daily_cap', 200);
        if (class_exists('AIEO_DMM_Product_Behavior_Metrics')) {
            AIEO_DMM_Product_Behavior_Metrics::recompute_stale(24, $cap);
        }
        if (class_exists('AIEO_DMM_User_Behavior_Metrics')) {
            AIEO_DMM_User_Behavior_Metrics::recompute_stale(24, $cap);
        }
    }

    
    public static function resolve_event_kind($order, $status_after, $reason_slug) {
        
        $override = (string) $order->get_meta(self::META_EVENT_KIND, true);
        if (in_array($override, array('0', '1', '2', '3'), true)) {
            return (int) $override;
        }

        
        if ($reason_slug !== '' && class_exists('AIEO_DMM_Cancellation_Reasons_Data')) {
            $row = AIEO_DMM_Cancellation_Reasons_Data::get($reason_slug);
            if (is_array($row) && isset($row['default_event_kind']) && $row['default_event_kind'] !== null && $row['default_event_kind'] !== '') {
                return (int) $row['default_event_kind'];
            }
        }

        
        return ((string) $status_after === 'refunded')
            ? self::KIND_RETURNED
            : self::KIND_CANCELLATION;
    }

    
    public static function compute_outbound_cost_for_order($order) {
        if (!is_object($order)) { return 0.0; }
        if (!class_exists('AIEO_DMM_Shipping_Cost_Resolver')) {
            
            
            
            return round((float) $order->get_shipping_total(), 2);
        }
        $r = AIEO_DMM_Shipping_Cost_Resolver::instance()->resolve_for_order((int) $order->get_id());
        if (is_array($r) && isset($r['cost'])) {
            return round((float) $r['cost'], 2);
        }
        return 0.0;
    }

    
    public static function backfill_outbound_costs_batch($batch_size = 100) {
        global $wpdb;
        $tbl = self::table_orders();
        $t0  = microtime(true);

        
        
        
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, order_id FROM {$tbl}
              WHERE event_kind > 0
                AND (outbound_shipping_cost IS NULL OR outbound_shipping_cost = 0)
              ORDER BY id ASC
              LIMIT %d",
            (int) $batch_size
        ), ARRAY_A);
        if (empty($rows)) {
            $remaining = (int) $wpdb->get_var(
                "SELECT COUNT(*) FROM {$tbl}
                  WHERE event_kind > 0
                    AND (outbound_shipping_cost IS NULL OR outbound_shipping_cost = 0)"
            );
            return array('processed' => 0, 'remaining' => $remaining, 'elapsed_ms' => 0);
        }
        $processed = 0;
        foreach ($rows as $r) {
            $order = function_exists('wc_get_order') ? wc_get_order((int) $r['order_id']) : null;
            if (!$order) {
                
                $wpdb->update($tbl, array('outbound_shipping_cost' => 0.01), array('id' => (int) $r['id']));
                $processed++;
                continue;
            }
            $cost = self::compute_outbound_cost_for_order($order);
            if ($cost <= 0) { $cost = 0.01; } 
            $wpdb->update($tbl, array('outbound_shipping_cost' => $cost), array('id' => (int) $r['id']));
            $processed++;
        }

        $remaining = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$tbl}
              WHERE event_kind > 0
                AND (outbound_shipping_cost IS NULL OR outbound_shipping_cost = 0)"
        );
        return array(
            'processed'  => $processed,
            'remaining'  => $remaining,
            'elapsed_ms' => (int) round((microtime(true) - $t0) * 1000),
        );
    }

    
    private static function resolve_campaign_id_for_order($order) {
        $slug = (string) $order->get_meta('_aieo_entry_campaign', true);
        if ($slug === '') { return null; }
        if (!class_exists('AIEO_DMM_Campaigns')) { return null; }
        $id = AIEO_DMM_Campaigns::touch($slug);
        return $id ? (int) $id : null;
    }

    
    private static function resolve_branch_id_for_order($order) {
        $soft1 = (string) $order->get_meta('soft1_branches', true);
        if ($soft1 === '') { return null; }
        if (strpos($soft1, ',') !== false) {
            $parts = array_filter(array_map('trim', explode(',', $soft1)));
            $soft1 = (string) reset($parts);
        }
        $soft1 = (int) $soft1;
        if ($soft1 <= 0) { return null; }

        global $wpdb;
        $tbl = $wpdb->prefix . 'aieo_dmm_branches';
        if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $tbl)) !== $tbl) { return null; }
        $branch_id = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM {$tbl} WHERE soft1_id = %d LIMIT 1",
            $soft1
        ));
        return $branch_id ? (int) $branch_id : null;
    }
}
