<?php

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

class AIEO_DMM_Role_Pricing extends AIEO_DMM_Module_Base {

    public function get_id() { return 'role_pricing'; }
    public function get_label() { return __('Role Pricing', 'ai-eshop-optimizer'); }
    public function get_priority() { return 35; }
    public function get_group() { return 'special_pricing_offers'; }

    public function register_hooks() {
        add_action('wp_ajax_aieo_dmm_role_pricing_save_settings', array($this, 'ajax_save_settings'));
        add_action('wp_ajax_aieo_dmm_role_pricing_upload', array($this, 'ajax_upload_csv'));
        add_action('wp_ajax_aieo_dmm_role_pricing_process_batch', array($this, 'ajax_process_batch'));
        add_action('wp_ajax_aieo_dmm_role_pricing_export', array($this, 'ajax_export_csv'));
        add_action('wp_ajax_aieo_dmm_role_pricing_download_template', array($this, 'ajax_download_template'));
        add_action('wp_ajax_aieo_dmm_role_pricing_fetch_rows', array($this, 'ajax_fetch_rows'));
        add_action('wp_ajax_aieo_dmm_role_pricing_save_cell', array($this, 'ajax_save_cell'));
        add_action('wp_ajax_aieo_dmm_role_pricing_gf_export', array($this, 'ajax_gf_export_forms'));

        
        
        
        
        
        
        add_action('woocommerce_before_shop_loop_item_title',   array($this, 'render_archive_pro_badge'), 11);
        
        
        
        
        
        
        
        
        
        
        
        
        add_filter('render_block_woocommerce/product-image-gallery',
            array($this, 'inject_pro_badge_into_gallery'), 11, 2);
        add_action('wp_enqueue_scripts',                        array($this, 'enqueue_pro_badge_css'));
        add_filter('body_class',                                array($this, 'add_badge_layout_body_class'));

        
        
        
        
        
        
        
        
        add_action('wp', array($this, 'rewire_login_message_renderer'), 100);
        add_filter('woocommerce_get_price_html', array($this, 'show_pro_price_as_sale'),         10, 2);
        add_filter('woocommerce_get_price_html', array($this, 'append_login_message_to_price'), 99, 2);
        add_action('wp_head', array($this, 'print_login_message_css'), 99);
        add_action('wp_footer', array($this, 'render_login_modal'));

        
        
        
        
        
        $GLOBALS['aieo_login_msg_owner_core'] = true;

        
        
        
        $price_filters = array(
            'woocommerce_product_get_price',
            'woocommerce_product_get_sale_price',
            'woocommerce_product_get_regular_price',
            'woocommerce_product_variation_get_price',
            'woocommerce_product_variation_get_sale_price',
            'woocommerce_product_variation_get_regular_price',
            'woocommerce_variation_prices_price',
            'woocommerce_variation_prices_sale_price',
            'woocommerce_variation_prices_regular_price',
        );
        foreach ($price_filters as $f) {
            add_filter($f, array($this, 'filter_role_price'), 10, 2);
        }
        
        
        add_filter('woocommerce_get_variation_prices_hash', array($this, 'modify_variation_hash'), 10, 3);
        
        add_action('woocommerce_before_calculate_totals', array($this, 'apply_cart_role_prices'), 1);

        
        
        
        
        
        
        
        add_filter('woocommerce_sale_flash',                    array($this, 'maybe_suppress_sale_flash'),         20, 3);
        add_filter('om_ppe_product_label_display',              array($this, 'maybe_suppress_om_ppe_sale_label'),  10, 3);

        
        
        
        
        
        
        
        add_action('gform_user_registered',  array($this, 'gf_assign_roles_on_registration'), 10, 3);
        add_action('gform_after_submission', array($this, 'gf_modify_roles_on_update'),       10, 2);
        add_action('gform_after_submission', array($this, 'gf_emit_program_prompt'),          10, 2);
        add_action('wp',                     array($this, 'gf_capture_pre_submission_roles'));
        add_action('wp_footer',              array($this, 'gf_print_post_login_refresh'));
        add_action('init',                   array($this, 'gf_send_no_cache_headers_on_membership_page'));
        add_shortcode('membership_gf',       array($this, 'gf_membership_shortcode'));
    }

    
    public function enqueue_pro_badge_css() {
        if (is_admin()) { return; }
        
        
        
        
        
        if (class_exists('AIEO_DMM_Badge_Renderer') && AIEO_DMM_Badge_Renderer::is_enabled()) { return; }
    }

    
    public function add_badge_layout_body_class($classes) {
        $position = AIEO_DMM_Settings::get('archive_badge_position', 'image_top_left');
        if ($position === 'below_favourite') {
            $classes[] = 'aieo-badges-below-fav';
        }
        return $classes;
    }

    
    
    

    
    private $has_pricing_memo = array();

    
    private $role_prices_memo = array();

