<?php

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

class AIEO_DMM_Shipping_Rates extends AIEO_DMM_Module_Base {

    public function get_id()       { return 'shipping_rates'; }
    public function get_label()    { return __('Table Rate Shipping', 'ai-eshop-optimizer'); }
    public function get_priority() { return 70; }
    public function get_group()    { return 'checkout_handling'; }

    public function __construct() {
        $this->load_services();
    }

    private function load_services() {
        $base = AIEO_DIR . 'includes/shipping-rates/';
        foreach (array(
            'class-aieo-trs-rules.php',
            'class-aieo-trs-validator.php',
            'class-aieo-trs-csv.php',
            'class-aieo-trs-zone-admin.php',
            'class-aieo-trs-migrator.php',
        ) as $f) {
            $path = $base . $f;
            if (file_exists($path)) { require_once $path; }
        }
        if (class_exists('AIEO_TRS_Zone_Admin')) { AIEO_TRS_Zone_Admin::register(); }
        if (class_exists('AIEO_TRS_Migrator'))   { AIEO_TRS_Migrator::register();   }
    }

    public function register_hooks() {
        
        
    }

    public function render_subtab() {
        $section  = isset($_GET['trs_section']) ? sanitize_key((string) $_GET['trs_section']) : 'rules';
        if (!in_array($section, array('rules', 'csv', 'migrator'), true)) { $section = 'rules'; }
        $base_url = admin_url('admin.php?page=' . AIEO_DMM::MENU_SLUG . '&dmm_tab=shipping_rates');

        echo '<div class="aieo-dmm-section">';
        echo '<h2>' . esc_html__('Table Rate Shipping', 'ai-eshop-optimizer') . '</h2>';
        echo '<p class="description">' . esc_html__('Configure per-zone shipping rules. Replaces the SkyVerge `woocommerce-table-rate-shipping` plugin with an in-memory matcher (object-cached, ~0 DB queries per cart re-render).', 'ai-eshop-optimizer') . '</p>';

        $tabs = array(
            'rules' => __('Rules', 'ai-eshop-optimizer'),
            'csv'   => __('Import / Export', 'ai-eshop-optimizer'),
        );
        if (class_exists('AIEO_TRS_Migrator') && AIEO_TRS_Migrator::has_legacy_table()) {
            $tabs['migrator'] = __('Migrate from legacy', 'ai-eshop-optimizer');
        }

        echo '<div class="nav-tab-wrapper" style="margin-bottom:14px;">';
        foreach ($tabs as $slug => $label) {
            $active = $section === $slug ? ' nav-tab-active' : '';
            echo '<a href="' . esc_url($base_url . '&trs_section=' . $slug) . '" class="nav-tab' . $active . '">' . esc_html($label) . '</a>'; 
        }
        echo '</div>';

        switch ($section) {
            case 'rules':    $this->render_rules();    break;
            case 'csv':      $this->render_csv();      break;
            case 'migrator': $this->render_migrator(); break;
        }

        echo '</div>';
    }

    
    private function discover_instances() {
        $out = array();
        if (!class_exists('WC_Shipping_Zones')) { return $out; }

        $zones = WC_Shipping_Zones::get_zones();
        
        $rest = WC_Shipping_Zones::get_zone(0);
        if ($rest) {
            $zones[0] = array(
                'zone_name'        => __('Rest of the World', 'ai-eshop-optimizer'),
                'shipping_methods' => $rest->get_shipping_methods(false),
            );
        }

        foreach ($zones as $zone_id => $zone) {
            $methods = isset($zone['shipping_methods']) ? $zone['shipping_methods'] : array();
            foreach ($methods as $m) {
                $mid = method_exists($m, 'get_id') ? $m->id : (string) ($m->id ?? '');
                $iid = (int) (method_exists($m, 'get_instance_id') ? $m->get_instance_id() : ($m->instance_id ?? 0));
                if ($iid <= 0) { continue; }
                $title = method_exists($m, 'get_title') ? (string) $m->get_title() : (string) ($m->title ?? '');
                if ($title === '') { $title = (string) ($m->method_title ?? __('Shipping', 'ai-eshop-optimizer')); }
                $is_table_rate = ($mid === 'aieo_table_rate' || $mid === 'table_rate');
                $is_enabled    = method_exists($m, 'is_enabled') ? (bool) $m->is_enabled() : true;
                $out[] = array(
                    'instance_id'  => $iid,
                    'zone_id'      => (int) $zone_id,
                    'zone_name'    => (string) ($zone['zone_name'] ?? __('Zone', 'ai-eshop-optimizer')),
                    'method_id'    => $mid,
                    'method_title' => $title,
                    'method_label' => method_exists($m, 'get_method_title') ? (string) $m->get_method_title() : $mid,
                    'is_aieo'      => $mid === 'aieo_table_rate',
                    'is_table_rate'=> $is_table_rate,
                    'is_enabled'   => $is_enabled,
                    'count'        => ($is_table_rate && class_exists('AIEO_TRS_Rules'))
                        ? (int) AIEO_TRS_Rules::count_for_instance($iid)
                        : 0,
                );
            }
        }
        return $out;
    }

    
    private function find_instance($instance_id) {
        $instance_id = (int) $instance_id;
        foreach ($this->discover_instances() as $row) {
            if ((int) $row['instance_id'] === $instance_id) { return $row; }
        }
        return null;
    }