    private function role_pricing_table() {
        global $wpdb;
        
        
        return $wpdb->prefix . 'aieo_dmm_role_prices';
    }

    
    private function product_has_role_pricing($product_id) {
        $product_id = (int) $product_id;
        if ($product_id <= 0) { return false; }
        $key = (string) $product_id;
        if (isset($this->has_pricing_memo[$key])) {
            return $this->has_pricing_memo[$key];
        }
        global $wpdb;
        $table = $this->role_pricing_table();
        
        $exists = (bool) $wpdb->get_var($wpdb->prepare(
            "SELECT 1 FROM {$table} WHERE product_id = %d OR parent_id = %d LIMIT 1",
            $product_id, $product_id
        ));
        $this->has_pricing_memo[$key] = $exists;
        return $exists;
    }

    
    private function get_role_prices_for_product($product_id) {
        $product_id = (int) $product_id;
        if ($product_id <= 0) { return array(); }
        if (isset($this->role_prices_memo[$product_id])) {
            return $this->role_prices_memo[$product_id];
        }
        global $wpdb;
        $table = $this->role_pricing_table();
        
        $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE product_id = %d", $product_id), ARRAY_A);
        $out = array();
        if (is_array($row)) {
            foreach ($row as $col => $val) {
                if (strpos($col, 'role_') !== 0) { continue; }
                if ($col === 'role_prices') { continue; } 
                if ($val === null || $val === '') { continue; }
                $price = (float) $val;
                if ($price > 0) {
                    $out[substr($col, 5)] = $price;
                }
            }
        }
        $this->role_prices_memo[$product_id] = $out;
        return $out;
    }

    
    private function product_has_real_public_sale($product) {
        if (!$product || !is_object($product) || !method_exists($product, 'get_id')) {
            return false;
        }
        $check = static function ($pid) {
            $raw_sale = get_post_meta((int) $pid, '_sale_price', true);
            if ($raw_sale === '' || $raw_sale === null) { return false; }
            $sale = (float) $raw_sale;
            if ($sale <= 0) { return false; }
            $raw_reg = get_post_meta((int) $pid, '_regular_price', true);
            if ($raw_reg === '' || $raw_reg === null) { return false; }
            return $sale < (float) $raw_reg;
        };

        if (method_exists($product, 'is_type') && $product->is_type('variable')) {
            foreach ((array) $product->get_children() as $vid) {
                if ($check($vid)) { return true; }
            }
            
            return $check($product->get_id());
        }
        return $check($product->get_id());
    }

    
    private function current_user_is_pro() {
        if (!is_user_logged_in()) { return false; }
        $user = wp_get_current_user();
        if (!$user || empty($user->roles)) { return false; }
        $enabled = (array) AIEO_DMM_Settings::get('enabled_roles', array());
        if (empty($enabled)) { return false; }
        foreach ((array) $user->roles as $r) {
            if (in_array($r, $enabled, true)) { return true; }
        }
        return false;
    }

    
    private function best_real_public_sale($product) {
        if (!$product || !is_object($product)) { return null; }
        $best = null;
        $consider = static function ($pid) use (&$best) {
            $raw_sale = get_post_meta((int) $pid, '_sale_price', true);
            if ($raw_sale === '' || $raw_sale === null) { return; }
            $sale = (float) $raw_sale;
            if ($sale <= 0) { return; }
            $raw_reg = get_post_meta((int) $pid, '_regular_price', true);
            if ($raw_reg === '' || $raw_reg === null) { return; }
            if ($sale >= (float) $raw_reg) { return; }
            if ($best === null || $sale < $best) { $best = $sale; }
        };
        if (method_exists($product, 'is_type') && $product->is_type('variable')) {
            foreach ((array) $product->get_children() as $vid) {
                $consider($vid);
            }
            $consider($product->get_id());
        } else {
            $consider($product->get_id());
        }
        return $best;
    }

    
    private function best_role_price_for_current_user($product) {
        if (!$product || !is_object($product)) { return null; }
        if (!$this->current_user_is_pro()) { return null; }
        $user = wp_get_current_user();
        $user_roles = $user && !empty($user->roles) ? (array) $user->roles : array();
        if (empty($user_roles)) { return null; }

        $best = null;
        $self = $this;
        $consider = static function ($pid) use (&$best, $self, $user_roles) {
            $prices = $self->get_role_prices_for_product_public((int) $pid);
            if (empty($prices)) { return; }
            foreach ($user_roles as $role) {
                $sanitized = strtolower(preg_replace('/[^a-z0-9_]/i', '_', (string) $role));
                if (isset($prices[$sanitized]) && (float) $prices[$sanitized] > 0) {
                    $p = (float) $prices[$sanitized];
                    if ($best === null || $p < $best) { $best = $p; }
                }
                if (isset($prices[$role]) && (float) $prices[$role] > 0) {
                    $p = (float) $prices[$role];
                    if ($best === null || $p < $best) { $best = $p; }
                }
            }
        };
        if (method_exists($product, 'is_type') && $product->is_type('variable')) {
            foreach ((array) $product->get_children() as $vid) {
                $consider($vid);
            }
            $consider($product->get_id());
        } else {
            $consider($product->get_id());
        }
        return $best;
    }

    
    public function get_role_prices_for_product_public($product_id) {
        return $this->get_role_prices_for_product($product_id);
    }

    
    private $in_price_filter = false;

    
    public function filter_role_price($price, $product) {
        if ($this->in_price_filter) { return $price; }
        if (!$product || !is_object($product) || !is_numeric($price) || (float) $price <= 0) {
            return $price;
        }
        if (!$this->current_user_is_pro()) { return $price; }

        $this->in_price_filter = true;
        $best = $this->best_role_price_for_current_user($product);
        $this->in_price_filter = false;

        if ($best === null || $best <= 0) { return $price; }

        $public = (float) $price;
        return ($best < $public) ? $best : $price;
    }

    
    public function modify_variation_hash($hash, $product, $for_display) {
        if (!is_user_logged_in()) { return $hash; }
        $u = wp_get_current_user();
        $roles = $u && !empty($u->roles) ? (array) $u->roles : array();
        if (empty($roles)) { return $hash; }
        sort($roles);
        if (!is_array($hash)) { $hash = (array) $hash; }
        $hash['aieo_role'] = md5(implode('|', $roles));
        return $hash;
    }

    
    public function apply_cart_role_prices($cart) {
        if (!is_a($cart, 'WC_Cart')) { return; }
        if (!$this->current_user_is_pro()) { return; }
        if (did_action('woocommerce_before_calculate_totals') >= 2 && doing_filter('woocommerce_calculate_totals')) {
            return;
        }
        foreach ($cart->get_cart() as $item) {
            if (!isset($item['data']) || !is_object($item['data'])) { continue; }
            $product = $item['data'];
            $best = $this->best_role_price_for_current_user($product);
            if ($best === null || $best <= 0) { continue; }
            $current = (float) $product->get_price('edit');
            if ($best < $current || $current <= 0) {
                $product->set_price($best);
            }
        }
    }

    
    private function should_suppress_sale_badge($product) {
        if (!$product || !is_object($product)) { return false; }
        $has_role = $this->product_has_role_pricing((int) $product->get_id());
        if (!$has_role) { return false; }
        $public_sale = $this->best_real_public_sale($product);
        if ($public_sale === null) { return true; }
        if (!$this->current_user_is_pro()) { return false; }
        $role_price = $this->best_role_price_for_current_user($product);
        if ($role_price === null) { return false; }
        
        
        return $role_price < $public_sale;
    }

    
    private function should_suppress_pro_badge($product) {
        if (!$product || !is_object($product)) { return true; }
        if (!$this->product_has_role_pricing((int) $product->get_id())) { return true; }
        if (!$this->current_user_is_pro()) { return false; }
        $public_sale = $this->best_real_public_sale($product);
        if ($public_sale === null) { return false; }
        $role_price = $this->best_role_price_for_current_user($product);
        if ($role_price === null) { return false; }
        return $role_price >= $public_sale;
    }

    
    public function show_pro_price_as_sale($price_html, $product) {
        if (!is_a($product, 'WC_Product') || !$this->current_user_is_pro()) {
            return $price_html;
        }
        if ($product->is_type('variable') || $product->is_type('grouped')) {
            return $price_html; 
        }
        $role_price = $this->best_role_price_for_current_user($product);
        if ($role_price === null || (float) $role_price <= 0) {
            return $price_html;
        }
        
        $public_regular = (float) str_replace(',', '.', (string) get_post_meta((int) $product->get_id(), '_regular_price', true));
        if ($public_regular <= 0 || (float) $role_price >= $public_regular) {
            return $price_html;
        }
        
        
        if (abs((float) $product->get_price() - (float) $role_price) > 0.0001) {
            return $price_html;
        }
        $reg_display  = wc_get_price_to_display($product, array('price' => $public_regular));
        $role_display = wc_get_price_to_display($product, array('price' => (float) $role_price));
        return wc_format_sale_price($reg_display, $role_display) . $product->get_price_suffix();
    }

    
    public function maybe_suppress_sale_flash($html, $post, $product) {
        return $this->should_suppress_sale_badge($product) ? '' : $html;
    }

    
    public function rewire_login_message_renderer() {
        if (!class_exists('OM_Role_Login_Message') || !class_exists('OM_Role_Price_Handler')) {
            return;
        }
        $msg = OM_Role_Login_Message::instance(OM_Role_Price_Handler::instance());
        if (!is_object($msg)) {
            return;
        }
        
        remove_filter('woocommerce_get_price_html', array($msg, 'add_login_message'), 99);
    }

    
    public function append_login_message_to_price($price_html, $product) {
        if (is_user_logged_in()) {
            return $price_html;
        }
        if (empty(AIEO_DMM_Settings::get('show_login_message', false))) {
            return $price_html;
        }
        if (!$product || !is_a($product, 'WC_Product')) {
            return $price_html;
        }
        if (!$this->product_has_role_pricing((int) $product->get_id())) {
            return $price_html;
        }
        
        
        
        if (!empty($GLOBALS['aieo_satc_suppress_login_message'])) {
            return $price_html;
        }

        $is_single = (function_exists('is_product') && is_product());
        
        
        
        
        
        if ($is_single && self::login_in_cluster()) {
            return $price_html;
        }
        $key = $is_single ? 'login_message_text' : 'login_message_archive';
        $raw = (string) AIEO_DMM_Settings::get($key, '');
        if ($raw === '') {
            return $price_html;
        }

        $login_url = function_exists('wc_get_page_permalink') ? (string) wc_get_page_permalink('myaccount') : wp_login_url();
        $raw       = str_replace('{login_url}', esc_url($login_url), $raw);

        $custom_class = (string) AIEO_DMM_Settings::get('login_message_css_class', '');
        $classes = 'om-role-login-message ' . ($is_single ? 'om-login-message-single' : 'om-login-message-archive');
        if ($custom_class !== '') {
            $classes .= ' ' . $custom_class;
        }

        
        
        
        
        
        
        
        
        
        return $price_html . sprintf(
            '<span class="%s">%s</span>',
            esc_attr($classes),
            $raw
        );
    }

    
    public function print_login_message_css() {
        if (is_user_logged_in()) { return; }
        if (empty(AIEO_DMM_Settings::get('show_login_message', false))) { return; }
        $color = (string) sanitize_hex_color((string) AIEO_DMM_Settings::get('login_message_color', ''));
        $ad    = (int) AIEO_DMM_Settings::get('login_message_font_archive_desktop', 0);
        $am    = (int) AIEO_DMM_Settings::get('login_message_font_archive_mobile', 0);
        $sd    = (int) AIEO_DMM_Settings::get('login_message_font_single_desktop', 0);
        $sm    = (int) AIEO_DMM_Settings::get('login_message_font_single_mobile', 0);
        if ($color === '' && $ad <= 0 && $am <= 0 && $sd <= 0 && $sm <= 0) {
            return;
        }
        $css = '';
        if ($color !== '') {
            $css .= '.om-role-login-message a{color:' . $color . '}';
        }
        
        if ($ad > 0) {
            $css .= '.om-login-message-archive,.om-login-message-archive a{font-size:' . $ad . 'px}';
        }
        if ($sd > 0) {
            $css .= '.om-login-message-single,.om-login-message-single a{font-size:' . $sd . 'px}';
        }
        
        $mq = '';
        if ($am > 0) {
            $mq .= '.om-login-message-archive,.om-login-message-archive a{font-size:' . $am . 'px}';
        }
        if ($sm > 0) {
            $mq .= '.om-login-message-single,.om-login-message-single a{font-size:' . $sm . 'px}';
        }
        if ($mq !== '') {
            $css .= '@media(max-width:768px){' . $mq . '}';
        }
        echo '<style id="aieo-role-login-message-css">' . $css . '</style>'; 
    }

    
    public function render_login_modal() {
        if (is_user_logged_in()) { return; }
        if (empty(AIEO_DMM_Settings::get('show_login_message', false))) { return; }
        if (empty(AIEO_DMM_Settings::get('login_message_modal_enabled', false))) { return; }
        if (function_exists('is_account_page') && is_account_page()) { return; }
        $is_ctx = (function_exists('is_woocommerce') && is_woocommerce())
            || (function_exists('is_shop') && is_shop())
            || is_front_page() || is_home();
        if (!$is_ctx) { return; }

        
        $current = esc_url_raw(home_url(wp_unslash($_SERVER['REQUEST_URI'] ?? '/'))); 

        $login_html = '';
        if (function_exists('woocommerce_login_form')) {
            ob_start();
            woocommerce_login_form(array('redirect' => $current));
            $login_html = (string) ob_get_clean();
        }

        $sso_html = class_exists('AIEO_Runtime_SSO_Buttons')
            ? (string) AIEO_Runtime_SSO_Buttons::render('login', array(
                'layout' => 'stacked',
                'intro'  => __('Ή συνδεθείτε με:', 'ai-eshop-optimizer'),
            ))
            : '';

        $rich = (string) AIEO_DMM_Settings::get('login_message_modal_content', '');

        if ($login_html === '' && $sso_html === '' && trim($rich) === '') { return; }
        ?>
        <div id="aieo-rolelogin-modal" class="aieo-rolelogin-modal" hidden role="dialog" aria-modal="true" aria-label="<?php esc_attr_e('Sign in for special prices', 'ai-eshop-optimizer'); ?>">
            <div class="aieo-rolelogin-modal__backdrop" data-aieo-rl-close></div>
            <div class="aieo-rolelogin-modal__panel">
                <button type="button" class="aieo-rolelogin-modal__close" data-aieo-rl-close aria-label="<?php esc_attr_e('Close', 'ai-eshop-optimizer'); ?>">&times;</button>
                <?php if ($login_html !== '') : ?>
                    <div class="aieo-rolelogin-modal__login"><?php echo $login_html; ?></div>
                <?php endif; ?>
                <?php if ($sso_html !== '') : ?>
                    <div class="aieo-rolelogin-modal__sso"><?php echo $sso_html; ?></div>
                <?php endif; ?>
                <?php if (trim($rich) !== '') : ?>
                    <div class="aieo-rolelogin-modal__rich"><?php echo wp_kses_post($rich); ?></div>
                <?php endif; ?>
            </div>
        </div>
        <style id="aieo-rolelogin-modal-css">
        .aieo-rolelogin-modal[hidden]{display:none}
        .aieo-rolelogin-modal{position:fixed;inset:0;z-index:1000000;display:flex;align-items:center;justify-content:center;padding:16px}
        .aieo-rolelogin-modal__backdrop{position:absolute;inset:0;background:rgba(20,20,20,.5)}
        .aieo-rolelogin-modal__panel{position:relative;z-index:1;width:min(440px,100%);max-height:90vh;overflow:auto;background:#fff;border-radius:14px;padding:30px 22px 24px;box-shadow:0 20px 60px rgba(0,0,0,.3)}
        .aieo-rolelogin-modal__close{position:absolute;top:8px;right:10px;width:36px;height:36px;border:0;background:transparent;font-size:28px;line-height:1;cursor:pointer;color:#1f2937}
        .aieo-rolelogin-modal__panel .woocommerce-form-login,.aieo-rolelogin-modal__panel form.login{margin:0;border:0;padding:0;box-shadow:none}
        .aieo-rolelogin-modal__sso{margin-top:16px}
        .aieo-rolelogin-modal__rich{margin-top:18px;font-size:13px;line-height:1.5}
        </style>
        <script>
        (function(){
            var m=document.getElementById('aieo-rolelogin-modal');if(!m)return;
            function open(){m.removeAttribute('hidden');document.body.style.overflow='hidden';}
            function close(){m.setAttribute('hidden','');document.body.style.overflow='';}
            document.addEventListener('click',function(e){
                var t=e.target;if(!t||!t.closest)return;
                if(t.closest('.om-role-login-message a')){e.preventDefault();open();return;}
                if(t.closest('[data-aieo-rl-close]')){e.preventDefault();close();}
            });
            document.addEventListener('keydown',function(e){if(e.key==='Escape'&&!m.hasAttribute('hidden'))close();});
        })();
        </script>
        <?php
    }

    
    public static function login_in_cluster() {
        $o = get_option('aieo_dmm_role_pricing_login_settings', array());
        return is_array($o) && !empty($o['enabled']);
    }

    
    public function render_cluster_login_message() {
        if (!self::login_in_cluster()) { return; }
        if (is_user_logged_in()) { return; }
        if (empty(AIEO_DMM_Settings::get('show_login_message', false))) { return; }
        if (!function_exists('is_product') || !is_product()) { return; }
        global $product;
        if (!$product || !is_a($product, 'WC_Product')) { $product = wc_get_product(get_the_ID()); }
        if (!$product || !is_a($product, 'WC_Product')) { return; }
        if (!$this->product_has_role_pricing((int) $product->get_id())) { return; }

        $raw = (string) AIEO_DMM_Settings::get('login_message_text', '');
        if ($raw === '') { return; }
        $login_url = function_exists('wc_get_page_permalink') ? (string) wc_get_page_permalink('myaccount') : wp_login_url();
        $raw       = str_replace('{login_url}', esc_url($login_url), $raw);

        $custom_class = (string) AIEO_DMM_Settings::get('login_message_css_class', '');
        $classes = 'om-role-login-message om-login-message-single om-login-message-cluster' . ($custom_class !== '' ? ' ' . $custom_class : '');
        echo '<span class="' . esc_attr($classes) . '">' . $raw . '</span>'; 
    }

    
    public function maybe_suppress_om_ppe_sale_label($display, $product, $rule) {
        if (!$display) { return $display; }
        if ($rule !== 'products_on_sale') { return $display; }
        return $this->should_suppress_sale_badge($product) ? false : $display;
    }

    
    public function render_archive_pro_badge() {
        if (class_exists('AIEO_DMM_Badge_Renderer') && AIEO_DMM_Badge_Renderer::is_enabled()) { return; }
        global $product;
        if (!$product || !is_a($product, 'WC_Product')) { return; }
        if ($this->should_suppress_pro_badge($product)) { return; }
        $label = apply_filters('aieo_dmm_pro_badge_label', __('PRO', 'ai-eshop-optimizer'));
        $tooltip = apply_filters(
            'aieo_dmm_pro_badge_tooltip',
            __('This product has special pricing for professionals', 'ai-eshop-optimizer')
        );
        echo '<span class="aieo-pro-badge" '
            . 'role="img" '
            . 'aria-label="' . esc_attr($tooltip) . '" '
            . 'title="' . esc_attr($tooltip) . '">'
            . '<span class="aieo-pro-badge-icon" aria-hidden="true">&#9733;</span>'
            . '<span class="aieo-pro-badge-text">' . esc_html($label) . '</span>'
            . '</span>';
    }

    
    public function render_single_pro_badge() {
        if (class_exists('AIEO_DMM_Badge_Renderer') && AIEO_DMM_Badge_Renderer::is_enabled()) { return; }
        $html = $this->build_single_pro_badge_html();
        if ($html !== '') {
            
            echo $html;
        }
    }

    
    private function build_single_pro_badge_html() {
        global $product;
        if (!$product || !is_a($product, 'WC_Product')) { return ''; }
        if ($this->should_suppress_pro_badge($product)) { return ''; }
        $label = apply_filters('aieo_dmm_pro_badge_label', __('PRO', 'ai-eshop-optimizer'));
        $tooltip = apply_filters(
            'aieo_dmm_pro_badge_tooltip',
            __('This product has special pricing for professionals', 'ai-eshop-optimizer')
        );
        return '<span class="aieo-pro-badge" '
            . 'role="img" '
            . 'aria-label="' . esc_attr($tooltip) . '" '
            . 'title="' . esc_attr($tooltip) . '">'
            . '<span class="aieo-pro-badge-icon" aria-hidden="true">&#9733;</span>'
            . '<span class="aieo-pro-badge-text">' . esc_html($label) . '</span>'
            . '</span>';
    }

    
    public function inject_pro_badge_into_gallery($block_content, $block) {
        if (class_exists('AIEO_DMM_Badge_Renderer') && AIEO_DMM_Badge_Renderer::is_enabled()) { return $block_content; }
        if (!is_singular('product')) { return $block_content; }
        $badge = $this->build_single_pro_badge_html();
        if ($badge === '') { return $block_content; }
        
        
        if (preg_match('/^(\s*<[a-zA-Z][^>]*>)(.*)$/s', (string) $block_content, $m)) {
            return $m[1] . $badge . $m[2];
        }
        return $badge . $block_content;
    }

    public function render_subtab() {
        $per_page     = 25;
        
        $current_page = isset($_GET['paged']) ? max(1, (int) $_GET['paged']) : 1;

        $this->render_template('role-pricing.php', array(
            'settings'     => AIEO_DMM_Settings::get_all(),
            'all_roles'    => $this->get_available_roles(),
            
            
            
            
            'total_rows'   => $this->count_priced_rows(),
            'price_rows'   => $this->fetch_priced_rows($per_page, $current_page),
            'per_page'     => $per_page,
            'current_page' => $current_page,
            'gf_forms'     => $this->gf_available_forms_for_select(),
            'gf_active'    => class_exists('GFAPI'),
            'gf_samples_url' => plugins_url('assets/samples/', AIEO_FILE),
        ));
    }

    
    private function gf_available_forms_for_select() {
        if (!class_exists('GFAPI')) { return array(); }
        $forms = GFAPI::get_forms(true, false);
        $out = array();
        if (is_array($forms)) {
            foreach ($forms as $f) {
                if (isset($f['id'])) {
                    $out[] = array(
                        'id'    => (int) $f['id'],
                        'title' => isset($f['title']) ? (string) $f['title'] : ('Form #' . (int) $f['id']),
                    );
                }
            }
        }
        return $out;
    }

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

        global $wpdb;
        $table = $wpdb->prefix . 'aieo_dmm_role_prices';
        if (!aieo_dmm_table_exists($table)) {
            wp_send_json_success(array('rows' => array(), 'total' => 0, 'filtered' => 0, 'columns' => array()));
        }

        $enabled_roles = AIEO_DMM_Settings::get('enabled_roles', array());
        $role_cols     = array();
        foreach ($enabled_roles as $role) {
            $role_cols[$role] = aieo_dmm_sanitize_role_column($role);
        }

        $page         = isset($_POST['page'])     ? max(1, absint($_POST['page']))                : 1;
        $per_page     = isset($_POST['per_page']) ? min(200, max(10, absint($_POST['per_page']))) : 25;
        $search       = isset($_POST['search']) ? trim(sanitize_text_field(wp_unslash($_POST['search']))) : '';
        $only_nonempty = !empty($_POST['only_nonempty']);
        $sort_col_req = isset($_POST['sort']) ? sanitize_key(wp_unslash($_POST['sort'])) : 'product_id';
        $dir_req      = isset($_POST['dir']) && sanitize_key(wp_unslash($_POST['dir'])) === 'desc' ? 'DESC' : 'ASC';

        
        $allowed_sort = array('product_id' => 'product_id', 'sku' => 'sku', 'product_type' => 'product_type');
        foreach ($role_cols as $role => $col) {
            $allowed_sort[$role] = $col;
        }
        $sort_col = isset($allowed_sort[$sort_col_req]) ? $allowed_sort[$sort_col_req] : 'product_id';

        
        $where  = array('1=1');
        $params = array();

        if ($search !== '') {
            $where[]  = '(sku LIKE %s OR CAST(product_id AS CHAR) LIKE %s)';
            $like     = '%' . $wpdb->esc_like($search) . '%';
            $params[] = $like;
            $params[] = $like;
        }

        if ($only_nonempty && !empty($role_cols)) {
            $non_null = array();
            foreach ($role_cols as $col) {
                
                $non_null[] = "(`{$col}` IS NOT NULL AND `{$col}` > 0)";
            }
            $where[] = '(' . implode(' OR ', $non_null) . ')';
        }

        $where_sql = 'WHERE ' . implode(' AND ', $where);

        
        
        $total = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}");

        
        
        $filtered_sql = "SELECT COUNT(*) FROM {$table} {$where_sql}";
        $filtered     = (int) $wpdb->get_var(empty($params) ? $filtered_sql : $wpdb->prepare($filtered_sql, $params));

        
        $offset = ($page - 1) * $per_page;
        
        $data_sql = "SELECT * FROM {$table} {$where_sql} ORDER BY `{$sort_col}` {$dir_req} LIMIT %d OFFSET %d";
        $data_params = array_merge($params, array($per_page, $offset));
        $rows = $wpdb->get_results($wpdb->prepare($data_sql, $data_params), ARRAY_A);
        if (!is_array($rows)) {
            $rows = array();
        }

        
        $columns = array(
            array('key' => 'product_id',   'label' => __('Product ID', 'ai-eshop-optimizer'),  'sortable' => true,  'editable' => false),
            array('key' => 'sku',          'label' => __('SKU', 'ai-eshop-optimizer'),         'sortable' => true,  'editable' => false),
            array('key' => 'product_type', 'label' => __('Type', 'ai-eshop-optimizer'),        'sortable' => true,  'editable' => false),
        );
        foreach ($enabled_roles as $role) {
            $columns[] = array('key' => $role, 'label' => $role, 'sortable' => true, 'editable' => true);
        }

        $out = array();
        foreach ($rows as $r) {
            $item = array(
                'product_id'   => (int) $r['product_id'],
                'sku'          => (string) ($r['sku'] ?? ''),
                'product_type' => (string) ($r['product_type'] ?? ''),
            );
            foreach ($role_cols as $role => $col) {
                $item[$role] = (isset($r[$col]) && $r[$col] !== null) ? (float) $r[$col] : null;
            }
            $out[] = $item;
        }

        wp_send_json_success(array(
            'columns'  => $columns,
            'rows'     => $out,
            'total'    => $total,
            'filtered' => $filtered,
            'page'     => $page,
            'per_page' => $per_page,
        ));
    }

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

        $product_id = isset($_POST['product_id']) ? absint(wp_unslash($_POST['product_id'])) : 0;
        $role       = isset($_POST['role']) ? sanitize_key(wp_unslash($_POST['role'])) : '';
        $raw_value  = isset($_POST['value']) ? trim(sanitize_text_field(wp_unslash($_POST['value']))) : '';

        if ($product_id <= 0 || $role === '') {
            wp_send_json_error(array('message' => __('Missing product_id or role.', 'ai-eshop-optimizer')));
        }

        $enabled_roles = AIEO_DMM_Settings::get('enabled_roles', array());
        if (!in_array($role, $enabled_roles, true)) {
            wp_send_json_error(array('message' => __('That role is not enabled.', 'ai-eshop-optimizer')));
        }

        
        $is_empty = $raw_value === '' || $raw_value === '-';
        if (!$is_empty) {
            $val = str_replace(',', '.', $raw_value);
            if (!is_numeric($val) || (float) $val < 0) {
                wp_send_json_error(array('message' => __('Value must be a non-negative number.', 'ai-eshop-optimizer')));
            }
            $val = (float) $val;
        } else {
            $val = null;
        }

        $col = aieo_dmm_sanitize_role_column($role);

        global $wpdb;
        $tables = array(
            $wpdb->prefix . 'aieo_dmm_role_prices',
            $wpdb->prefix . 'om_role_based_pricing',
        );