    private function render_rules() {
        $nonce       = wp_create_nonce('aieo_trs_admin');
        $instance_id = isset($_GET['instance_id']) ? (int) $_GET['instance_id'] : 0;
        $instances   = $this->discover_instances();
        $base_url    = admin_url('admin.php?page=' . AIEO_DMM::MENU_SLUG . '&dmm_tab=shipping_rates&trs_section=rules');

        
        $by_zone = array();
        foreach ($instances as $row) {
            $by_zone[$row['zone_id']]['name'] = $row['zone_name'];
            $by_zone[$row['zone_id']]['rows'][] = $row;
        }

        echo '<div class="aieo-dmm-card"><h3>' . esc_html__('Carriers', 'ai-eshop-optimizer') . '</h3>';
        if (empty($by_zone)) {
            echo '<p>' . esc_html__('No table-rate shipping methods found in any zone yet. Add an "AIEO Table Rate" method to a WooCommerce shipping zone, then return here to configure rules.', 'ai-eshop-optimizer') . '</p>';
            echo '<p><a class="button" href="' . esc_url(admin_url('admin.php?page=wc-settings&tab=shipping')) . '" target="_blank">' . esc_html__('Open WC → Shipping zones', 'ai-eshop-optimizer') . '</a></p>';
        } else {
            echo '<style>
                .aieo-trs-zone-block { margin-bottom: 18px; }
                .aieo-trs-zone-title { font-size: 13px; font-weight: 600; color: #1f2937; padding: 6px 0; border-bottom: 1px solid #e5e7eb; margin-bottom: 6px; }
                .aieo-trs-carrier-row { display: flex; align-items: center; gap: 12px; padding: 8px 10px; border-radius: 6px; background: #f9fafb; margin-bottom: 4px; }
                .aieo-trs-carrier-row.is-active { background: #eef2ff; outline: 1px solid #c7d2fe; }
                .aieo-trs-carrier-name { flex: 1; font-weight: 500; }
                .aieo-trs-badge { font-size: 11px; padding: 2px 8px; border-radius: 999px; font-weight: 600; }
                .aieo-trs-badge.legacy { background: #fee2e2; color: #b91c1c; }
                .aieo-trs-badge.aieo   { background: #dcfce7; color: #15803d; }
                .aieo-trs-rule-count { font-size: 12px; color: #6b7280; min-width: 64px; text-align: right; }
                .aieo-trs-pretty-row td { vertical-align: middle; }
                .aieo-trs-pretty-row .col-cond { color: #4b5563; font-size: 13px; }
                .aieo-trs-pretty-row .col-cost { font-variant-numeric: tabular-nums; }
                .aieo-trs-pretty-row .label-empty { color: #9ca3af; font-style: italic; }
            </style>';
            
            
            
            
            
            

            foreach ($by_zone as $zid => $zone) {
                $zone_edit_url = admin_url('admin.php?page=wc-settings&tab=shipping&zone_id=' . (int) $zid);
                echo '<div class="aieo-trs-zone-block">';
                echo '<div class="aieo-trs-zone-title" style="display:flex;align-items:center;gap:10px;">';
                echo '<span style="flex:1;">' . esc_html($zone['name']) . '</span>';
                echo '<a href="' . esc_url($zone_edit_url) . '" target="_blank" rel="noopener" style="font-size:11px;font-weight:500;color:#3b82f6;text-decoration:none;">↗ ' . esc_html__('Edit zone regions / postcodes', 'ai-eshop-optimizer') . '</a>';
                echo '</div>';
                foreach ($zone['rows'] as $row) {
                    $is_active = $row['instance_id'] === $instance_id;
                    echo '<div class="aieo-trs-carrier-row' . ($is_active ? ' is-active' : '') . '">';
                    echo '<span class="aieo-trs-carrier-name"' . (!$row['is_enabled'] ? ' style="color:#9ca3af;"' : '') . '>' . esc_html($row['method_title']);
                    if (!$row['is_enabled']) {
                        echo ' <span title="' . esc_attr__('Disabled in WooCommerce shipping zone settings — flipping the calculator is still allowed.', 'ai-eshop-optimizer') . '" style="display:inline-block;margin-left:6px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;padding:2px 7px;border-radius:999px;background:#fee2e2;color:#b91c1c;vertical-align:middle;">' . esc_html__('off', 'ai-eshop-optimizer') . '</span>';
                    }
                    echo '</span>';

                    if ($row['is_table_rate']) {
                        
                        $badge_class = $row['is_aieo'] ? 'aieo' : 'legacy';
                        $badge_text  = $row['is_aieo'] ? __('AIEO', 'ai-eshop-optimizer') : __('Legacy', 'ai-eshop-optimizer');
                        echo '<span class="aieo-trs-badge ' . esc_attr($badge_class) . '">' . esc_html($badge_text) . '</span>';
                        echo '<span class="aieo-trs-rule-count">' . sprintf(
                            
                            esc_html(_n('%d rule', '%d rules', (int) $row['count'], 'ai-eshop-optimizer')),
                            (int) $row['count']
                        ) . '</span>';
                        echo '<button type="button" class="button button-primary aieo-trs-open-rules" '
                            . 'data-instance="' . (int) $row['instance_id'] . '" '
                            . 'data-method-title="' . esc_attr($row['method_title']) . '" '
                            . 'data-zone-name="' . esc_attr($row['zone_name']) . '" '
                            . 'data-is-aieo="' . ($row['is_aieo'] ? '1' : '0') . '">'
                            . esc_html__('Rules', 'ai-eshop-optimizer') . '</button>';

                        if ($row['is_aieo']) {
                            $flip_label = __('Switch to legacy', 'ai-eshop-optimizer');
                            $flip_to    = 'table_rate';
                            $flip_color = '#6b7280';
                        } else {
                            $flip_label = __('Activate AIEO', 'ai-eshop-optimizer');
                            $flip_to    = 'aieo_table_rate';
                            $flip_color = '#15803d';
                        }
                        echo '<button type="button" class="button aieo-trs-flip" data-instance="' . (int) $row['instance_id'] . '" data-to="' . esc_attr($flip_to) . '" data-nonce="' . esc_attr($nonce) . '" style="color:' . esc_attr($flip_color) . ';font-weight:600;">' . esc_html($flip_label) . '</button>';
                    } else {
                        
                        echo '<span class="aieo-trs-badge" style="background:#e5e7eb;color:#4b5563;">' . esc_html($row['method_label']) . '</span>';
                        echo '<span class="aieo-trs-rule-count">' . esc_html__('configured in WC', 'ai-eshop-optimizer') . '</span>';
                        $wc_method_url = admin_url('admin.php?page=wc-settings&tab=shipping&zone_id=' . (int) $zid . '&instance_id=' . (int) $row['instance_id']);
                        echo '<a class="button" href="' . esc_url($wc_method_url) . '" target="_blank" rel="noopener">' . esc_html__('Open in WC', 'ai-eshop-optimizer') . '</a>';
                    }
                    echo '</div>';
                }
                
                $add_method_url = admin_url('admin.php?page=wc-settings&tab=shipping&zone_id=' . (int) $zid);
                echo '<div style="margin-top:6px;font-size:12px;"><a href="' . esc_url($add_method_url) . '" target="_blank" rel="noopener" style="color:#3b82f6;text-decoration:none;">+ ' . esc_html__('Add another carrier or free-shipping rule to this zone', 'ai-eshop-optimizer') . '</a></div>';
                echo '</div>';
            }

            
            $ajax = admin_url('admin-ajax.php');
            ?>
            <script>
            (function(){
                document.querySelectorAll('.aieo-trs-flip').forEach(function(btn){
                    btn.addEventListener('click', function(){
                        if (!confirm('<?php echo esc_js(__('Switch this carrier\'s shipping engine? Customers will see no change in titles or behavior; only the calculator changes. Reversible.', 'ai-eshop-optimizer')); ?>')) return;
                        btn.disabled = true; btn.textContent = '...';
                        var fd = new FormData();
                        fd.append('action', 'aieo_trs_flip_method');
                        fd.append('nonce', btn.dataset.nonce);
                        fd.append('instance_id', btn.dataset.instance);
                        fd.append('target', btn.dataset.to);
                        fetch(<?php echo wp_json_encode($ajax); ?>, {method:'POST',credentials:'same-origin',body:fd})
                            .then(function(r){return r.json();})
                            .then(function(j){
                                if (!j || !j.success) { alert((j && j.data && j.data.message) || 'Flip failed.'); btn.disabled = false; return; }
                                location.reload();
                            });
                    });
                });

            })();
            </script>
            <?php
        }
        echo '</div>';

        
        ?>
        <div id="aieo-trs-modal" data-nonce="<?php echo esc_attr($nonce); ?>" style="display:none;position:fixed;inset:0;background:rgba(15,23,42,0.55);z-index:99990;align-items:flex-start;justify-content:center;padding:40px 20px;overflow-y:auto;">
            <div id="aieo-trs-modal-card" style="background:#fff;border-radius:10px;width:100%;max-width:1100px;max-height:90vh;display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,0.3);">
                <div style="display:flex;align-items:center;gap:12px;padding:14px 20px;border-bottom:1px solid #e5e7eb;">
                    <div style="flex:1;">
                        <h3 id="aieo-trs-modal-title" style="margin:0;font-size:16px;line-height:1.3;"></h3>
                        <div id="aieo-trs-modal-sub" style="font-size:12px;color:#6b7280;margin-top:2px;"></div>
                    </div>
                    <button type="button" id="aieo-trs-modal-close" class="button" aria-label="<?php echo esc_attr__('Close', 'ai-eshop-optimizer'); ?>" style="font-size:16px;line-height:1;padding:4px 10px;">×</button>
                </div>
                <div id="aieo-trs-modal-banner"></div>
                <div id="aieo-trs-grid" style="padding:14px 20px;overflow-y:auto;"></div>
            </div>
        </div>
        <?php
        $this->print_grid_js(0, $nonce);
        $this->print_modal_js($nonce);
    }

    
    private function print_modal_js($nonce) {
        $legacy_warning = __('This carrier is still on the legacy calculator. Edits here update the AIEO rules table, but they only take effect once you click "Activate AIEO" above.', 'ai-eshop-optimizer');
        ?>
        <script>
        (function(){
            var modal = document.getElementById('aieo-trs-modal');
            var titleEl = document.getElementById('aieo-trs-modal-title');
            var subEl   = document.getElementById('aieo-trs-modal-sub');
            var bannerEl = document.getElementById('aieo-trs-modal-banner');
            var closeBtn = document.getElementById('aieo-trs-modal-close');
            if (!modal) return;
            function open(opts) {
                titleEl.textContent = opts.title || '';
                subEl.textContent   = opts.subtitle || '';
                if (opts.is_aieo) {
                    bannerEl.innerHTML = '';
                } else {
                    bannerEl.innerHTML = '<div style="margin:12px 20px 0;background:#fef3c7;padding:10px 12px;border-radius:6px;border-left:3px solid #f59e0b;font-size:13px;color:#92400e;">'
                        + <?php echo wp_json_encode(esc_html($legacy_warning)); ?>
                        + '</div>';
                }
                modal.style.display = 'flex';
                document.body.style.overflow = 'hidden';
                if (window.aieoTrsLoadGrid) { window.aieoTrsLoadGrid(opts.instance_id); }
            }
            function close() {
                modal.style.display = 'none';
                document.body.style.overflow = '';
            }
            document.querySelectorAll('.aieo-trs-open-rules').forEach(function(btn){
                btn.addEventListener('click', function(){
                    open({
                        instance_id:  parseInt(btn.dataset.instance, 10),
                        title:        '<?php echo esc_js(__('Rules', 'ai-eshop-optimizer')); ?>: ' + (btn.dataset.methodTitle || ''),
                        subtitle:     btn.dataset.zoneName || '',
                        is_aieo:      btn.dataset.isAieo === '1',
                    });
                });
            });
            closeBtn.addEventListener('click', close);
            modal.addEventListener('click', function(e){ if (e.target === modal) close(); });
            document.addEventListener('keydown', function(e){
                if (e.key === 'Escape' && modal.style.display !== 'none') close();
            });
        })();
        </script>
        <?php
    }

    private function print_grid_js($instance_id, $nonce) {
        $ajax = admin_url('admin-ajax.php');
        
        
        
        $currency_symbol = function_exists('get_woocommerce_currency_symbol') ? get_woocommerce_currency_symbol() : '€';
        $currency_symbol = html_entity_decode((string) $currency_symbol, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        $weight_unit     = function_exists('get_option') ? (string) get_option('woocommerce_weight_unit', 'kg') : 'kg';
        $strings = array(
            'add'        => __('Add rule', 'ai-eshop-optimizer'),
            'save'       => __('Save', 'ai-eshop-optimizer'),
            'cancel'     => __('Cancel', 'ai-eshop-optimizer'),
            'delete'     => __('Delete', 'ai-eshop-optimizer'),
            'edit'       => __('Edit', 'ai-eshop-optimizer'),
            'duplicate'  => __('Duplicate', 'ai-eshop-optimizer'),
            'class'      => __('Class', 'ai-eshop-optimizer'),
            'class_any'  => __('Any class', 'ai-eshop-optimizer'),
            'class_none' => __('No class', 'ai-eshop-optimizer'),
            'condition'  => __('Condition', 'ai-eshop-optimizer'),
            'rule'       => __('Rule', 'ai-eshop-optimizer'),
            'cost_col'   => __('Base cost', 'ai-eshop-optimizer'),
            'extras_col' => __('Extras', 'ai-eshop-optimizer'),
            'min'        => __('Min', 'ai-eshop-optimizer'),
            'max'        => __('Max', 'ai-eshop-optimizer'),
            'cost'       => __('Cost', 'ai-eshop-optimizer'),
            'per_item'   => __('+ per item', 'ai-eshop-optimizer'),
            
            'per_weight' => sprintf(__('+ per %s', 'ai-eshop-optimizer'), $weight_unit),
            'percent'    => __('+ % of subtotal', 'ai-eshop-optimizer'),
            'label'      => __('Label', 'ai-eshop-optimizer'),
            'no_label'   => __('(uses method title)', 'ai-eshop-optimizer'),
            'priority'   => __('Stop after match', 'ai-eshop-optimizer'),
            'abort'      => __('Abort method', 'ai-eshop-optimizer'),
            'abort_reason' => __('Reason shown to customer', 'ai-eshop-optimizer'),
            'enabled'    => __('Enabled', 'ai-eshop-optimizer'),
            'disabled'   => __('Disabled', 'ai-eshop-optimizer'),
            'no_rules'   => __('No rules yet. Click "Add rule" to create your first one.', 'ai-eshop-optimizer'),
            'confirm_delete' => __('Delete this rule?', 'ai-eshop-optimizer'),
            'cond_price' => __('Subtotal', 'ai-eshop-optimizer'),
            'cond_weight'=> __('Weight', 'ai-eshop-optimizer'),
            'cond_items' => __('Items', 'ai-eshop-optimizer'),
            'cond_items_in_class' => __('Items in class', 'ai-eshop-optimizer'),
            'cond_none'  => __('Always matches', 'ai-eshop-optimizer'),
            'currency'   => $currency_symbol,
            'weight_unit'=> $weight_unit,
        );
        ?>
        <script>
        (function(){
            var grid = document.getElementById('aieo-trs-grid');
            if (!grid) return;
            var INSTANCE = 0; // set per-open by aieoTrsLoadGrid()
            var NONCE = <?php echo wp_json_encode($nonce); ?>;
            var AJAX = <?php echo wp_json_encode($ajax); ?>;
            var T = <?php echo wp_json_encode($strings); ?>;
            var CLASSES = [];
            var RULES = [];

            function post(action, params) {
                var fd = new FormData();
                fd.append('action', action);
                fd.append('nonce', NONCE);
                Object.keys(params || {}).forEach(function(k){
                    var v = params[k];
                    if (Array.isArray(v)) v.forEach(function(item){ fd.append(k+'[]', item); });
                    else if (v && typeof v === 'object') {
                        Object.keys(v).forEach(function(kk){ fd.append(k+'['+kk+']', v[kk]); });
                    }
                    else if (v !== undefined && v !== null) fd.append(k, v);
                });
                return fetch(AJAX, { method: 'POST', credentials: 'same-origin', body: fd }).then(function(r){ return r.json(); });
            }
            function load() {
                if (!INSTANCE) return Promise.resolve();
                grid.innerHTML = '<p style="color:#9ca3af;">' + escHtml(T.add ? '...' : 'Loading…') + '</p>';
                return post('aieo_trs_list', {instance_id: INSTANCE}).then(function(j){
                    if (!j || !j.success) return;
                    RULES = (j.data && j.data.rules) || [];
                    CLASSES = (j.data && j.data.classes) || [];
                    render();
                });
            }
            // Expose so the modal opener can switch the active instance
            // and trigger a reload without re-printing the grid JS.
            window.aieoTrsLoadGrid = function(instance_id) {
                INSTANCE = parseInt(instance_id, 10) || 0;
                load();
            };
            function escHtml(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, function(c){return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[c];}); }

            function num(n) {
                if (n == null || n === '') return '';
                var f = parseFloat(n); if (!isFinite(f)) return '';
                // Strip trailing zeros: 3.5000 → 3.5, 4.0000 → 4
                return parseFloat(f.toFixed(4)).toString();
            }
            function money(n) {
                var s = num(n); if (s === '') return '';
                return T.currency + ' ' + s;
            }
            function classLabel(slug) {
                if (slug === '' || slug == null) return T.class_any;
                if (slug === '0') return T.class_none;
                var found = CLASSES.filter(function(c){ return c.slug === slug; })[0];
                return found ? found.name : slug;
            }
            function condLabel(cond) {
                switch (cond) {
                    case 'price':          return T.cond_price;
                    case 'weight':         return T.cond_weight;
                    case 'items':          return T.cond_items;
                    case 'items_in_class': return T.cond_items_in_class;
                    case '': case null: case undefined: return T.cond_none;
                    default: return cond;
                }
            }
            function unitFor(cond) {
                if (cond === 'price')          return T.currency;
                if (cond === 'weight')         return T.weight_unit;
                if (cond === 'items' || cond === 'items_in_class') return '';
                return '';
            }
            function describeRange(r) {
                var unit = unitFor(r.rate_condition);
                var lo = (r.rate_min == null || r.rate_min === '') ? null : num(r.rate_min);
                var hi = (r.rate_max == null || r.rate_max === '') ? null : num(r.rate_max);
                var label = condLabel(r.rate_condition);
                if (lo == null && hi == null) return label;
                if (lo != null && hi != null) return label + ': ' + lo + '–' + hi + (unit ? ' ' + unit : '');
                if (lo != null) return label + ' ≥ ' + lo + (unit ? ' ' + unit : '');
                return label + ' ≤ ' + hi + (unit ? ' ' + unit : '');
            }
            function describeExtras(r) {
                var bits = [];
                var pi = num(r.rate_cost_per_item);
                var pw = num(r.rate_cost_per_weight_unit);
                var pc = num(r.rate_cost_percent);
                if (pi && parseFloat(pi) !== 0) bits.push('+' + T.currency + pi + '/' + (T.cond_items.toLowerCase().split(' ')[0]));
                if (pw && parseFloat(pw) !== 0) bits.push('+' + T.currency + pw + '/' + T.weight_unit);
                if (pc && parseFloat(pc) !== 0) bits.push('+' + pc + '% ' + T.cond_price.toLowerCase());
                return bits.length ? bits.join(' ') : '—';
            }

            function render() {
                var html = '';
                html += '<p><button type="button" class="button button-primary" id="trs-add">+ ' + escHtml(T.add) + '</button></p>';
                if (!RULES.length) { html += '<p>' + escHtml(T.no_rules) + '</p>'; grid.innerHTML = html; bind(); return; }
                html += '<table class="wp-list-table widefat striped"><thead><tr>';
                html += '<th style="width:24px;"></th>';
                html += '<th style="width:32px;">#</th>';
                html += '<th>' + escHtml(T.class) + '</th>';
                html += '<th>' + escHtml(T.rule) + '</th>';
                html += '<th style="text-align:right;">' + escHtml(T.cost_col) + '</th>';
                html += '<th>' + escHtml(T.extras_col) + '</th>';
                html += '<th>' + escHtml(T.label) + '</th>';
                html += '<th style="width:60px;text-align:center;">' + escHtml(T.enabled) + '</th>';
                html += '<th style="width:170px;"></th>';
                html += '</tr></thead><tbody id="trs-tbody">';
                RULES.forEach(function(r, idx){
                    var enabled = parseInt(r.enabled,10) === 1;
                    var disabledStyle = enabled ? '' : 'style="opacity:0.5;"';
                    html += '<tr data-id="'+(r.id|0)+'" class="aieo-trs-pretty-row" '+disabledStyle+'>';
                    html += '<td style="cursor:grab;color:#9ca3af;font-size:18px;line-height:1;">≡</td>';
                    html += '<td style="color:#6b7280;">' + (idx + 1) + '</td>';
                    html += '<td>' + escHtml(classLabel(r.rate_class)) + '</td>';
                    html += '<td class="col-cond">' + escHtml(describeRange(r)) + (parseInt(r.rate_priority,10)===1 ? ' <span title="Stop after match" style="color:#f59e0b;font-weight:600;">⛔</span>' : '') + (parseInt(r.rate_abort,10)===1 ? ' <span title="Aborts the method" style="color:#dc2626;font-weight:600;">✗</span>' : '') + '</td>';
                    html += '<td class="col-cost" style="text-align:right;font-weight:600;">' + escHtml(money(r.rate_cost)) + '</td>';
                    html += '<td class="col-cond">' + escHtml(describeExtras(r)) + '</td>';
                    html += '<td>' + (r.rate_label ? escHtml(r.rate_label) : '<span class="label-empty">' + escHtml(T.no_label) + '</span>') + '</td>';
                    html += '<td style="text-align:center;font-size:16px;">' + (enabled ? '<span style="color:#15803d;">✓</span>' : '<span style="color:#9ca3af;">○</span>') + '</td>';
                    html += '<td style="white-space:nowrap;">'
                          + '<button type="button" class="button button-small trs-edit">' + escHtml(T.edit) + '</button> '
                          + '<button type="button" class="button button-small trs-duplicate" title="' + escHtml(T.duplicate) + '">⎘</button> '
                          + '<button type="button" class="button-link trs-delete" style="color:#b91c1c;margin-left:4px;">' + escHtml(T.delete) + '</button>'
                          + '</td>';
                    html += '</tr>';
                });
                html += '</tbody></table>';
                grid.innerHTML = html;
                bind();
            }

            function bind() {
                var addBtn = document.getElementById('trs-add');
                if (addBtn) addBtn.addEventListener('click', function(){ openEditor(null); });
                grid.querySelectorAll('.trs-edit').forEach(function(b){
                    b.addEventListener('click', function(){
                        var tr = b.closest('tr');
                        var id = parseInt(tr.dataset.id, 10);
                        var rule = RULES.filter(function(r){ return (r.id|0) === id; })[0];
                        openEditor(rule);
                    });
                });
                grid.querySelectorAll('.trs-delete').forEach(function(b){
                    b.addEventListener('click', function(){
                        if (!confirm(T.confirm_delete)) return;
                        var tr = b.closest('tr');
                        post('aieo_trs_delete', {rule_id: parseInt(tr.dataset.id, 10)}).then(load);
                    });
                });
                grid.querySelectorAll('.trs-duplicate').forEach(function(b){
                    b.addEventListener('click', function(){
                        var tr = b.closest('tr');
                        var id = parseInt(tr.dataset.id, 10);
                        var src = RULES.filter(function(r){ return (r.id|0) === id; })[0];
                        if (!src) return;
                        // Open the editor pre-filled but with id=0 (treated as create).
                        var copy = Object.assign({}, src);
                        delete copy.id;
                        copy.rate_label = (src.rate_label || '') + ' (copy)';
                        openEditor(copy);
                    });
                });
                // Drag-to-reorder via HTML5 native drag (jQuery-UI free).
                var tbody = document.getElementById('trs-tbody');
                if (tbody) {
                    Array.from(tbody.querySelectorAll('tr')).forEach(function(tr){
                        tr.draggable = true;
                        tr.addEventListener('dragstart', function(e){ e.dataTransfer.setData('text/plain', tr.dataset.id); tr.style.opacity = '0.4'; });
                        tr.addEventListener('dragend',   function(){ tr.style.opacity = ''; });
                        tr.addEventListener('dragover',  function(e){ e.preventDefault(); });
                        tr.addEventListener('drop',      function(e){
                            e.preventDefault();
                            var fromId = e.dataTransfer.getData('text/plain');
                            var toId   = tr.dataset.id;
                            if (fromId === toId) return;
                            var fromRow = tbody.querySelector('tr[data-id="'+fromId+'"]');
                            if (!fromRow) return;
                            tbody.insertBefore(fromRow, tr);
                            var ids = Array.from(tbody.querySelectorAll('tr')).map(function(r){ return parseInt(r.dataset.id,10); });
                            post('aieo_trs_reorder', {ids: ids}).then(load);
                        });
                    });
                }
            }

            function openEditor(rule) {
                rule = rule || {};
                var html = '<div id="trs-editor" style="position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:99999;display:flex;align-items:center;justify-content:center;">'
                    + '<div style="background:#fff;border-radius:8px;padding:20px;max-width:640px;width:92%;max-height:90vh;overflow:auto;">'
                    + '<h3 style="margin-top:0;">'+ ((rule && rule.id) ? escHtml(T.edit) : escHtml(T.add)) +'</h3>'
                    + '<table class="form-table"><tbody>'
                    + row(T.class, classSelect(rule.rate_class || ''))
                    + row(T.condition, conditionSelect(rule.rate_condition || ''))
                    + row(T.min, '<input type="number" step="0.01" name="rate_min" value="'+escHtml(rule.rate_min == null ? '' : rule.rate_min)+'">')
                    + row(T.max, '<input type="number" step="0.01" name="rate_max" value="'+escHtml(rule.rate_max == null ? '' : rule.rate_max)+'">')
                    + row(T.cost, '<input type="number" step="0.0001" name="rate_cost" value="'+escHtml(rule.rate_cost || 0)+'">')
                    + row(T.per_item, '<input type="number" step="0.0001" name="rate_cost_per_item" value="'+escHtml(rule.rate_cost_per_item || 0)+'">')
                    + row(T.per_weight, '<input type="number" step="0.0001" name="rate_cost_per_weight_unit" value="'+escHtml(rule.rate_cost_per_weight_unit || 0)+'">')
                    + row(T.percent, '<input type="number" step="0.0001" name="rate_cost_percent" value="'+escHtml(rule.rate_cost_percent || 0)+'">')
                    + row(T.label, '<input type="text" name="rate_label" class="regular-text" value="'+escHtml(rule.rate_label || '')+'">')
                    + row(T.priority, '<label><input type="checkbox" name="rate_priority" value="1"'+ (parseInt(rule.rate_priority,10)===1?' checked':'') +'> '+escHtml(T.priority)+'</label>')
                    + row(T.abort, '<label><input type="checkbox" name="rate_abort" value="1"'+ (parseInt(rule.rate_abort,10)===1?' checked':'') +'> '+escHtml(T.abort)+'</label>')
                    + row(T.abort_reason, '<input type="text" name="rate_abort_reason" class="regular-text" value="'+escHtml(rule.rate_abort_reason || '')+'">')
                    + row(T.enabled, '<label><input type="checkbox" name="enabled" value="1"'+ ((rule.enabled === undefined || parseInt(rule.enabled,10)===1)?' checked':'') +'> '+escHtml(T.enabled)+'</label>')
                    + '</tbody></table>'
                    + '<p><button type="button" class="button button-primary" id="trs-save">'+escHtml(T.save)+'</button> '
                    + '<button type="button" class="button" id="trs-cancel">'+escHtml(T.cancel)+'</button>'
                    + '<span id="trs-editor-msg" style="margin-left:10px;color:#b91c1c;"></span></p>'
                    + '</div></div>';
                var holder = document.createElement('div');
                holder.innerHTML = html;
                document.body.appendChild(holder.firstChild);
                document.getElementById('trs-cancel').addEventListener('click', closeEditor);
                document.getElementById('trs-save').addEventListener('click', function(){ saveEditor(rule.id || 0); });
            }

            function row(label, html) { return '<tr><th>'+escHtml(label)+'</th><td>'+html+'</td></tr>'; }
            function classSelect(current) {
                return '<select name="rate_class">' + CLASSES.map(function(c){
                    var sel = (c.slug === current) ? ' selected' : '';
                    return '<option value="'+escHtml(c.slug)+'"'+sel+'>'+escHtml(c.name)+'</option>';
                }).join('') + '</select>';
            }
            function conditionSelect(current) {
                var opts = [['','—'],['price','price'],['weight','weight'],['items','items'],['items_in_class','items_in_class']];
                return '<select name="rate_condition">' + opts.map(function(o){
                    var sel = (o[0] === current) ? ' selected' : '';
                    return '<option value="'+escHtml(o[0])+'"'+sel+'>'+escHtml(o[1])+'</option>';
                }).join('') + '</select>';
            }

            function closeEditor() {
                var el = document.getElementById('trs-editor');
                if (el) el.remove();
            }

            function saveEditor(rule_id) {
                var el = document.getElementById('trs-editor');
                if (!el) return;
                var data = {shipping_method_id: INSTANCE};
                el.querySelectorAll('input, select').forEach(function(inp){
                    var n = inp.name; if (!n) return;
                    if (inp.type === 'checkbox') { data[n] = inp.checked ? '1' : '0'; }
                    else { data[n] = inp.value; }
                });
                post('aieo_trs_save', {rule_id: rule_id, rule: data}).then(function(j){
                    if (!j || !j.success) {
                        var msg = (j && j.data && j.data.errors) ? j.data.errors.join(' · ') : 'Save failed.';
                        var box = document.getElementById('trs-editor-msg');
                        if (box) box.textContent = msg;
                        return;
                    }
                    closeEditor();
                    load();
                });
            }
            // Initial load is triggered by the modal opener via
            // window.aieoTrsLoadGrid(instance_id). We do not auto-load.
        })();
        </script>
        <?php
    }

    private function render_csv() {
        $nonce = wp_create_nonce('aieo_trs_admin');
        $instances = $this->discover_instances();
        $ajax = admin_url('admin-ajax.php');

        echo '<div class="aieo-dmm-card"><h3>' . esc_html__('Export', 'ai-eshop-optimizer') . '</h3>';
        echo '<p>' . esc_html__('Download all current rules as a CSV. Pick a single zone-method to limit the export, or export everything.', 'ai-eshop-optimizer') . '</p>';
        echo '<form method="get" action="' . esc_url($ajax) . '" style="margin-bottom:8px;">';
        echo '<input type="hidden" name="action" value="aieo_trs_csv_export">';
        echo '<input type="hidden" name="nonce" value="' . esc_attr($nonce) . '">';
        echo '<select name="instance_id">';
        echo '<option value="">' . esc_html__('All zone-methods', 'ai-eshop-optimizer') . '</option>';
        foreach ($instances as $row) {
            echo '<option value="' . (int) $row['instance_id'] . '">' . esc_html($row['zone_name'] . ' — #' . $row['instance_id']) . '</option>';
        }
        echo '</select> <button type="submit" class="button button-primary">' . esc_html__('Download CSV', 'ai-eshop-optimizer') . '</button>';
        echo '</form></div>';

        echo '<div class="aieo-dmm-card" style="margin-top:14px;"><h3>' . esc_html__('Import', 'ai-eshop-optimizer') . '</h3>';
        echo '<p>' . esc_html__('Upload a CSV with the legacy plugin\'s column shape. Re-uploading the same file is safe — rows with the same (method_id, class, condition, min, max) tuple update in place.', 'ai-eshop-optimizer') . '</p>';
        echo '<form id="aieo-trs-csv-import" enctype="multipart/form-data">';
        echo '<input type="hidden" name="nonce" value="' . esc_attr($nonce) . '">';
        echo '<input type="file" name="file" accept=".csv,text/csv" required style="margin-right:10px;">';
        echo '<select name="instance_id">';
        echo '<option value="">' . esc_html__('Use shipping_method_id from CSV', 'ai-eshop-optimizer') . '</option>';
        foreach ($instances as $row) {
            echo '<option value="' . (int) $row['instance_id'] . '">' . esc_html__('Force into ', 'ai-eshop-optimizer') . esc_html($row['zone_name'] . ' — #' . $row['instance_id']) . '</option>';
        }
        echo '</select>';
        echo ' <button type="submit" class="button button-primary">' . esc_html__('Import', 'ai-eshop-optimizer') . '</button>';
        echo ' <span id="aieo-trs-csv-status" style="margin-left:10px;"></span>';
        echo '</form>';
        ?>
        <script>
        (function(){
            var f = document.getElementById('aieo-trs-csv-import');
            if (!f) return;
            f.addEventListener('submit', function(e){
                e.preventDefault();
                var status = document.getElementById('aieo-trs-csv-status');
                status.textContent = '<?php echo esc_js(__('Uploading…', 'ai-eshop-optimizer')); ?>';
                status.style.color = '#646970';
                var fd = new FormData(f);
                fd.append('action', 'aieo_trs_csv_import');
                fetch(<?php echo wp_json_encode($ajax); ?>, {method:'POST',credentials:'same-origin',body:fd})
                    .then(function(r){return r.json();})
                    .then(function(j){
                        if (!j || !j.success) {
                            var msg = (j && j.data && j.data.errors) ? j.data.errors.join(' · ') : '<?php echo esc_js(__('Import failed.', 'ai-eshop-optimizer')); ?>';
                            status.textContent = msg; status.style.color = '#b91c1c'; return;
                        }
                        var d = j.data;
                        status.textContent = '<?php echo esc_js(__('Inserted', 'ai-eshop-optimizer')); ?> ' + (d.inserted|0) + ', <?php echo esc_js(__('updated', 'ai-eshop-optimizer')); ?> ' + (d.updated|0) + ', <?php echo esc_js(__('skipped', 'ai-eshop-optimizer')); ?> ' + (d.skipped|0);
                        status.style.color = '#15803d';
                        if (d.errors && d.errors.length) { console.warn('TRS import warnings:', d.errors); }
                    });
            });
        })();
        </script>
        <?php
        echo '</div>';
    }

    private function render_migrator() {
        if (!class_exists('AIEO_TRS_Migrator')) {
            echo '<div class="aieo-dmm-card"><p>' . esc_html__('Migrator unavailable.', 'ai-eshop-optimizer') . '</p></div>';
            return;
        }
        $nonce = wp_create_nonce('aieo_trs_admin');
        $audit = AIEO_TRS_Migrator::audit();

        echo '<div class="aieo-dmm-card">';
        echo '<h3>' . esc_html__('Legacy data audit', 'ai-eshop-optimizer') . '</h3>';
        if (empty($audit['legacy_table'])) {
            echo '<p>' . esc_html__('No legacy `wp_woocommerce_shipping_table_rates` table found. Nothing to migrate.', 'ai-eshop-optimizer') . '</p>';
            echo '</div>';
            return;
        }
        echo '<p>';
        echo sprintf(
            
            esc_html__('Legacy table has %1$d rules. %2$d already migrated.', 'ai-eshop-optimizer'),
            (int) $audit['legacy_rows'],
            (int) $audit['already_migrated']
        );
        echo '</p>';
        $orphans = isset($audit['orphan_zone_methods']) ? (int) $audit['orphan_zone_methods'] : 0;
        if ($orphans > 0) {
            echo '<div class="notice notice-warning inline" style="margin:10px 0;padding:8px 12px;"><p>';
            echo sprintf(
                
                esc_html__('%d WooCommerce zone-method row(s) still reference the legacy `table_rate` slug and emit no rates. Re-run the migration below to flip them onto `aieo_table_rate`.', 'ai-eshop-optimizer'),
                $orphans
            );
            echo '</p></div>';
        }
        if (!empty($audit['by_instance'])) {
            echo '<table class="wp-list-table widefat" style="max-width:480px;"><thead><tr>';
            echo '<th>' . esc_html__('Method instance', 'ai-eshop-optimizer') . '</th>';
            echo '<th>' . esc_html__('Rule rows', 'ai-eshop-optimizer') . '</th>';
            echo '</tr></thead><tbody>';
            foreach ($audit['by_instance'] as $r) {
                echo '<tr><td>#' . (int) $r['shipping_method_id'] . '</td><td>' . (int) $r['rows_count'] . '</td></tr>';
            }
            echo '</tbody></table>';
        }
        echo '<p style="margin-top:14px;">';
        echo '<button type="button" class="button button-primary" id="aieo-trs-migrate-run">' . esc_html__('Run migration', 'ai-eshop-optimizer') . '</button>';
        echo ' <span id="aieo-trs-migrate-status" style="margin-left:10px;font-size:13px;color:#646970;"></span>';
        echo '</p>';
        echo '</div>';

        $ajax = admin_url('admin-ajax.php');
        ?>
        <script>
        (function(){
            var btn = document.getElementById('aieo-trs-migrate-run');
            if (!btn) return;
            var status = document.getElementById('aieo-trs-migrate-status');
            var NONCE = <?php echo wp_json_encode($nonce); ?>;
            var AJAX  = <?php echo wp_json_encode($ajax); ?>;
            function post(action, params) {
                var fd = new FormData();
                fd.append('action', action); fd.append('nonce', NONCE);
                Object.keys(params || {}).forEach(function(k){ fd.append(k, params[k]); });
                return fetch(AJAX, {method:'POST',credentials:'same-origin',body:fd}).then(function(r){return r.json();});
            }
            btn.addEventListener('click', function(){
                var cursor = 0, ins = 0, upd = 0, skp = 0, total = 0, done = false;
                function tick(){
                    if (done) return;
                    status.textContent = '<?php echo esc_js(__('Migrating… processed', 'ai-eshop-optimizer')); ?> ' + total;
                    status.style.color = '#646970';
                    post('aieo_trs_migrate_run', {cursor: cursor, batch_size: 500}).then(function(j){
                        if (!j || !j.success) { status.textContent = '<?php echo esc_js(__('Failed.', 'ai-eshop-optimizer')); ?>'; status.style.color = '#b91c1c'; done = true; return; }
                        var d = j.data || {};
                        total += (d.processed|0); ins += (d.inserted|0); upd += (d.updated|0); skp += (d.skipped|0);
                        if (d.done) {
                            status.textContent = '<?php echo esc_js(__('Done. Inserted', 'ai-eshop-optimizer')); ?> ' + ins + ', <?php echo esc_js(__('updated', 'ai-eshop-optimizer')); ?> ' + upd + (skp ? ', <?php echo esc_js(__('skipped', 'ai-eshop-optimizer')); ?> ' + skp : '');
                            status.style.color = '#15803d'; done = true;
                            if (d.errors && d.errors.length) console.warn('TRS migration warnings:', d.errors);
                        } else {
                            cursor = (d.next_cursor|0);
                            setTimeout(tick, 30);
                        }
                    });
                }
                tick();
            });
        })();
        </script>
        <?php
    }
}