        foreach ($tables as $table) {
            if (!aieo_dmm_table_exists($table)) {
                continue;
            }
            $exists = $wpdb->get_var($wpdb->prepare(
                
                "SELECT 1 FROM {$table} WHERE product_id = %d LIMIT 1",
                $product_id
            ));
            if ($exists) {
                
                $wpdb->query($wpdb->prepare(
                    "UPDATE {$table} SET `{$col}` = " . ($is_empty ? 'NULL' : '%f') . " WHERE product_id = %d",
                    $is_empty ? array($product_id) : array($val, $product_id)
                ));
            } else {
                
                $product = function_exists('wc_get_product') ? wc_get_product($product_id) : null;
                $sku = $product ? (string) $product->get_sku() : '';
                $parent_id = $product && $product->is_type('variation') ? (int) $product->get_parent_id() : $product_id;
                $type = $product && $product->is_type('variation') ? 'variation' : 'simple';
                $insert = array(
                    'product_id'   => $product_id,
                    'parent_id'    => $parent_id,
                    'product_type' => $type,
                    'sku'          => $sku,
                );
                $fmt = array('%d', '%d', '%s', '%s');
                if (!$is_empty) {
                    $insert[$col] = $val;
                    $fmt[] = '%f';
                }
                $wpdb->insert($table, $insert, $fmt);
            }
        }

        AIEO_DMM_Cache::bump_version('prices');

        wp_send_json_success(array(
            'product_id' => $product_id,
            'role'       => $role,
            'value'      => $is_empty ? null : $val,
        ));
    }

    
    public function get_available_roles() {
        if (!function_exists('get_editable_roles')) {
            require_once ABSPATH . 'wp-admin/includes/user.php';
        }
        $roles = get_editable_roles();
        $out = array();
        foreach ($roles as $slug => $data) {
            if ($slug === 'administrator') {
                continue;
            }
            $out[$slug] = isset($data['name']) ? translate_user_role($data['name']) : $slug;
        }
        return $out;
    }

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

        $raw_roles = isset($_POST['enabled_roles']) && is_array($_POST['enabled_roles'])
            ? array_map('sanitize_key', wp_unslash($_POST['enabled_roles']))
            : array();
        $roles = array();
        foreach ($raw_roles as $r) {
            $k = sanitize_key($r);
            if ($k !== '' && $k !== 'administrator') {
                $roles[] = $k;
            }
        }

        $badge_position = isset($_POST['archive_badge_position']) ? sanitize_key(wp_unslash($_POST['archive_badge_position'])) : 'image_top_left';
        if (!in_array($badge_position, array('image_top_left', 'below_favourite'), true)) {
            $badge_position = 'image_top_left';
        }

        
        
        
        AIEO_DMM_Settings::update(array(
            'enabled_roles'          => $roles,
            'show_login_message'     => !empty($_POST['show_login_message']),
            'login_message_text'     => isset($_POST['login_message_text']) ? wp_kses_post(wp_unslash($_POST['login_message_text'])) : '',
            'login_message_archive'  => isset($_POST['login_message_archive']) ? wp_kses_post(wp_unslash($_POST['login_message_archive'])) : '',
            'login_message_css_class'=> isset($_POST['login_message_css_class']) ? sanitize_html_class(wp_unslash($_POST['login_message_css_class']), '') : '',
            
            'login_message_color'        => isset($_POST['login_message_color']) ? (string) sanitize_hex_color(wp_unslash($_POST['login_message_color'])) : '',
            'login_message_font_archive_desktop' => isset($_POST['login_message_font_archive_desktop']) ? max(0, min(72, (int) $_POST['login_message_font_archive_desktop'])) : 0,
            'login_message_font_archive_mobile'  => isset($_POST['login_message_font_archive_mobile'])  ? max(0, min(72, (int) $_POST['login_message_font_archive_mobile']))  : 0,
            'login_message_font_single_desktop'  => isset($_POST['login_message_font_single_desktop'])  ? max(0, min(72, (int) $_POST['login_message_font_single_desktop']))  : 0,
            'login_message_font_single_mobile'   => isset($_POST['login_message_font_single_mobile'])   ? max(0, min(72, (int) $_POST['login_message_font_single_mobile']))   : 0,
            
            'login_message_modal_enabled' => !empty($_POST['login_message_modal_enabled']),
            'login_message_modal_content' => isset($_POST['login_message_modal_content']) ? wp_kses_post(wp_unslash($_POST['login_message_modal_content'])) : '',
            
            
            'mini_cart_badge_icon'   => isset($_POST['mini_cart_badge_icon']) ? wp_kses_post(wp_unslash($_POST['mini_cart_badge_icon'])) : '',
            'mini_cart_badge_text'   => isset($_POST['mini_cart_badge_text']) ? sanitize_text_field(wp_unslash($_POST['mini_cart_badge_text'])) : '',
            'mini_cart_badge_alt'    => isset($_POST['mini_cart_badge_alt'])  ? wp_kses_post(wp_unslash($_POST['mini_cart_badge_alt']))  : '',
            'wpdatatables_table_id'  => isset($_POST['wpdatatables_table_id']) ? absint(wp_unslash($_POST['wpdatatables_table_id'])) : 0,
            
            'archive_badge_position' => $badge_position,
        ));

        
        
        
        update_option('aieo_dmm_role_pricing_login_settings', array(
            'enabled' => !empty($_POST['login_message_in_cluster']),
        ), false);

        
        if (function_exists('aieo_dmm_sync_role_columns')) {
            aieo_dmm_sync_role_columns($roles);
        }
        AIEO_DMM_Settings::sync_legacy_role_pricing_option();

        AIEO_DMM_Cache::bump_version('prices');

        wp_send_json_success(array(
            'message' => __('Settings saved.', 'ai-eshop-optimizer'),
            'enabled_roles' => $roles,
        ));
    }

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

        if (empty($_FILES['csv_file']) || !isset($_FILES['csv_file']['tmp_name'])) {
            wp_send_json_error(array('message' => __('No file received.', 'ai-eshop-optimizer')));
        }

        $mode = isset($_POST['mode']) && sanitize_key(wp_unslash($_POST['mode'])) === 'overwrite' ? 'overwrite' : 'merge';

        $file = $_FILES['csv_file']; 
        $tmp  = isset($file['tmp_name']) ? $file['tmp_name'] : '';
        if (!is_uploaded_file($tmp)) {
            wp_send_json_error(array('message' => __('Upload validation failed.', 'ai-eshop-optimizer')));
        }

        
        
        
        
        
        $raw = file_get_contents($tmp);
        if (is_string($raw) && substr($raw, 0, 3) === "\xEF\xBB\xBF") {
            file_put_contents($tmp, substr($raw, 3));
        }

        
        
        
        
        
        $first_line = '';
        if ($peek = fopen($tmp, 'r')) {
            $first_line = (string) fgets($peek);
            fclose($peek);
        }
        $delim = ',';
        if ($first_line !== '') {
            $commas    = substr_count($first_line, ',');
            $semis     = substr_count($first_line, ';');
            $tabs      = substr_count($first_line, "\t");
            if ($semis  > $commas && $semis  >= $tabs) { $delim = ';'; }
            elseif ($tabs > $commas && $tabs  >  $semis) { $delim = "\t"; }
        }

        $handle = fopen($tmp, 'r');
        if (!$handle) {
            wp_send_json_error(array('message' => __('Cannot read uploaded file.', 'ai-eshop-optimizer')));
        }

        
        $header = fgetcsv($handle, 0, $delim);
        if (!$header || !is_array($header) || count($header) < 2) {
            fclose($handle);
            wp_send_json_error(array('message' => __('CSV must have at least a sku column and one role column. (Detected separator: ', 'ai-eshop-optimizer') . ($delim === "\t" ? 'TAB' : $delim) . ')'));
        }
        $header_map = array();
        foreach ($header as $idx => $col) {
            
            
            $clean = strtolower(trim(str_replace("\xEF\xBB\xBF", '', (string) $col)));
            $header_map[$clean] = $idx;
        }
        if (!isset($header_map['sku'])) {
            $detected = $delim === "\t" ? 'TAB' : $delim;
            $found    = implode(', ', array_keys($header_map));
            fclose($handle);
            wp_send_json_error(array(
                'message' => sprintf(
                    
                    __('CSV must include a sku column. Detected separator: %1$s. Columns found in header: %2$s', 'ai-eshop-optimizer'),
                    $detected,
                    $found
                ),
            ));
        }

        $sku_idx = $header_map['sku'];
        unset($header_map['sku']);

        
        $enabled_roles = AIEO_DMM_Settings::get('enabled_roles', array());
        $role_map = array();
        foreach ($header_map as $name => $idx) {
            foreach ($enabled_roles as $role) {
                if (strtolower($role) === $name) {
                    $role_map[$role] = $idx;
                    break;
                }
            }
        }

        if (empty($role_map)) {
            fclose($handle);
            wp_send_json_error(array('message' => __('No CSV column matches an enabled role. Enable roles first.', 'ai-eshop-optimizer')));
        }

        $rows = array();
        while (($row = fgetcsv($handle, 0, $delim)) !== false) {
            if (!is_array($row)) {
                continue;
            }
            $sku = isset($row[$sku_idx]) ? trim((string) $row[$sku_idx]) : '';
            if ($sku === '') {
                continue;
            }
            $prices = array();
            foreach ($role_map as $role => $idx) {
                $v = isset($row[$idx]) ? (string) $row[$idx] : '';
                $v = trim(str_replace(',', '.', $v));
                if ($v === '' || !is_numeric($v) || (float) $v <= 0) {
                    continue;
                }
                $prices[$role] = (float) $v;
            }
            if (!empty($prices)) {
                $rows[] = array('sku' => $sku, 'prices' => $prices);
            }
        }
        fclose($handle);

        if (empty($rows)) {
            wp_send_json_error(array('message' => __('CSV had no valid rows.', 'ai-eshop-optimizer')));
        }

        $job_id = wp_generate_password(16, false, false);
        $this->create_job($job_id, 'role_pricing_import', count($rows), array(
            'mode' => $mode,
            'rows' => $rows,
            'roles' => array_keys($role_map),
        ));

        wp_send_json_success(array(
            'job_id' => $job_id,
            'total' => count($rows),
            'batch_size' => 100,
        ));
    }

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

        $job_id = isset($_POST['job_id']) ? sanitize_text_field(wp_unslash($_POST['job_id'])) : '';
        $job    = $this->load_job($job_id);
        if (!$job || $job['type'] !== 'role_pricing_import') {
            wp_send_json_error(array('message' => __('Invalid job.', 'ai-eshop-optimizer')));
        }

        $params = json_decode($job['params'], true);
        if (!is_array($params) || !isset($params['rows']) || !is_array($params['rows'])) {
            wp_send_json_error(array('message' => __('Corrupted job state.', 'ai-eshop-optimizer')));
        }

        $mode       = isset($params['mode']) && $params['mode'] === 'overwrite' ? 'overwrite' : 'merge';
        $batch_size = 100;
        $offset     = (int) $job['processed'];

        
        if ($offset === 0 && $mode === 'overwrite') {
            global $wpdb;
            $t_new = $wpdb->prefix . 'aieo_dmm_role_prices';
            $t_old = $wpdb->prefix . 'om_role_based_pricing';
            
            $wpdb->query("TRUNCATE TABLE {$t_new}");
            if (aieo_dmm_table_exists($t_old)) {
                
                $wpdb->query("TRUNCATE TABLE {$t_old}");
            }
        }

        $slice = array_slice($params['rows'], $offset, $batch_size);
        $errors = array();
        $written = 0;

        foreach ($slice as $row) {
            $result = $this->write_row($row['sku'], $row['prices']);
            if ($result['ok']) {
                $written++;
            } else {
                $errors[] = $result['error'];
            }
        }

        $new_processed = $offset + count($slice);
        $complete = $new_processed >= (int) $job['total'];

        $this->update_job($job_id, array(
            'processed' => $new_processed,
            'status'    => $complete ? 'complete' : 'running',
        ));

        if ($complete) {
            AIEO_DMM_Cache::bump_version('prices');
        }

        wp_send_json_success(array(
            'processed'  => $new_processed,
            'total'      => (int) $job['total'],
            'written'    => $written,
            'errors'     => array_slice($errors, 0, 20),
            'complete'   => $complete,
            'percentage' => (int) $job['total'] > 0 ? (int) floor(($new_processed / (int) $job['total']) * 100) : 100,
        ));
    }

    
    private function write_row($sku, array $prices) {
        global $wpdb;

        $product_id = function_exists('wc_get_product_id_by_sku') ? (int) wc_get_product_id_by_sku($sku) : 0;
        if ($product_id <= 0) {
            return array('ok' => false, 'error' => sprintf(
                
                __('SKU %s not found.', 'ai-eshop-optimizer'),
                $sku
            ));
        }

        $parent_id = $product_id;
        $type = 'simple';
        $post = get_post($product_id);
        if ($post && $post->post_type === 'product_variation') {
            $parent_id = (int) $post->post_parent;
            $type = 'variation';
        }

        $row_base = array(
            'parent_id'    => $parent_id,
            'product_type' => $type,
            'sku'          => (string) $sku,
        );
        $row_formats = array('%d', '%s', '%s');

        foreach ($prices as $role => $price) {
            $col = aieo_dmm_sanitize_role_column((string) $role);
            $row_base[$col] = (float) $price;
            $row_formats[] = '%f';
        }

        $tables = array(
            $wpdb->prefix . 'aieo_dmm_role_prices',
            $wpdb->prefix . 'om_role_based_pricing',
        );

        foreach ($tables as $table) {
            if (!aieo_dmm_table_exists($table)) {
                continue;
            }
            $exists = $wpdb->get_var($wpdb->prepare(
                
                "SELECT 1 FROM {$table} WHERE product_id = %d LIMIT 1",
                $product_id
            ));

            if ($exists) {
                $wpdb->update($table, $row_base, array('product_id' => $product_id), $row_formats, array('%d'));
            } else {
                $row_with_pk = array('product_id' => $product_id) + $row_base;
                $formats_with_pk = array_merge(array('%d'), $row_formats);
                $wpdb->insert($table, $row_with_pk, $formats_with_pk);
            }
        }
        return array('ok' => true);
    }

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

        global $wpdb;
        $table = $wpdb->prefix . 'aieo_dmm_role_prices';
        if (!aieo_dmm_table_exists($table)) {
            wp_send_json_error(array('message' => __('No role pricing data yet.', 'ai-eshop-optimizer')));
        }

        $enabled_roles = AIEO_DMM_Settings::get('enabled_roles', array());
        if (empty($enabled_roles)) {
            wp_send_json_error(array('message' => __('Enable at least one role first.', 'ai-eshop-optimizer')));
        }

        $cols = array('sku');
        foreach ($enabled_roles as $role) {
            $cols[] = $role;
        }

        $db_cols = array('sku');
        foreach ($enabled_roles as $role) {
            $db_cols[] = aieo_dmm_sanitize_role_column($role);
        }

        $select = implode(', ', array_map(function ($c) { return '`' . $c . '`'; }, $db_cols));
        
        $rows = $wpdb->get_results("SELECT {$select} FROM {$table} ORDER BY product_id ASC", ARRAY_N);

        $dir = AIEO_DMM::get_upload_path('exports');
        $filename = 'role-prices-' . gmdate('Ymd-His') . '.csv';
        $path = trailingslashit($dir['path']) . $filename;
        $url  = trailingslashit($dir['url']) . $filename;

        $handle = fopen($path, 'w');
        if (!$handle) {
            wp_send_json_error(array('message' => __('Cannot write export file.', 'ai-eshop-optimizer')));
        }
        
        
        
        
        
        
        
        
        
        
        fputcsv($handle, $cols);
        foreach ((array) $rows as $row) {
            fputcsv($handle, $row);
        }
        fclose($handle);

        wp_send_json_success(array(
            'url'      => $url,
            'filename' => $filename,
            'rows'     => is_array($rows) ? count($rows) : 0,
        ));
    }

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

        $roles = AIEO_DMM_Settings::get('enabled_roles', array());
        $cols  = array_merge(array('sku'), $roles);

        
        $samples = $this->build_template_sample_rows($roles, 3);

        $dir = AIEO_DMM::get_upload_path('exports');
        $filename = 'role-prices-template.csv';
        $path = trailingslashit($dir['path']) . $filename;
        $url  = trailingslashit($dir['url']) . $filename;

        $handle = fopen($path, 'w');
        if (!$handle) {
            wp_send_json_error(array('message' => __('Cannot write template file.', 'ai-eshop-optimizer')));
        }
        AIEO_DMM_Sample::write_csv_bom($handle);
        fputcsv($handle, $cols);
        foreach ($samples as $row) {
            fputcsv($handle, $row);
        }
        fclose($handle);

        wp_send_json_success(array('url' => $url, 'filename' => $filename));
    }

    
    private function build_template_sample_rows(array $roles, $count = 3) {
        $rows = array();

        $product_skus = array();
        if (function_exists('wc_get_products')) {
            $products = wc_get_products(array(
                'status'  => 'publish',
                'limit'   => max(1, (int) $count),
                'orderby' => 'date',
                'order'   => 'DESC',
            ));
            foreach ($products as $p) {
                $sku = (string) $p->get_sku();
                if ($sku !== '') {
                    $product_skus[] = $sku;
                }
                if (count($product_skus) >= (int) $count) {
                    break;
                }
            }
        }
        if (empty($product_skus)) {
            $product_skus = array('EXAMPLE-SKU-1', 'EXAMPLE-SKU-2', 'EXAMPLE-SKU-3');
        }

        
        $base_prices = array(19.99, 24.50, 9.90);

        foreach ($product_skus as $idx => $sku) {
            $row = array($sku);
            $base = isset($base_prices[$idx]) ? $base_prices[$idx] : 9.99;
            foreach ($roles as $n => $role) {
                
                
                $price = max(0.01, $base - ($n * 1.5));
                $row[] = number_format($price, 2, '.', '');
            }
            $rows[] = $row;
            if (count($rows) >= (int) $count) {
                break;
            }
        }

        return $rows;
    }

    
    
    

    
    private function count_price_rows() {
        global $wpdb;
        $table = $wpdb->prefix . 'aieo_dmm_role_prices';
        if (!aieo_dmm_table_exists($table)) {
            return 0;
        }
        
        return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
    }

    
    private function priced_rows_where_clause() {
        $roles = (array) AIEO_DMM_Settings::get('enabled_roles', array());
        if (empty($roles)) { return '1=0'; }
        $parts = array();
        foreach ($roles as $role) {
            $col = aieo_dmm_sanitize_role_column($role);
            
            $parts[] = "(`{$col}` IS NOT NULL AND `{$col}` > 0)";
        }
        return '(' . implode(' OR ', $parts) . ')';
    }

    
    private function count_priced_rows() {
        global $wpdb;
        $table = $wpdb->prefix . 'aieo_dmm_role_prices';
        if (!aieo_dmm_table_exists($table)) {
            return 0;
        }
        $where = $this->priced_rows_where_clause();
        
        return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE {$where}");
    }

    
    private function fetch_priced_rows($per_page, $page) {
        global $wpdb;
        $table = $wpdb->prefix . 'aieo_dmm_role_prices';
        if (!aieo_dmm_table_exists($table)) {
            return array();
        }
        $where  = $this->priced_rows_where_clause();
        $offset = max(0, ((int) $page - 1) * (int) $per_page);
        
        $sql = $wpdb->prepare(
            "SELECT * FROM {$table} WHERE {$where} ORDER BY product_id ASC LIMIT %d OFFSET %d",
            (int) $per_page,
            $offset
        );
        $rows = $wpdb->get_results($sql, ARRAY_A);
        return is_array($rows) ? $rows : array();
    }

    
    private function fetch_price_rows($per_page, $page) {
        global $wpdb;
        $table = $wpdb->prefix . 'aieo_dmm_role_prices';
        if (!aieo_dmm_table_exists($table)) {
            return array();
        }
        $offset = max(0, ((int) $page - 1) * (int) $per_page);
        
        $sql = $wpdb->prepare(
            "SELECT * FROM {$table} ORDER BY product_id ASC LIMIT %d OFFSET %d",
            (int) $per_page,
            $offset
        );
        $rows = $wpdb->get_results($sql, ARRAY_A);
        return is_array($rows) ? $rows : array();
    }

    
    
    

    
    private function create_job($job_id, $type, $total, array $params) {
        global $wpdb;
        $table = $wpdb->prefix . 'aieo_dmm_export_jobs';
        if (!aieo_dmm_table_exists($table)) {
            return;
        }
        $wpdb->insert($table, array(
            'job_id'    => $job_id,
            'type'      => $type,
            'status'    => 'running',
            'total'     => $total,
            'processed' => 0,
            'params'    => wp_json_encode($params),
            'user_id'   => get_current_user_id(),
        ), array('%s', '%s', '%s', '%d', '%d', '%s', '%d'));
    }

    
    private function load_job($job_id) {
        global $wpdb;
        if ($job_id === '') {
            return null;
        }
        $table = $wpdb->prefix . 'aieo_dmm_export_jobs';
        
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$table} WHERE job_id = %s",
            $job_id
        ), ARRAY_A);
        return is_array($row) ? $row : null;
    }

    
    private function update_job($job_id, array $fields) {
        global $wpdb;
        $table = $wpdb->prefix . 'aieo_dmm_export_jobs';
        $formats = array();
        foreach ($fields as $k => $v) {
            $formats[] = in_array($k, array('processed', 'total', 'last_id', 'user_id'), true) ? '%d' : '%s';
        }
        $wpdb->update($table, $fields, array('job_id' => $job_id), $formats, array('%s'));
    }

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

        if (!class_exists('GFAPI') || !class_exists('GFExport')) {
            wp_send_json_error(array('message' => __('Gravity Forms is not active on this site.', 'ai-eshop-optimizer')));
        }

        $ids = array();
        foreach (array('gf_registration_form_id', 'gf_update_form_id', 'gf_prompt_form_id') as $k) {
            $id = (int) AIEO_DMM_Settings::get($k, 0);
            if ($id > 0) { $ids[$id] = true; }
        }
        $ids = array_keys($ids);
        if (empty($ids)) {
            wp_send_json_error(array('message' => __('Configure at least one form ID first.', 'ai-eshop-optimizer')));
        }

        
        $forms = array();
        foreach ($ids as $id) {
            $form = GFAPI::get_form((int) $id);
            if (is_array($form)) {
                $forms[] = $form;
            }
        }
        if (empty($forms)) {
            wp_send_json_error(array('message' => __('Could not resolve any of the configured forms.', 'ai-eshop-optimizer')));
        }

        
        
        
        
        $payload = array();
        $payload['version'] = defined('GFForms::$version') ? GFForms::$version : '2.7';
        $payload['aieo_export_meta'] = array(
            'exported_at'              => gmdate('c'),
            'gf_registration_form_id'  => (int) AIEO_DMM_Settings::get('gf_registration_form_id', 0),
            'gf_update_form_id'        => (int) AIEO_DMM_Settings::get('gf_update_form_id', 0),
            'gf_prompt_form_id'        => (int) AIEO_DMM_Settings::get('gf_prompt_form_id', 0),
            'gf_checkbox_field_id'     => (int) AIEO_DMM_Settings::get('gf_checkbox_field_id', 11),
            'gf_role_map'              => (array) AIEO_DMM_Settings::get('gf_role_map', array()),
        );
        foreach ($forms as $i => $form) {
            $payload[(string) $i] = $form;
        }

        $dir = AIEO_DMM::get_upload_path('exports');
        $filename = 'gf-membership-forms-' . gmdate('Ymd-His') . '.json';
        $path = trailingslashit($dir['path']) . $filename;
        $url  = trailingslashit($dir['url']) . $filename;

        $written = file_put_contents($path, wp_json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
        if ($written === false) {
            wp_send_json_error(array('message' => __('Could not write export file.', 'ai-eshop-optimizer')));
        }

        wp_send_json_success(array(
            'url'      => $url,
            'filename' => $filename,
            'forms'    => count($forms),
        ));
    }

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    
    private function gf_inverted_role_map() {
        $map = (array) AIEO_DMM_Settings::get('gf_role_map', array());
        $out = array();
        foreach ($map as $slug => $value) {
            $int_val = (int) $value;
            if ($int_val > 0 && (string) $slug !== '') {
                $out[$int_val] = (string) $slug;
            }
        }
        return $out;
    }

    
    private function gf_collected_checkbox_values($entry) {
        $field_id = (int) AIEO_DMM_Settings::get('gf_checkbox_field_id', 11);
        if ($field_id <= 0 || !is_array($entry)) {
            return array();
        }
        $out = array();
        
        
        for ($i = 1; $i <= 30; $i++) {
            $key = $field_id . '.' . $i;
            if (!isset($entry[$key])) {
                continue;
            }
            $raw = trim((string) $entry[$key]);
            if ($raw === '') {
                continue;
            }
            $iv = (int) $raw;
            if ($iv > 0) {
                $out[] = $iv;
            }
        }
        return $out;
    }

    
    public function gf_assign_roles_on_registration($user_id, $user_config, $entry) {
        $form_id = (int) AIEO_DMM_Settings::get('gf_registration_form_id', 0);
        if ($form_id <= 0) { return; }
        if (!is_array($entry) || (int) (isset($entry['form_id']) ? $entry['form_id'] : 0) !== $form_id) {
            return;
        }

        $user = get_userdata($user_id);
        if (!$user) { return; }

        $inv = $this->gf_inverted_role_map();
        if (empty($inv)) { return; }

        foreach ($this->gf_collected_checkbox_values($entry) as $val) {
            if (isset($inv[$val])) {
                $user->add_role($inv[$val]);
            }
        }
    }

    
    public function gf_modify_roles_on_update($entry, $form) {
        $form_id = (int) AIEO_DMM_Settings::get('gf_update_form_id', 0);
        if ($form_id <= 0) { return; }
        if (!is_array($form) || (int) $form['id'] !== $form_id) { return; }

        $user = wp_get_current_user();
        if (!$user || !$user->ID) { return; }

        $inv = $this->gf_inverted_role_map();
        if (empty($inv)) { return; }

        foreach ($inv as $existing_role) {
            if (in_array($existing_role, (array) $user->roles, true)) {
                $user->remove_role($existing_role);
            }
        }
        foreach ($this->gf_collected_checkbox_values($entry) as $val) {
            if (isset($inv[$val])) {
                $user->add_role($inv[$val]);
            }
        }

        delete_transient('aieo_pre_gf_roles_' . $user->ID);
    }

    
    public function gf_emit_program_prompt($entry, $form) {
        $form_id = (int) AIEO_DMM_Settings::get('gf_prompt_form_id', 0);
        if ($form_id <= 0) { return; }
        if (!is_array($form) || (int) $form['id'] !== $form_id) { return; }

        $pro_field    = (int) AIEO_DMM_Settings::get('gf_prompt_pro_field_id',    51);
        $member_field = (int) AIEO_DMM_Settings::get('gf_prompt_member_field_id', 48);
        $page_id      = (int) AIEO_DMM_Settings::get('gf_professionals_page_id',  0);
        if ($page_id <= 0) { return; }

        $is_pro       = (string) (isset($entry[$pro_field])    ? $entry[$pro_field]    : '') === '1';
        $not_in_prog  = empty($entry[$member_field]) || (string) $entry[$member_field] === '0';
        if (!$is_pro || !$not_in_prog) { return; }

        $url = get_permalink($page_id);
        if (!$url) { return; }

        echo "<div style='display:grid;place-items:center;justify-content:center;align-items:center;'><div style='text-align:center;'><h2>"
            . esc_html__('You\'re a professional but not yet enrolled in the program — ', 'ai-eshop-optimizer')
            . '<a href="' . esc_url($url) . '">'
            . esc_html__('join here', 'ai-eshop-optimizer')
            . '</a></h2></div></div>';
    }

    
    public function gf_capture_pre_submission_roles() {
        if (!is_user_logged_in()) { return; }
        $page_id = (int) AIEO_DMM_Settings::get('gf_professionals_page_id', 0);
        if ($page_id <= 0 || !is_page($page_id)) { return; }

        $user = wp_get_current_user();
        if (!$user || !$user->ID) { return; }
        set_transient('aieo_pre_gf_roles_' . $user->ID, (array) $user->roles, 20000);
    }

    
    public function gf_print_post_login_refresh() {
        $page_id = (int) AIEO_DMM_Settings::get('gf_professionals_page_id', 0);
        if ($page_id <= 0 || !is_page($page_id)) { return; }
        if (!isset($_GET['just_logged_in'])) { return; } 
        ?>
        <script>
            if (window.location.href.indexOf("just_logged_in=true") > -1) {
                var u = new URL(window.location.href);
                u.searchParams.delete("just_logged_in");
                window.location.replace(u.toString());
            }
        </script>
        <?php
    }

    
    public function gf_send_no_cache_headers_on_membership_page() {
        if (is_admin()) { return; }
        $page_id = (int) AIEO_DMM_Settings::get('gf_professionals_page_id', 0);
        if ($page_id <= 0) { return; }
        
        
        if (function_exists('headers_sent') && headers_sent()) { return; }
        
        $page = get_post($page_id);
        if (!$page || !isset($page->post_name) || $page->post_name === '') { return; }
        $uri  = isset($_SERVER['REQUEST_URI']) ? (string) sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
        if ($uri === '' || strpos($uri, '/' . $page->post_name) === false) { return; }
        header('Cache-Control: no-cache, must-revalidate, max-age=0');
        header('Pragma: no-cache');
        header('Expires: Wed, 11 Jan 1984 05:00:00 GMT');
    }

    
    public function gf_membership_shortcode() {
        if (!function_exists('gravity_form')) {
            return '';
        }

        $reg_form_id    = (int) AIEO_DMM_Settings::get('gf_registration_form_id', 0);
        $update_form_id = (int) AIEO_DMM_Settings::get('gf_update_form_id', 0);
        if ($reg_form_id <= 0 && $update_form_id <= 0) {
            return '';
        }

        if (is_user_logged_in()) {
            if ($update_form_id <= 0) { return ''; }
            $user = wp_get_current_user();
            $map  = (array) AIEO_DMM_Settings::get('gf_role_map', array());

            $values = array();
            foreach ((array) $user->roles as $role) {
                if (isset($map[$role]) && (int) $map[$role] > 0) {
                    $values[] = (int) $map[$role];
                }
            }

            $intro = (string) AIEO_DMM_Settings::get('gf_intro_logged_in', '');
            $name  = trim($user->first_name . ' ' . $user->last_name);
            if ($name === '') { $name = $user->display_name; }
            $intro = str_replace(
                array('{user_name}', '{first_name}', '{last_name}'),
                array(esc_html($name), esc_html($user->first_name), esc_html($user->last_name)),
                $intro
            );

            ob_start();
            if ($intro !== '') {
                echo '<div class="aieo-gf-intro">' . wp_kses_post($intro) . '</div>';
            }
            echo gravity_form((int) $update_form_id, false, true, false, array( 
                'customer_email' => $user->user_email,
                'user_id'        => $user->ID,
                'first_name'     => $user->first_name,
                'last_name'      => $user->last_name,
                'user_name'      => $user->user_login,
                'nick_name'      => $user->nickname,
                'membership'     => implode(',', $values),
            ), true, 1, false);
            return ob_get_clean();
        }

        
        
        
        if ($reg_form_id <= 0) {
            
        }
        $intro = (string) AIEO_DMM_Settings::get('gf_intro_logged_out', '');
        $page_id    = (int) AIEO_DMM_Settings::get('gf_professionals_page_id', 0);
        $return_url = $page_id > 0 ? get_permalink($page_id) : home_url();
        if ($return_url) {
            $return_url = add_query_arg(array(
                'just_logged_in' => 'true',
                'ts'             => time(),
            ), $return_url);
        }
        $intro = str_replace('{return_url}', esc_url($return_url), $intro);

        ob_start();
        if ($intro !== '') {
            echo '<div class="aieo-gf-intro">' . wp_kses_post($intro) . '</div>';
        }
        if ($reg_form_id > 0) {
            echo gravity_form((int) $reg_form_id, false, true, false, null, true, 1, false); 
        }
        return ob_get_clean();
    }
}
