<?php

/**
 * EntitySelector Trait
 *
 * Universal entity targeting widget for PrestaShop admin controllers.
 * Supports group-based product targeting with include/exclude logic.
 *
 * Structure:
 * - Block (e.g., Products)
 *   - Group 1: Include by category "Hoodies" EXCEPT specific products "Blue Hoodie"
 *   - Group 2: Include by manufacturer "Nike"
 *
 * Logic:
 * - Within group: Include MINUS Exclude
 * - Between groups: OR (union)
 * - Between blocks: AND (intersection)
 *
 * Usage:
 * 1. Add `use EntitySelector;` to your admin controller
 * 2. Call `$this->initEntitySelector()` in setMedia() after parent::setMedia()
 * 3. Handle AJAX with `$this->handleEntitySelectorAjax()` in ajaxProcess*
 * 4. Use `$this->renderEntitySelectorWidget()` to render the HTML
 *
 * Asset paths can be configured by overriding getEntitySelectorAssetPath()
 * Translation handled via $this->transEntitySelector() - module must be available
 *
 * @author    mypresta.rocks <info@mypresta.rocks>
 * @copyright Copyright (c) mypresta.rocks
 * @license   MIT
 */

namespace MyPrestaRocks\EntitySelector;

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

use Db;
use DbQuery;
use Context;
use Tools;
use Shop;

// Extracted helper classes (lazy-loaded)
use MyPrestaRocks\EntitySelector\EntitySelector\ProductConditionResolver;
use MyPrestaRocks\EntitySelector\EntitySelector\EntityQueryHandler;
use MyPrestaRocks\EntitySelector\EntitySelector\EntitySelectorRenderer;
use MyPrestaRocks\EntitySelector\EntitySelector\EntitySearchEngine;
use MyPrestaRocks\EntitySelector\EntitySelector\EntityPreviewHandler;

trait EntitySelector
{
    /**
     * @var bool Whether target conditions assets have been loaded
     */
    protected $entitySelectorAssetsLoaded = false;

    /**
     * @var array Current render config (set during renderEntitySelectorHtml)
     */
    protected $entitySelectorRenderConfig = [];

    /**
     * Lazy-loaded helper class instances
     * These are populated on first access via getters
     */
    protected $productConditionResolver = null;
    protected $entityQueryHandler = null;
    protected $entitySelectorRenderer = null;
    protected $entitySearchEngine = null;
    protected $entityPreviewHandler = null;

    /**
     * Get ProductConditionResolver instance (lazy-loaded)
     *
     * @return ProductConditionResolver
     */
    protected function getProductConditionResolver()
    {
        if ($this->productConditionResolver === null) {
            $idLang = (int) Context::getContext()->language->id;
            $idShop = (int) Context::getContext()->shop->id;
            $this->productConditionResolver = new ProductConditionResolver($idLang, $idShop);
        }
        return $this->productConditionResolver;
    }

    /**
     * Get EntityQueryHandler instance (lazy-loaded)
     *
     * @return EntityQueryHandler
     */
    protected function getEntityQueryHandler()
    {
        if ($this->entityQueryHandler === null) {
            $idLang = (int) Context::getContext()->language->id;
            $idShop = (int) Context::getContext()->shop->id;
            $this->entityQueryHandler = new EntityQueryHandler($idLang, $idShop);
        }
        return $this->entityQueryHandler;
    }

    /**
     * Get EntitySelectorRenderer instance (lazy-loaded)
     *
     * @return EntitySelectorRenderer
     */
    protected function getEntitySelectorRenderer()
    {
        if ($this->entitySelectorRenderer === null) {
            $this->entitySelectorRenderer = new EntitySelectorRenderer(
                Context::getContext(),
                function ($string) {
                    return $this->transEntitySelector($string);
                }
            );
            // Set block definitions for rendering
            $this->entitySelectorRenderer->setBlockDefinitions($this->getTargetBlocks());
            // Set controller name for AJAX URLs if available
            if (property_exists($this, 'controller_name') && !empty($this->controller_name)) {
                $this->entitySelectorRenderer->setControllerName($this->controller_name);
            }
        }
        return $this->entitySelectorRenderer;
    }

    /**
     * Get EntitySearchEngine instance (lazy-loaded)
     *
     * @return EntitySearchEngine
     */
    protected function getEntitySearchEngine()
    {
        if ($this->entitySearchEngine === null) {
            $idLang = (int) Context::getContext()->language->id;
            $idShop = (int) Context::getContext()->shop->id;
            $this->entitySearchEngine = new EntitySearchEngine($idLang, $idShop);
        }
        return $this->entitySearchEngine;
    }

    /**
     * Get EntityPreviewHandler instance (lazy-loaded)
     *
     * @return EntityPreviewHandler
     */
    protected function getEntityPreviewHandler()
    {
        if ($this->entityPreviewHandler === null) {
            $idLang = (int) Context::getContext()->language->id;
            $idShop = (int) Context::getContext()->shop->id;
            $this->entityPreviewHandler = new EntityPreviewHandler($idLang, $idShop);
        }
        return $this->entityPreviewHandler;
    }

    /**
     * Get base path for target conditions assets from vendor package
     * Assets are loaded directly from the package - no manual copying needed
     *
     * @return string URL path ending with /
     */
    protected function getEntitySelectorAssetPath()
    {
        // Derive path from the calling module, not the trait file location.
        // __TRAIT__ reflection returns wherever PHP's autoloader first loaded the trait from,
        // which may be a completely different module. $this->module is always correct.
        $modulePath = $this->module->getLocalPath();
        $assetsDir = $modulePath . 'vendor/myprestarocks/prestashop-entity-selector/assets/';

        // Convert filesystem path to URL path relative to PS root
        $psRoot = _PS_ROOT_DIR_ . '/';
        $relativePath = str_replace($psRoot, '', $assetsDir);

        return __PS_BASE_URI__ . $relativePath;
    }

    /**
     * Translate a string for EntitySelector
     * Override this method to customize translation handling
     *
     * @param string $string String to translate
     * @return string Translated string
     */
    protected function transEntitySelector($string)
    {
        return $this->module->l($string, 'EntitySelector');
    }

    /**
     * Initialize target conditions assets
     */
    protected function initEntitySelector()
    {
        if ($this->entitySelectorAssetsLoaded) {
            return;
        }

        $assetPath = $this->getEntitySelectorAssetPath();

        // Cache-busting version — bump when assets change
        $v = '?v=' . filemtime(dirname(__DIR__) . '/assets/js/admin/entity-selector.js');

        // Load compiled CSS (SCSS-based, Bootstrap 4 compatible)
        $this->addCSS($assetPath . 'css/admin/entity-selector.css' . $v);

        // Note: modal.js is NOT loaded here - MPRAdminController already provides MPRModal
        // If using EntitySelector outside of MPRAdminController context, you may need to load modal separately

        $this->addJS($assetPath . 'js/admin/entity-selector.js' . $v);

        $this->entitySelectorAssetsLoaded = true;
    }
    protected function handleEntitySelectorAjax()
    {
        $action = Tools::getValue('action');
        $traitName = Tools::getValue('trait');

        if ($traitName !== 'EntitySelector') {
            return false;
        }

        switch ($action) {
            case 'searchTargetEntities':
                $this->ajaxSearchTargetEntities();
                return true;
            case 'getTargetEntitiesByIds':
                $this->ajaxGetTargetEntitiesByIds();
                return true;
            case 'getTargetEntitiesByIdsBulk':
                $this->ajaxGetTargetEntitiesByIdsBulk();
                return true;
            case 'previewEntitySelector':
                $this->ajaxPreviewEntitySelector();
                return true;
            case 'previewEntitySelectorBulk':
                $this->ajaxPreviewEntitySelectorBulk();
                return true;
            case 'getTargetFilterableAttributes':
                $this->ajaxGetTargetFilterableAttributes();
                return true;
            case 'getAttributeGroups':
                $this->ajaxGetAttributeGroups();
                return true;
            case 'getAttributeValues':
                $this->ajaxGetAttributeValues();
                return true;
            case 'getFeatureGroups':
                $this->ajaxGetFeatureGroups();
                return true;
            case 'countPatternMatches':
                $this->ajaxCountPatternMatches();
                return true;
            case 'previewPatternMatches':
                $this->ajaxPreviewPatternMatches();
                return true;
            case 'countConditionMatches':
                $this->ajaxCountConditionMatches();
                return true;
            case 'countConditionMatchesBulk':
                $this->ajaxCountConditionMatchesBulk();
                return true;
            case 'previewConditionItems':
                $this->ajaxPreviewConditionItems();
                return true;
            case 'previewGroupItems':
                $this->ajaxPreviewGroupItems();
                return true;
            case 'countGroupItems':
                $this->ajaxCountGroupItems();
                return true;
            case 'getCategoryTree':
                $this->ajaxGetCategoryTree();
                return true;
            case 'previewFilterGroupProducts':
                $this->ajaxPreviewFilterGroupProducts();
                return true;
            case 'previewCategoryProducts':
                $this->ajaxPreviewCategoryProducts();
                return true;
            case 'previewCategoryPages':
                $this->ajaxPreviewCategoryPages();
                return true;
            case 'getHolidaysPreview':
                $this->ajaxGetHolidaysPreview();
                return true;
            case 'getHolidaysForCountries':
                $this->ajaxGetHolidaysForCountries();
                return true;
            case 'getZonesForFilter':
                $this->ajaxGetZonesForFilter();
                return true;
        }

        return false;
    }

    /**
     * Extract pattern string from pattern value
     * Patterns can be either plain strings or arrays with ['pattern' => '...', 'caseSensitive' => bool]
     *
     * @param mixed $pattern Pattern value (string or array)
     * @return string The pattern string
     */
    protected function extractPatternString($pattern)
    {
        if (is_array($pattern)) {
            return isset($pattern['pattern']) ? (string) $pattern['pattern'] : '';
        }
        return (string) $pattern;
    }

    /**
     * AJAX: Count products matching a single condition (method + values)
     * Delegates to ProductConditionResolver
     */
    protected function ajaxCountConditionMatches()
    {
        $method = Tools::getValue('method', '');
        $valuesJson = Tools::getValue('values', '[]');
        $blockType = Tools::getValue('block_type', 'products');

        $values = json_decode($valuesJson, true);
        if (!is_array($values)) {
            $values = [];
        }

        try {
            $count = 0;
            if ($blockType === 'products') {
                $matchingIds = $this->getProductConditionResolver()->getIdsByMethod($method, $values);
                $count = count($matchingIds);
            }

            $this->ajaxDie(json_encode([
                'success' => true,
                'count' => $count,
            ]));
        } catch (\Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => $e->getMessage(),
                'count' => 0,
            ]));
        }
    }

    /**
     * AJAX: Count products matching multiple conditions in a single request
     * Receives an array of conditions and returns counts for each
     */
    protected function ajaxCountConditionMatchesBulk()
    {
        $startTime = microtime(true);
        $conditionsJson = Tools::getValue('conditions', '[]');
        $conditions = json_decode($conditionsJson, true);

        if (!is_array($conditions)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'Invalid conditions format',
                'counts' => [],
            ]));
            return;
        }

        try {
            $counts = [];
            $timing = [];

            foreach ($conditions as $conditionId => $condition) {
                $conditionStart = microtime(true);
                $method = $condition['method'] ?? '';
                $values = $condition['values'] ?? [];
                $blockType = $condition['block_type'] ?? 'products';

                if (empty($method)) {
                    $counts[$conditionId] = 0;
                    continue;
                }

                if ($blockType === 'products') {
                    $matchingIds = $this->getProductConditionResolver()->getIdsByMethod($method, $values);
                    $counts[$conditionId] = count($matchingIds);
                } else {
                    // For non-product entity types, use the query handler
                    $matchingIds = $this->getEntityQueryHandler()->getIdsByMethod($blockType, $method, $values);
                    $counts[$conditionId] = count($matchingIds);
                }
                $timing[$conditionId] = round((microtime(true) - $conditionStart) * 1000, 2);
            }

            $totalTime = round((microtime(true) - $startTime) * 1000, 2);

            $this->ajaxDie(json_encode([
                'success' => true,
                'counts' => $counts,
                'timing' => $timing,
                'total_time_ms' => $totalTime,
                'condition_count' => count($conditions),
            ]));
        } catch (\Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => $e->getMessage(),
                'counts' => [],
            ]));
        }
    }

    /**
     * AJAX: Preview items matching a single condition (method + values)
     * Delegates to ProductConditionResolver and EntityPreviewHandler
     */
    protected function ajaxPreviewConditionItems()
    {
        $method = Tools::getValue('method', '');
        $valuesJson = Tools::getValue('values', '[]');
        $blockType = Tools::getValue('block_type', 'products');
        $limit = (int) Tools::getValue('limit', 10);
        $offset = (int) Tools::getValue('offset', 0);
        $filter = Tools::getValue('filter', '');

        $values = json_decode($valuesJson, true);
        if (!is_array($values)) {
            $values = [];
        }

        $idLang = (int) $this->context->language->id;
        $idShop = (int) $this->context->shop->id;

        try {
            if ($blockType === 'products') {
                $matchingIds = $this->getProductConditionResolver()->getIdsByMethod($method, $values);

                // Apply filter if provided
                $previewHandler = $this->getEntityPreviewHandler();
                if (!empty($filter)) {
                    $matchingIds = $previewHandler->filterProductIdsByQuery($matchingIds, $filter, $idLang, $idShop);
                }

                $totalCount = count($matchingIds);

                $previewItems = [];
                if ($totalCount > 0 && $limit > 0) {
                    $previewIds = array_slice($matchingIds, $offset, $limit);
                    $previewItems = $previewHandler->getProductPreviewData($previewIds, $idLang, $idShop);
                }

                $this->ajaxDie(json_encode([
                    'success' => true,
                    'count' => $totalCount,
                    'items' => $previewItems,
                    'hasMore' => $totalCount > ($offset + $limit),
                ]));
            } else {
                $this->ajaxDie(json_encode([
                    'success' => false,
                    'error' => 'Unsupported block type for condition preview',
                    'count' => 0,
                    'items' => [],
                ]));
            }
        } catch (\Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => $e->getMessage(),
                'count' => 0,
                'items' => [],
            ]));
        }
    }

    /**
     * AJAX: Preview items matching a single group with modifiers applied
     * Delegates to ProductConditionResolver and EntityPreviewHandler
     */
    protected function ajaxPreviewGroupItems()
    {
        $groupDataJson = Tools::getValue('group_data', '{}');
        $blockType = Tools::getValue('block_type', 'products');
        $limit = (int) Tools::getValue('limit', 10);
        $offset = (int) Tools::getValue('offset', 0);
        $filter = Tools::getValue('filter', '');

        $groupData = json_decode($groupDataJson, true);
        if (!is_array($groupData) || !isset($groupData['include'])) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'Invalid group data',
                'count' => 0,
                'items' => [],
            ]));
            return;
        }

        $idLang = (int) $this->context->language->id;
        $idShop = (int) $this->context->shop->id;

        try {
            if ($blockType === 'products') {
                $resolver = $this->getProductConditionResolver();
                $previewHandler = $this->getEntityPreviewHandler();

                $includeMethod = $groupData['include']['method'] ?? 'all';
                $includeValues = $groupData['include']['values'] ?? [];

                // Check if this is a combination method in combinations mode
                $isCombinationMode = ($includeMethod === 'by_combination'
                    && isset($includeValues['mode'])
                    && $includeValues['mode'] === 'combinations');

                if ($isCombinationMode) {
                    $combinationIds = $resolver->getIdsByMethod($includeMethod, $includeValues);

                    // Apply excludes to combinations
                    if (!empty($groupData['excludes']) && is_array($groupData['excludes'])) {
                        foreach ($groupData['excludes'] as $exclude) {
                            $excludeMethod = $exclude['method'] ?? '';
                            $excludeValues = $exclude['values'] ?? [];
                            if ($excludeMethod && !empty($excludeValues)) {
                                $excludeIds = $resolver->getIdsByMethod($excludeMethod, $excludeValues);
                                $combinationIds = array_diff($combinationIds, $excludeIds);
                            }
                        }
                    }

                    // Convert to combination array format
                    $combinations = [];
                    if (!empty($combinationIds)) {
                        $combinationIds = array_map('intval', $combinationIds);
                        $results = Db::getInstance()->executeS('
                            SELECT pa.id_product, pa.id_product_attribute
                            FROM ' . _DB_PREFIX_ . 'product_attribute pa
                            WHERE pa.id_product_attribute IN (' . implode(',', $combinationIds) . ')
                        ');
                        foreach ($results ?: [] as $row) {
                            $combinations[] = [
                                'id_product' => (int) $row['id_product'],
                                'id_product_attribute' => (int) $row['id_product_attribute'],
                            ];
                        }
                    }

                    $totalCount = count($combinations);
                    $previewItems = [];
                    if ($totalCount > 0 && $limit > 0) {
                        $previewCombinations = array_slice($combinations, $offset, $limit);
                        $previewItems = $previewHandler->getCombinationPreviewData($previewCombinations, $idLang, $idShop);
                    }

                    $this->ajaxDie(json_encode([
                        'success' => true,
                        'count' => $totalCount,
                        'items' => $previewItems,
                        'hasMore' => $totalCount > ($offset + $limit),
                        'hasCombinations' => true,
                    ]));
                } else {
                    // Standard products mode
                    $matchingIds = $resolver->getIdsByMethod($includeMethod, $includeValues);

                    // Apply excludes
                    if (!empty($groupData['excludes']) && is_array($groupData['excludes'])) {
                        foreach ($groupData['excludes'] as $exclude) {
                            $excludeMethod = $exclude['method'] ?? '';
                            $excludeValues = $exclude['values'] ?? [];
                            if ($excludeMethod && !empty($excludeValues)) {
                                $excludeIds = $resolver->getIdsByMethod($excludeMethod, $excludeValues);
                                $matchingIds = array_diff($matchingIds, $excludeIds);
                            }
                        }
                    }

                    // Apply modifiers (sort, limit)
                    $modifiers = $groupData['modifiers'] ?? [];
                    if (!empty($modifiers['sort_by']) || !empty($modifiers['limit'])) {
                        $matchingIds = $resolver->applyModifiers($matchingIds, $modifiers);
                    }

                    // Apply filter if provided
                    if (!empty($filter)) {
                        $matchingIds = $previewHandler->filterProductIdsByQuery($matchingIds, $filter, $idLang, $idShop);
                    }

                    $totalCount = count($matchingIds);
                    $previewItems = [];
                    if ($totalCount > 0 && $limit > 0) {
                        $previewIds = array_slice($matchingIds, $offset, $limit);
                        $previewItems = $previewHandler->getProductPreviewData($previewIds, $idLang, $idShop);
                    }

                    $this->ajaxDie(json_encode([
                        'success' => true,
                        'count' => $totalCount,
                        'items' => $previewItems,
                        'hasMore' => $totalCount > ($offset + $limit),
                    ]));
                }
            } else {
                $this->ajaxDie(json_encode([
                    'success' => false,
                    'error' => 'Unsupported block type for group preview',
                    'count' => 0,
                    'items' => [],
                ]));
            }
        } catch (\Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => $e->getMessage(),
                'count' => 0,
                'items' => [],
            ]));
        }
    }

    /**
     * AJAX: Count items matching a group (include - excludes, before limit modifier)
     * Returns: include_count, exclude_count, final_count
     */
    protected function ajaxCountGroupItems()
    {
        $groupDataJson = Tools::getValue('group_data', '{}');
        $blockType = Tools::getValue('block_type', 'products');

        $groupData = json_decode($groupDataJson, true);
        if (!is_array($groupData) || !isset($groupData['include'])) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => 'Invalid group data',
                'include_count' => 0,
                'exclude_count' => 0,
                'final_count' => 0,
            ]));
            return;
        }

        try {
            if ($blockType === 'products') {
                $resolver = $this->getProductConditionResolver();

                $includeMethod = $groupData['include']['method'] ?? 'all';
                $includeValues = $groupData['include']['values'] ?? [];
                $matchingIds = $resolver->getIdsByMethod($includeMethod, $includeValues);
                $includeCount = count($matchingIds);

                $excludeCount = 0;
                if (!empty($groupData['excludes']) && is_array($groupData['excludes'])) {
                    foreach ($groupData['excludes'] as $exclude) {
                        $excludeMethod = $exclude['method'] ?? '';
                        $excludeValues = $exclude['values'] ?? [];
                        if ($excludeMethod && !empty($excludeValues)) {
                            $excludeIds = $resolver->getIdsByMethod($excludeMethod, $excludeValues);
                            $actuallyExcluded = array_intersect($matchingIds, $excludeIds);
                            $excludeCount += count($actuallyExcluded);
                            $matchingIds = array_diff($matchingIds, $excludeIds);
                        }
                    }
                }

                $finalCount = count($matchingIds);

                $this->ajaxDie(json_encode([
                    'success' => true,
                    'include_count' => $includeCount,
                    'exclude_count' => $excludeCount,
                    'final_count' => $finalCount,
                ]));
            } elseif ($blockType === 'countries') {
                // For countries, count selected countries (not holidays)
                $includeMethod = $groupData['include']['method'] ?? 'all';
                $includeValues = $groupData['include']['values'] ?? [];

                // For 'specific' method, count is the number of selected countries
                // For 'all', we'd need to count all active countries
                if ($includeMethod === 'specific') {
                    $includeCount = count($includeValues);
                } elseif ($includeMethod === 'all') {
                    // Count all active countries
                    $includeCount = (int) Db::getInstance()->getValue(
                        'SELECT COUNT(*) FROM ' . _DB_PREFIX_ . 'country WHERE active = 1'
                    );
                } else {
                    $includeCount = 0;
                }

                $excludeCount = 0;
                if (!empty($groupData['excludes']) && is_array($groupData['excludes'])) {
                    foreach ($groupData['excludes'] as $exclude) {
                        $excludeValues = $exclude['values'] ?? [];
                        $excludeCount += count($excludeValues);
                    }
                }

                $finalCount = max(0, $includeCount - $excludeCount);

                $this->ajaxDie(json_encode([
                    'success' => true,
                    'include_count' => $includeCount,
                    'exclude_count' => $excludeCount,
                    'final_count' => $finalCount,
                ]));
            } else {
                $this->ajaxDie(json_encode([
                    'success' => false,
                    'error' => 'Unsupported block type',
                    'include_count' => 0,
                    'exclude_count' => 0,
                    'final_count' => 0,
                ]));
            }
        } catch (\Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => $e->getMessage(),
                'include_count' => 0,
                'exclude_count' => 0,
                'final_count' => 0,
            ]));
        }
    }

    /**
     * AJAX: Preview products that have any value from a specific attribute or feature group
     * Used by filter panel toggle buttons
     */
    protected function ajaxPreviewFilterGroupProducts()
    {
        $groupId = (int) Tools::getValue('group_id');
        $groupType = Tools::getValue('group_type'); // 'attribute' or 'feature'
        $limit = (int) Tools::getValue('limit', 10);
        $filter = Tools::getValue('filter', '');

        if (!$groupId || !in_array($groupType, ['attribute', 'feature'])) {
            die(json_encode([
                'success' => false,
                'error' => 'Invalid parameters'
            ]));
        }

        $idLang = (int) Context::getContext()->language->id;
        $idShop = (int) Context::getContext()->shop->id;

        try {
            $db = Db::getInstance();

            if ($groupType === 'attribute') {
                // Get products with any attribute from this group
                $sql = new DbQuery();
                $sql->select('DISTINCT p.id_product');
                $sql->from('product', 'p');
                $sql->innerJoin('product_shop', 'ps', 'ps.id_product = p.id_product AND ps.id_shop = ' . $idShop);
                $sql->innerJoin('product_attribute', 'pa', 'pa.id_product = p.id_product');
                $sql->innerJoin('product_attribute_combination', 'pac', 'pac.id_product_attribute = pa.id_product_attribute');
                $sql->innerJoin('attribute', 'a', 'a.id_attribute = pac.id_attribute AND a.id_attribute_group = ' . $groupId);
                $sql->where('ps.active = 1');
            } else {
                // Get products with any feature value from this group
                $sql = new DbQuery();
                $sql->select('DISTINCT p.id_product');
                $sql->from('product', 'p');
                $sql->innerJoin('product_shop', 'ps', 'ps.id_product = p.id_product AND ps.id_shop = ' . $idShop);
                $sql->innerJoin('feature_product', 'fp', 'fp.id_product = p.id_product');
                $sql->innerJoin('feature_value', 'fv', 'fv.id_feature_value = fp.id_feature_value AND fv.id_feature = ' . $groupId);
                $sql->where('ps.active = 1');
            }

            // Get all matching product IDs
            $results = $db->executeS($sql);
            $productIds = array_column($results, 'id_product');

            // Apply filter if provided
            if (!empty($filter) && !empty($productIds)) {
                $productIds = $this->getEntityPreviewHandler()->filterProductIdsByQuery($productIds, $filter, $idLang, $idShop);
            }

            $totalCount = count($productIds);

            // Get limited results for preview
            $previewIds = array_slice($productIds, 0, $limit);

            // Get product details for preview
            $items = [];
            if (!empty($previewIds)) {
                $productSql = new DbQuery();
                $productSql->select('p.id_product, pl.name, p.reference, i.id_image, m.name as manufacturer');
                $productSql->from('product', 'p');
                $productSql->innerJoin('product_lang', 'pl', 'pl.id_product = p.id_product AND pl.id_lang = ' . $idLang . ' AND pl.id_shop = ' . $idShop);
                $productSql->leftJoin('image', 'i', 'i.id_product = p.id_product AND i.cover = 1');
                $productSql->leftJoin('manufacturer', 'm', 'm.id_manufacturer = p.id_manufacturer');
                $productSql->where('p.id_product IN (' . implode(',', array_map('intval', $previewIds)) . ')');

                $products = $db->executeS($productSql);

                // Map products by ID for ordering
                $productsById = [];
                foreach ($products as $product) {
                    $imageUrl = null;
                    if ($product['id_image']) {
                        $imageUrl = Context::getContext()->link->getImageLink(
                            Tools::link_rewrite($product['name']),
                            $product['id_product'] . '-' . $product['id_image'],
                            'small_default'
                        );
                    }

                    $productsById[(int) $product['id_product']] = [
                        'id' => (int) $product['id_product'],
                        'name' => $product['name'],
                        'reference' => $product['reference'],
                        'manufacturer' => $product['manufacturer'],
                        'image' => $imageUrl
                    ];
                }

                // Preserve order from previewIds
                foreach ($previewIds as $id) {
                    if (isset($productsById[(int) $id])) {
                        $items[] = $productsById[(int) $id];
                    }
                }
            }

            die(json_encode([
                'success' => true,
                'items' => $items,
                'count' => $totalCount,
                'hasMore' => $totalCount > count($items)
            ]));

        } catch (\Exception $e) {
            die(json_encode([
                'success' => false,
                'error' => $e->getMessage()
            ]));
        }
    }

    /**
     * AJAX: Preview products in a category
     * Used by tree view product count click
     */
    protected function ajaxPreviewCategoryProducts()
    {
        $categoryId = (int) Tools::getValue('category_id');
        $limit = (int) Tools::getValue('limit', 10);
        $offset = (int) Tools::getValue('offset', 0);
        $query = Tools::getValue('query', '');

        if (!$categoryId) {
            die(json_encode(['success' => false, 'error' => 'Invalid category ID']));
        }

        $idLang = (int) Context::getContext()->language->id;
        $idShop = (int) Context::getContext()->shop->id;

        try {
            $db = Db::getInstance();

            // Get products in category
            $sql = new DbQuery();
            $sql->select('DISTINCT p.id_product');
            $sql->from('product', 'p');
            $sql->innerJoin('product_shop', 'ps', 'ps.id_product = p.id_product AND ps.id_shop = ' . $idShop);
            $sql->innerJoin('category_product', 'cp', 'cp.id_product = p.id_product AND cp.id_category = ' . $categoryId);
            $sql->where('ps.active = 1');

            $results = $db->executeS($sql);
            $productIds = array_column($results, 'id_product');

            // Apply filter if provided
            if (!empty($query) && !empty($productIds)) {
                $productIds = $this->getEntityPreviewHandler()->filterProductIdsByQuery($productIds, $query, $idLang, $idShop);
            }

            $totalCount = count($productIds);

            // Get limited results for preview
            $previewIds = array_slice($productIds, $offset, $limit);

            // Get product details
            $items = [];
            if (!empty($previewIds)) {
                $productSql = new DbQuery();
                $productSql->select('p.id_product, pl.name, p.reference, i.id_image, m.name as manufacturer');
                $productSql->from('product', 'p');
                $productSql->innerJoin('product_lang', 'pl', 'pl.id_product = p.id_product AND pl.id_lang = ' . $idLang . ' AND pl.id_shop = ' . $idShop);
                $productSql->leftJoin('image', 'i', 'i.id_product = p.id_product AND i.cover = 1');
                $productSql->leftJoin('manufacturer', 'm', 'm.id_manufacturer = p.id_manufacturer');
                $productSql->where('p.id_product IN (' . implode(',', array_map('intval', $previewIds)) . ')');

                $products = $db->executeS($productSql);

                $productsById = [];
                foreach ($products as $product) {
                    $imageUrl = null;
                    if ($product['id_image']) {
                        $imageUrl = Context::getContext()->link->getImageLink(
                            Tools::link_rewrite($product['name']),
                            $product['id_product'] . '-' . $product['id_image'],
                            'small_default'
                        );
                    }

                    $productsById[(int) $product['id_product']] = [
                        'id' => (int) $product['id_product'],
                        'name' => $product['name'],
                        'reference' => $product['reference'],
                        'manufacturer' => $product['manufacturer'],
                        'image' => $imageUrl
                    ];
                }

                foreach ($previewIds as $id) {
                    if (isset($productsById[(int) $id])) {
                        $items[] = $productsById[(int) $id];
                    }
                }
            }

            die(json_encode([
                'success' => true,
                'items' => $items,
                'count' => $totalCount,
                'hasMore' => ($offset + count($items)) < $totalCount
            ]));

        } catch (\Exception $e) {
            die(json_encode(['success' => false, 'error' => $e->getMessage()]));
        }
    }

    /**
     * AJAX: Preview CMS pages in a CMS category
     * Used by tree view page count click
     */
    protected function ajaxPreviewCategoryPages()
    {
        $categoryId = (int) Tools::getValue('category_id');
        $limit = (int) Tools::getValue('limit', 10);
        $offset = (int) Tools::getValue('offset', 0);
        $query = Tools::getValue('query', '');

        if (!$categoryId) {
            die(json_encode(['success' => false, 'error' => 'Invalid category ID']));
        }

        $idLang = (int) Context::getContext()->language->id;
        $idShop = (int) Context::getContext()->shop->id;

        try {
            $db = Db::getInstance();

            // Get CMS pages in category
            $sql = new DbQuery();
            $sql->select('c.id_cms, cl.meta_title');
            $sql->from('cms', 'c');
            $sql->innerJoin('cms_shop', 'cs', 'cs.id_cms = c.id_cms AND cs.id_shop = ' . $idShop);
            $sql->innerJoin('cms_lang', 'cl', 'cl.id_cms = c.id_cms AND cl.id_lang = ' . $idLang . ' AND cl.id_shop = ' . $idShop);
            $sql->where('c.id_cms_category = ' . $categoryId);
            $sql->where('c.active = 1');

            if (!empty($query)) {
                $sql->where('cl.meta_title LIKE \'%' . pSQL($query) . '%\'');
            }

            $allPages = $db->executeS($sql);
            $totalCount = count($allPages);

            // Get limited results
            $pages = array_slice($allPages, $offset, $limit);

            $items = [];
            foreach ($pages as $page) {
                $items[] = [
                    'id' => (int) $page['id_cms'],
                    'name' => $page['meta_title']
                ];
            }

            die(json_encode([
                'success' => true,
                'items' => $items,
                'count' => $totalCount,
                'hasMore' => ($offset + count($items)) < $totalCount
            ]));

        } catch (\Exception $e) {
            die(json_encode(['success' => false, 'error' => $e->getMessage()]));
        }
    }

    /**
     * Apply modifiers (sort, limit) to product IDs
     * Delegates to ProductConditionResolver
     */
    protected function applyProductModifiers(array $productIds, array $modifiers, $idLang, $idShop)
    {
        return $this->getProductConditionResolver()->applyModifiers($productIds, $modifiers);
    }

    /**
     * Resolve a single product condition to matching IDs
     * Delegates to ProductConditionResolver
     */
    protected function resolveProductCondition($method, $values, $idLang, $idShop)
    {
        return $this->getProductConditionResolver()->getIdsByMethod($method, $values);
    }

    /**
     * AJAX: Count entities matching a pattern (for live preview)
     * Delegates to ProductConditionResolver
     */
    protected function ajaxCountPatternMatches()
    {
        $pattern = Tools::getValue('pattern', '');
        $field = Tools::getValue('field', 'name');
        $caseSensitive = (bool) Tools::getValue('case_sensitive', false);
        $entityType = Tools::getValue('entity_type', 'products');

        if (empty($pattern)) {
            $this->ajaxDie(json_encode([
                'success' => true,
                'count' => 0,
            ]));
            return;
        }

        $patternData = [
            ['pattern' => $pattern, 'caseSensitive' => $caseSensitive]
        ];

        try {
            $matchingIds = [];

            // Only products supported for pattern matching
            if ($entityType === 'products') {
                $resolver = $this->getProductConditionResolver();
                if ($field === 'reference') {
                    $matchingIds = $resolver->getProductIdsByReferencePattern($patternData);
                } else {
                    $matchingIds = $resolver->getProductIdsByNamePattern($patternData);
                }
            }

            $this->ajaxDie(json_encode([
                'success' => true,
                'count' => count($matchingIds),
            ]));
        } catch (\Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => $e->getMessage(),
                'count' => 0,
            ]));
        }
    }

    /**
     * AJAX: Preview entities matching a pattern
     * Delegates to ProductConditionResolver and EntityPreviewHandler
     */
    protected function ajaxPreviewPatternMatches()
    {
        $pattern = Tools::getValue('pattern', '');
        $entityType = Tools::getValue('entity_type', 'products');
        $caseSensitive = (bool) Tools::getValue('case_sensitive', false);
        $limit = (int) Tools::getValue('limit', 50);

        if (empty($pattern)) {
            $this->ajaxDie(json_encode([
                'success' => true,
                'items' => [],
            ]));
            return;
        }

        $idLang = (int) $this->context->language->id;
        $idShop = (int) $this->context->shop->id;

        $patternData = [
            ['pattern' => $pattern, 'caseSensitive' => $caseSensitive]
        ];

        try {
            $items = [];

            if ($entityType === 'products') {
                $resolver = $this->getProductConditionResolver();
                $matchingIds = $resolver->getProductIdsByNamePattern($patternData);
                if (!empty($matchingIds)) {
                    $previewIds = array_slice($matchingIds, 0, $limit);
                    $items = $this->getEntityPreviewHandler()->getProductPreviewData($previewIds, $idLang, $idShop);
                }
            }

            $this->ajaxDie(json_encode([
                'success' => true,
                'items' => $items,
                'count' => count($items),
            ]));
        } catch (\Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => $e->getMessage(),
                'items' => [],
            ]));
        }
    }

    /**
     * AJAX: Preview target conditions - count and list matching items
     * Delegates to EntityPreviewHandler
     */
    protected function ajaxPreviewEntitySelector()
    {
        $conditionsJson = Tools::getValue('conditions', '{}');
        $blockType = Tools::getValue('block_type', 'products');
        $limit = (int) Tools::getValue('limit', 10);
        $offset = (int) Tools::getValue('offset', 0);

        $conditions = json_decode($conditionsJson, true);
        if (!is_array($conditions)) {
            $conditions = [];
        }

        $groups = $conditions[$blockType]['groups'] ?? $conditions['groups'] ?? [];

        $result = $this->getEntityPreviewHandler()->preview($blockType, $groups, $limit, $offset);

        $this->ajaxDie(json_encode($result));
    }

    /**
     * AJAX: Get preview counts for all entity types in a single request
     */
    protected function ajaxPreviewEntitySelectorBulk()
    {
        $startTime = microtime(true);
        $conditionsJson = Tools::getValue('conditions', '{}');

        $conditions = json_decode($conditionsJson, true);
        if (!is_array($conditions)) {
            $conditions = [];
        }

        try {
            $timing = [];
            $counts = [];

            foreach ($conditions as $entityType => $conditionData) {
                $typeStart = microtime(true);
                $groups = $conditionData['groups'] ?? [];

                try {
                    $counts[$entityType] = $this->getEntityPreviewHandler()->getPreviewCount($entityType, $groups);
                } catch (\Exception $e) {
                    $counts[$entityType] = 0;
                }
                $timing[$entityType] = round((microtime(true) - $typeStart) * 1000, 2);
            }

            $totalTime = round((microtime(true) - $startTime) * 1000, 2);

            $this->ajaxDie(json_encode([
                'success' => true,
                'counts' => $counts,
                'timing' => $timing,
                'total_time_ms' => $totalTime,
                'entity_count' => count($conditions),
            ]));
        } catch (\Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'error' => $e->getMessage(),
                'counts' => [],
            ]));
        }
    }

    /**
     * AJAX: Search entities for target conditions
     */
    protected function ajaxSearchTargetEntities()
    {
        $entityType = Tools::getValue('entity_type', '');
        $query = trim(Tools::getValue('q', ''));
        $limit = (int) Tools::getValue('limit', 20);
        $offset = (int) Tools::getValue('offset', 0);

        // Sorting parameters
        $sortBy = Tools::getValue('sort_by', 'name');
        $sortDir = strtoupper(Tools::getValue('sort_dir', 'ASC'));
        if (!in_array($sortDir, ['ASC', 'DESC'])) {
            $sortDir = 'ASC';
        }

        $idLang = (int) $this->context->language->id;
        $idShop = (int) $this->context->shop->id;

        // Get attribute/feature filters for product search
        $filterAttributes = Tools::getValue('filter_attributes', '');
        $filterFeatures = Tools::getValue('filter_features', '');

        // Get refine query and negate flag
        $refine = trim(Tools::getValue('refine', ''));
        $refineNegate = (bool) Tools::getValue('refine_negate', 0);

        // Product-specific quick filters
        $filterInStock = (bool) Tools::getValue('filter_in_stock', 0);
        $filterDiscounted = (bool) Tools::getValue('filter_discounted', 0);
        $filterPriceMin = Tools::getValue('filter_price_min', '');
        $filterPriceMax = Tools::getValue('filter_price_max', '');

        // Entity-specific filters
        $filterProductCountMin = Tools::getValue('filter_product_count_min', '');
        $filterProductCountMax = Tools::getValue('filter_product_count_max', '');
        $filterDepth = Tools::getValue('filter_depth', '');
        $filterHasProducts = (bool) Tools::getValue('filter_has_products', 0);
        $filterHasDescription = (bool) Tools::getValue('filter_has_description', 0);
        $filterHasImage = (bool) Tools::getValue('filter_has_image', 0);
        $filterSalesMin = Tools::getValue('filter_sales_min', '');
        $filterSalesMax = Tools::getValue('filter_sales_max', '');
        $filterTurnoverMin = Tools::getValue('filter_turnover_min', '');
        $filterTurnoverMax = Tools::getValue('filter_turnover_max', '');
        $filterActive = (bool) Tools::getValue('filter_active', 0);
        $filterAttributeGroup = Tools::getValue('filter_attribute_group', '');
        $filterFeatureGroup = Tools::getValue('filter_feature_group', '');
        $filterDateAddFrom = Tools::getValue('filter_date_add_from', '');
        $filterDateAddTo = Tools::getValue('filter_date_add_to', '');
        $filterLastProductFrom = Tools::getValue('filter_last_product_from', '');
        $filterLastProductTo = Tools::getValue('filter_last_product_to', '');
        $filterIsColor = (bool) Tools::getValue('filter_is_color', 0);
        $filterIsCustom = (bool) Tools::getValue('filter_is_custom', 0);
        $filterIndexable = (bool) Tools::getValue('filter_indexable', 0);

        // Country-specific filters
        $filterHasHolidays = (bool) Tools::getValue('filter_has_holidays', 0);
        $filterContainsStates = (bool) Tools::getValue('filter_contains_states', 0);
        $filterZone = Tools::getValue('filter_zone', '');

        $filters = [
            'attributes' => $filterAttributes ? json_decode($filterAttributes, true) : [],
            'features' => $filterFeatures ? json_decode($filterFeatures, true) : [],
            'refine' => $refine,
            'refine_negate' => $refineNegate,
            // Product quick filters
            'in_stock' => $filterInStock,
            'discounted' => $filterDiscounted,
            'price_min' => $filterPriceMin !== '' ? (float) $filterPriceMin : null,
            'price_max' => $filterPriceMax !== '' ? (float) $filterPriceMax : null,
            // Entity-specific
            'product_count_min' => $filterProductCountMin !== '' ? (int) $filterProductCountMin : null,
            'product_count_max' => $filterProductCountMax !== '' ? (int) $filterProductCountMax : null,
            'depth' => $filterDepth !== '' ? (int) $filterDepth : null,
            'has_products' => $filterHasProducts,
            'has_description' => $filterHasDescription,
            'has_image' => $filterHasImage,
            'sales_min' => $filterSalesMin !== '' ? (int) $filterSalesMin : null,
            'sales_max' => $filterSalesMax !== '' ? (int) $filterSalesMax : null,
            'turnover_min' => $filterTurnoverMin !== '' ? (float) $filterTurnoverMin : null,
            'turnover_max' => $filterTurnoverMax !== '' ? (float) $filterTurnoverMax : null,
            'active_only' => $filterActive,
            'attribute_group' => $filterAttributeGroup !== '' ? (int) $filterAttributeGroup : null,
            'feature_group' => $filterFeatureGroup !== '' ? (int) $filterFeatureGroup : null,
            'date_add_from' => $filterDateAddFrom !== '' ? $filterDateAddFrom : null,
            'date_add_to' => $filterDateAddTo !== '' ? $filterDateAddTo : null,
            'last_product_from' => $filterLastProductFrom !== '' ? $filterLastProductFrom : null,
            'last_product_to' => $filterLastProductTo !== '' ? $filterLastProductTo : null,
            'is_color' => $filterIsColor,
            'is_custom' => $filterIsCustom,
            'indexable' => $filterIndexable,
            // Country-specific
            'has_holidays' => $filterHasHolidays,
            'contains_states' => $filterContainsStates,
            'zone' => $filterZone !== '' ? (int) $filterZone : null,
            // Sorting
            'sort_by' => $sortBy,
            'sort_dir' => $sortDir,
        ];

        // Delegate to EntitySearchEngine
        $searchEngine = $this->getEntitySearchEngine();
        $results = $searchEngine->search($entityType, $query, $limit, $offset, $filters);
        $total = $searchEngine->count($entityType, $query, $filters);

        $this->ajaxDie(json_encode([
            'success' => true,
            'results' => $results,
            'total' => $total,
            'offset' => $offset,
            'limit' => $limit,
        ]));
    }

    /**
     * AJAX: Get entities by IDs
     * Delegates to EntitySearchEngine
     */
    protected function ajaxGetTargetEntitiesByIds()
    {
        $entityType = Tools::getValue('entity_type', '');
        $ids = Tools::getValue('ids', '');
        if (is_string($ids)) {
            $ids = json_decode($ids, true);
        }
        if (!is_array($ids)) {
            $ids = [];
        }

        // Delegate to EntitySearchEngine
        $entities = $this->getEntitySearchEngine()->getByIds($entityType, $ids);

        $this->ajaxDie(json_encode([
            'success' => true,
            'entities' => $entities,
        ]));
    }

    /**
     * AJAX: Get entities by IDs in bulk (multiple entity types in one request)
     * Accepts: entities = { "products": [1,2,3], "categories": [4,5], ... }
     * Returns: { "products": [...], "categories": [...], ... }
     */
    protected function ajaxGetTargetEntitiesByIdsBulk()
    {
        $entitiesParam = Tools::getValue('entities', '');
        \PrestaShopLogger::addLog('[EntitySelector] ajaxGetTargetEntitiesByIdsBulk called, raw param: ' . substr($entitiesParam, 0, 500), 1);

        if (is_string($entitiesParam)) {
            $entitiesParam = json_decode($entitiesParam, true);
        }
        if (!is_array($entitiesParam)) {
            $entitiesParam = [];
        }

        \PrestaShopLogger::addLog('[EntitySelector] Parsed entities param: ' . json_encode($entitiesParam), 1);

        $result = [];
        $searchEngine = $this->getEntitySearchEngine();

        foreach ($entitiesParam as $entityType => $ids) {
            if (!is_array($ids) || empty($ids)) {
                \PrestaShopLogger::addLog('[EntitySelector] Skipping entityType=' . $entityType . ' - empty or not array', 1);
                continue;
            }
            // Deduplicate and sanitize IDs
            $ids = array_unique(array_map('intval', $ids));
            \PrestaShopLogger::addLog('[EntitySelector] Fetching entityType=' . $entityType . ' ids=' . implode(',', $ids), 1);
            $result[$entityType] = $searchEngine->getByIds($entityType, $ids);
            \PrestaShopLogger::addLog('[EntitySelector] Got ' . count($result[$entityType]) . ' results for ' . $entityType, 1);
        }

        $this->ajaxDie(json_encode([
            'success' => true,
            'entities' => $result,
        ]));
    }

    /**
     * Get target blocks configuration
     */
    protected function getTargetBlocks()
    {
        return [
            'products' => [
                'label' => $this->transEntitySelector('Products'),
                'icon' => 'inventory',
                'entity_label' => $this->transEntitySelector('product'),
                'entity_label_plural' => $this->transEntitySelector('products'),
                'selection_methods' => $this->getProductSelectionMethods(),
            ],
            'categories' => [
                'label' => $this->transEntitySelector('Categories'),
                'icon' => 'folder_open',
                'entity_label' => $this->transEntitySelector('category'),
                'entity_label_plural' => $this->transEntitySelector('categories'),
                'selection_methods' => $this->getCategorySelectionMethods(),
            ],
            'manufacturers' => [
                'label' => $this->transEntitySelector('Manufacturers'),
                'icon' => 'business',
                'entity_label' => $this->transEntitySelector('manufacturer'),
                'entity_label_plural' => $this->transEntitySelector('manufacturers'),
                'selection_methods' => $this->getManufacturerSelectionMethods(),
            ],
            'suppliers' => [
                'label' => $this->transEntitySelector('Suppliers'),
                'icon' => 'inventory_2',
                'entity_label' => $this->transEntitySelector('supplier'),
                'entity_label_plural' => $this->transEntitySelector('suppliers'),
                'selection_methods' => $this->getSupplierSelectionMethods(),
            ],
            'cms' => [
                'label' => $this->transEntitySelector('CMS Pages'),
                'icon' => 'description',
                'entity_label' => $this->transEntitySelector('CMS page'),
                'entity_label_plural' => $this->transEntitySelector('CMS pages'),
                'selection_methods' => $this->getCmsSelectionMethods(),
            ],
            'cms_categories' => [
                'label' => $this->transEntitySelector('CMS Categories'),
                'icon' => 'folder',
                'entity_label' => $this->transEntitySelector('CMS category'),
                'entity_label_plural' => $this->transEntitySelector('CMS categories'),
                'selection_methods' => $this->getCmsCategorySelectionMethods(),
            ],
            // Transactional / System entities
            'employees' => [
                'label' => $this->transEntitySelector('Employees'),
                'icon' => 'work',
                'entity_label' => $this->transEntitySelector('employee'),
                'entity_label_plural' => $this->transEntitySelector('employees'),
                'selection_methods' => $this->getEmployeeSelectionMethods(),
            ],
            'customers' => [
                'label' => $this->transEntitySelector('Customers'),
                'icon' => 'person',
                'entity_label' => $this->transEntitySelector('customer'),
                'entity_label_plural' => $this->transEntitySelector('customers'),
                'selection_methods' => $this->getCustomerSelectionMethods(),
            ],
            'customer_groups' => [
                'label' => $this->transEntitySelector('Customer Groups'),
                'icon' => 'group',
                'entity_label' => $this->transEntitySelector('customer group'),
                'entity_label_plural' => $this->transEntitySelector('customer groups'),
                'selection_methods' => $this->getCustomerGroupSelectionMethods(),
            ],
            'carriers' => [
                'label' => $this->transEntitySelector('Carriers'),
                'icon' => 'local_shipping',
                'entity_label' => $this->transEntitySelector('carrier'),
                'entity_label_plural' => $this->transEntitySelector('carriers'),
                'selection_methods' => $this->getCarrierSelectionMethods(),
            ],
            'zones' => [
                'label' => $this->transEntitySelector('Zones'),
                'icon' => 'public',
                'entity_label' => $this->transEntitySelector('zone'),
                'entity_label_plural' => $this->transEntitySelector('zones'),
                'selection_methods' => $this->getZoneSelectionMethods(),
            ],
            'countries' => [
                'label' => $this->transEntitySelector('Countries'),
                'icon' => 'flag',
                'entity_label' => $this->transEntitySelector('country'),
                'entity_label_plural' => $this->transEntitySelector('countries'),
                'selection_methods' => $this->getCountrySelectionMethods(),
            ],
            'currencies' => [
                'label' => $this->transEntitySelector('Currencies'),
                'icon' => 'payments',
                'entity_label' => $this->transEntitySelector('currency'),
                'entity_label_plural' => $this->transEntitySelector('currencies'),
                'selection_methods' => $this->getCurrencySelectionMethods(),
            ],
            'languages' => [
                'label' => $this->transEntitySelector('Languages'),
                'icon' => 'language',
                'entity_label' => $this->transEntitySelector('language'),
                'entity_label_plural' => $this->transEntitySelector('languages'),
                'selection_methods' => $this->getLanguageSelectionMethods(),
            ],
            'shops' => [
                'label' => $this->transEntitySelector('Shops'),
                'icon' => 'shopping_cart',
                'entity_label' => $this->transEntitySelector('shop'),
                'entity_label_plural' => $this->transEntitySelector('shops'),
                'selection_methods' => $this->getShopSelectionMethods(),
            ],
            'profiles' => [
                'label' => $this->transEntitySelector('Employee Profiles'),
                'icon' => 'key',
                'entity_label' => $this->transEntitySelector('profile'),
                'entity_label_plural' => $this->transEntitySelector('profiles'),
                'selection_methods' => $this->getProfileSelectionMethods(),
            ],
            'order_states' => [
                'label' => $this->transEntitySelector('Order States'),
                'icon' => 'task_alt',
                'entity_label' => $this->transEntitySelector('order state'),
                'entity_label_plural' => $this->transEntitySelector('order states'),
                'selection_methods' => $this->getOrderStateSelectionMethods(),
            ],
            'taxes' => [
                'label' => $this->transEntitySelector('Taxes'),
                'icon' => 'calculate',
                'entity_label' => $this->transEntitySelector('tax'),
                'entity_label_plural' => $this->transEntitySelector('taxes'),
                'selection_methods' => $this->getTaxSelectionMethods(),
            ],
        ];
    }

    /**
     * Selection methods for Products block
     */
    protected function getProductSelectionMethods()
    {
        return [
            // No group - always first
            'all' => [
                'label' => $this->transEntitySelector('All products'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],

            // ===== BY ENTITY =====
            'specific' => [
                'label' => $this->transEntitySelector('Specific products'),
                'icon' => 'inventory',
                'value_type' => 'entity_search',
                'search_entity' => 'products',
                'group' => 'by_entity',
            ],
            'by_category' => [
                'label' => $this->transEntitySelector('Category'),
                'icon' => 'folder_open',
                'value_type' => 'entity_search',
                'search_entity' => 'categories',
                'group' => 'by_entity',
            ],
            'by_manufacturer' => [
                'label' => $this->transEntitySelector('Manufacturer'),
                'icon' => 'business',
                'value_type' => 'entity_search',
                'search_entity' => 'manufacturers',
                'group' => 'by_entity',
            ],
            'by_supplier' => [
                'label' => $this->transEntitySelector('Supplier'),
                'icon' => 'local_shipping',
                'value_type' => 'entity_search',
                'search_entity' => 'suppliers',
                'group' => 'by_entity',
            ],
            'by_tag' => [
                'label' => $this->transEntitySelector('Tag'),
                'icon' => 'sell',
                'value_type' => 'entity_search',
                'search_entity' => 'tags',
                'group' => 'by_entity',
            ],
            'by_attribute' => [
                'label' => $this->transEntitySelector('Attribute'),
                'icon' => 'brush',
                'value_type' => 'entity_search',
                'search_entity' => 'attributes',
                'group' => 'by_entity',
            ],
            'by_feature' => [
                'label' => $this->transEntitySelector('Feature'),
                'icon' => 'format_list_bulleted',
                'value_type' => 'entity_search',
                'search_entity' => 'features',
                'group' => 'by_entity',
            ],
            'by_combination' => [
                'label' => $this->transEntitySelector('Combination'),
                'icon' => 'grid_view',
                'value_type' => 'combination_attributes',
                'group' => 'by_entity',
            ],

            // ===== BY PROPERTY =====
            'by_condition' => [
                'label' => $this->transEntitySelector('Condition'),
                'icon' => 'verified',
                'value_type' => 'multi_select_tiles',
                'options' => [
                    'new' => ['label' => $this->transEntitySelector('New'), 'icon' => 'star'],
                    'used' => ['label' => $this->transEntitySelector('Used'), 'icon' => 'recycling'],
                    'refurbished' => ['label' => $this->transEntitySelector('Refurbished'), 'icon' => 'refresh'],
                ],
                'group' => 'by_property',
            ],
            'by_visibility' => [
                'label' => $this->transEntitySelector('Visibility'),
                'icon' => 'visibility',
                'value_type' => 'multi_select_tiles',
                'options' => [
                    'both' => ['label' => $this->transEntitySelector('Everywhere'), 'icon' => 'public'],
                    'catalog' => ['label' => $this->transEntitySelector('Catalog only'), 'icon' => 'menu_book'],
                    'search' => ['label' => $this->transEntitySelector('Search only'), 'icon' => 'search'],
                    'none' => ['label' => $this->transEntitySelector('Nowhere'), 'icon' => 'visibility_off'],
                ],
                'group' => 'by_property',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Active status'),
                'icon' => 'toggle_on',
                'value_type' => 'multi_select_tiles',
                'options' => [
                    'active' => ['label' => $this->transEntitySelector('Active'), 'icon' => 'check'],
                    'inactive' => ['label' => $this->transEntitySelector('Inactive'), 'icon' => 'close'],
                ],
                'group' => 'by_property',
            ],
            'by_stock_status' => [
                'label' => $this->transEntitySelector('Stock status'),
                'icon' => 'inventory_2',
                'value_type' => 'multi_select_tiles',
                'options' => [
                    'in_stock' => ['label' => $this->transEntitySelector('In stock'), 'icon' => 'check_circle'],
                    'out_of_stock' => ['label' => $this->transEntitySelector('Out of stock'), 'icon' => 'cancel'],
                    'low_stock' => ['label' => $this->transEntitySelector('Low stock'), 'icon' => 'warning'],
                ],
                'group' => 'by_property',
            ],
            'by_on_sale' => [
                'label' => $this->transEntitySelector('On sale'),
                'icon' => 'label',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_has_specific_price' => [
                'label' => $this->transEntitySelector('Has discount'),
                'icon' => 'confirmation_number',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_is_virtual' => [
                'label' => $this->transEntitySelector('Virtual product'),
                'icon' => 'cloud_download',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_is_pack' => [
                'label' => $this->transEntitySelector('Pack product'),
                'icon' => 'redeem',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_has_combinations' => [
                'label' => $this->transEntitySelector('Has combinations'),
                'icon' => 'account_tree',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_available_for_order' => [
                'label' => $this->transEntitySelector('Available for order'),
                'icon' => 'shopping_cart',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_online_only' => [
                'label' => $this->transEntitySelector('Online only'),
                'icon' => 'laptop',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_has_related' => [
                'label' => $this->transEntitySelector('Has related products'),
                'icon' => 'link',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_has_customization' => [
                'label' => $this->transEntitySelector('Has customization'),
                'icon' => 'edit_note',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_has_attachments' => [
                'label' => $this->transEntitySelector('Has attachments'),
                'icon' => 'attach_file',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_out_of_stock_behavior' => [
                'label' => $this->transEntitySelector('Out of stock behavior'),
                'icon' => 'block',
                'value_type' => 'multi_select_tiles',
                'options' => [
                    'deny' => ['label' => $this->transEntitySelector('Deny orders'), 'icon' => 'cancel'],
                    'allow' => ['label' => $this->transEntitySelector('Allow orders'), 'icon' => 'check_circle'],
                    'default' => ['label' => $this->transEntitySelector('Use default'), 'icon' => 'settings'],
                ],
                'group' => 'by_property',
            ],
            'by_delivery_time' => [
                'label' => $this->transEntitySelector('Delivery time setting'),
                'icon' => 'schedule',
                'value_type' => 'multi_select_tiles',
                'options' => [
                    'none' => ['label' => $this->transEntitySelector('None'), 'icon' => 'close'],
                    'default' => ['label' => $this->transEntitySelector('Default'), 'icon' => 'settings'],
                    'specific' => ['label' => $this->transEntitySelector('Specific'), 'icon' => 'edit'],
                ],
                'group' => 'by_property',
            ],
            'by_has_additional_shipping' => [
                'label' => $this->transEntitySelector('Has additional shipping cost'),
                'icon' => 'local_shipping',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'yes' => ['label' => $this->transEntitySelector('Yes'), 'icon' => 'check', 'color' => 'green'],
                    'no' => ['label' => $this->transEntitySelector('No'), 'icon' => 'close', 'color' => 'red'],
                ],
                'group' => 'by_property',
            ],
            'by_carrier_restriction' => [
                'label' => $this->transEntitySelector('Carrier restriction'),
                'icon' => 'local_shipping',
                'value_type' => 'multi_select_tiles',
                'exclusive' => true,
                'options' => [
                    'restricted' => ['label' => $this->transEntitySelector('Has restriction'), 'icon' => 'lock', 'color' => 'red'],
                    'all' => ['label' => $this->transEntitySelector('All carriers'), 'icon' => 'lock_open', 'color' => 'green'],
                ],
                'group' => 'by_property',
            ],
            'by_carrier' => [
                'label' => $this->transEntitySelector('Specific carrier'),
                'icon' => 'local_shipping',
                'value_type' => 'entity_search',
                'search_entity' => 'carriers',
                'group' => 'by_entity',
            ],

            // ===== BY TEXT =====
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'by_text',
            ],
            'by_reference_pattern' => [
                'label' => $this->transEntitySelector('Reference contains'),
                'icon' => 'qr_code',
                'value_type' => 'pattern',
                'group' => 'by_text',
            ],
            'by_description_pattern' => [
                'label' => $this->transEntitySelector('Description contains'),
                'icon' => 'format_align_left',
                'value_type' => 'pattern',
                'group' => 'by_text',
            ],
            'by_long_description_pattern' => [
                'label' => $this->transEntitySelector('Long description contains'),
                'icon' => 'description',
                'value_type' => 'pattern',
                'group' => 'by_text',
            ],
            'by_ean13_pattern' => [
                'label' => $this->transEntitySelector('EAN-13 contains'),
                'icon' => 'qr_code',
                'value_type' => 'pattern',
                'group' => 'by_text',
            ],
            'by_upc_pattern' => [
                'label' => $this->transEntitySelector('UPC contains'),
                'icon' => 'qr_code',
                'value_type' => 'pattern',
                'group' => 'by_text',
            ],
            'by_isbn_pattern' => [
                'label' => $this->transEntitySelector('ISBN contains'),
                'icon' => 'menu_book',
                'value_type' => 'pattern',
                'group' => 'by_text',
            ],
            'by_mpn_pattern' => [
                'label' => $this->transEntitySelector('MPN contains'),
                'icon' => 'settings',
                'value_type' => 'pattern',
                'group' => 'by_text',
            ],
            'by_meta_title_pattern' => [
                'label' => $this->transEntitySelector('Meta title contains'),
                'icon' => 'title',
                'value_type' => 'pattern',
                'group' => 'by_text',
            ],
            'by_meta_description_pattern' => [
                'label' => $this->transEntitySelector('Meta description contains'),
                'icon' => 'description',
                'value_type' => 'pattern',
                'group' => 'by_text',
            ],

            // ===== BY RANGE =====
            'by_id_range' => [
                'label' => $this->transEntitySelector('ID range'),
                'icon' => 'format_list_numbered',
                'value_type' => 'multi_numeric_range',
                'step' => 1,
                'min' => 1,
                'group' => 'by_range',
            ],
            'by_price_range' => [
                'label' => $this->transEntitySelector('Price range'),
                'icon' => 'payments',
                'value_type' => 'multi_numeric_range',
                'step' => 0.01,
                'min' => 0,
                'group' => 'by_range',
            ],
            'by_weight_range' => [
                'label' => $this->transEntitySelector('Weight range'),
                'icon' => 'dashboard',
                'value_type' => 'multi_numeric_range',
                'step' => 0.001,
                'min' => 0,
                'group' => 'by_range',
            ],
            'by_quantity_range' => [
                'label' => $this->transEntitySelector('Quantity range'),
                'icon' => 'view_in_ar',
                'value_type' => 'multi_numeric_range',
                'step' => 1,
                'group' => 'by_range',
            ],
            'by_position_range' => [
                'label' => $this->transEntitySelector('Position range'),
                'icon' => 'sort',
                'value_type' => 'multi_numeric_range',
                'step' => 1,
                'min' => 0,
                'group' => 'by_range',
            ],
            'by_date_added' => [
                'label' => $this->transEntitySelector('Date added'),
                'icon' => 'calendar_today',
                'value_type' => 'date_range',
                'group' => 'by_range',
            ],
            'by_date_updated' => [
                'label' => $this->transEntitySelector('Date modified'),
                'icon' => 'event',
                'value_type' => 'date_range',
                'group' => 'by_range',
            ],
        ];
    }

    /**
     * Get option group labels
     */
    protected function getMethodGroupLabels()
    {
        return [
            'by_entity' => $this->transEntitySelector('By Entity'),
            'by_property' => $this->transEntitySelector('By Property'),
            'by_text' => $this->transEntitySelector('By Text'),
            'by_range' => $this->transEntitySelector('By Range'),
        ];
    }

    /**
     * Selection methods for Categories block
     */
    protected function getCategorySelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All categories'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific categories'),
                'icon' => 'folder_open',
                'value_type' => 'entity_search',
                'search_entity' => 'categories',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_product_count' => [
                'label' => $this->transEntitySelector('Product count'),
                'icon' => 'view_in_ar',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
            'by_depth_level' => [
                'label' => $this->transEntitySelector('Depth level'),
                'icon' => 'account_tree',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Manufacturers block
     */
    protected function getManufacturerSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All manufacturers'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific manufacturers'),
                'icon' => 'business',
                'value_type' => 'entity_search',
                'search_entity' => 'manufacturers',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_product_count' => [
                'label' => $this->transEntitySelector('Product count'),
                'icon' => 'view_in_ar',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Suppliers block
     */
    protected function getSupplierSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All suppliers'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific suppliers'),
                'icon' => 'local_shipping',
                'value_type' => 'entity_search',
                'search_entity' => 'suppliers',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_product_count' => [
                'label' => $this->transEntitySelector('Product count'),
                'icon' => 'view_in_ar',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for CMS Pages block
     */
    protected function getCmsSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All CMS pages'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific CMS pages'),
                'icon' => 'description',
                'value_type' => 'entity_search',
                'search_entity' => 'cms',
                'group' => 'select_by',
            ],
            'by_cms_category' => [
                'label' => $this->transEntitySelector('CMS pages in category'),
                'icon' => 'folder',
                'value_type' => 'entity_search',
                'search_entity' => 'cms_categories',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Title contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
            'by_indexable' => [
                'label' => $this->transEntitySelector('Indexable'),
                'icon' => 'search',
                'value_type' => 'select',
                'options' => [
                    'yes' => $this->transEntitySelector('Yes'),
                    'no' => $this->transEntitySelector('No'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for CMS Categories block
     */
    protected function getCmsCategorySelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All CMS categories'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific CMS categories'),
                'icon' => 'folder',
                'value_type' => 'entity_search',
                'search_entity' => 'cms_categories',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
            'by_page_count' => [
                'label' => $this->transEntitySelector('Page count'),
                'icon' => 'description',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Employees block
     */
    protected function getEmployeeSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All employees'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific employees'),
                'icon' => 'admin_panel_settings',
                'value_type' => 'entity_search',
                'search_entity' => 'employees',
                'group' => 'select_by',
            ],
            'by_profile' => [
                'label' => $this->transEntitySelector('By profile'),
                'icon' => 'key',
                'value_type' => 'entity_search',
                'search_entity' => 'profiles',
                'group' => 'filter_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Customers block
     */
    protected function getCustomerSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All customers'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific customers'),
                'icon' => 'person',
                'value_type' => 'entity_search',
                'search_entity' => 'customers',
                'group' => 'select_by',
            ],
            'by_group' => [
                'label' => $this->transEntitySelector('By customer group'),
                'icon' => 'group',
                'value_type' => 'entity_search',
                'search_entity' => 'customer_groups',
                'group' => 'filter_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'person',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_email_pattern' => [
                'label' => $this->transEntitySelector('Email contains'),
                'icon' => 'alternate_email',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_company' => [
                'label' => $this->transEntitySelector('Has company'),
                'icon' => 'business',
                'value_type' => 'select',
                'options' => [
                    'yes' => $this->transEntitySelector('Has company'),
                    'no' => $this->transEntitySelector('No company'),
                ],
                'group' => 'filter_by',
            ],
            'by_company_pattern' => [
                'label' => $this->transEntitySelector('Company name contains'),
                'icon' => 'business',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_address_count' => [
                'label' => $this->transEntitySelector('Number of addresses'),
                'icon' => 'location_on',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
            'by_order_count' => [
                'label' => $this->transEntitySelector('Number of orders'),
                'icon' => 'shopping_cart',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
            'by_turnover' => [
                'label' => $this->transEntitySelector('Total spent'),
                'icon' => 'payments',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
            'by_newsletter' => [
                'label' => $this->transEntitySelector('Newsletter'),
                'icon' => 'email',
                'value_type' => 'select',
                'options' => [
                    'subscribed' => $this->transEntitySelector('Subscribed'),
                    'not_subscribed' => $this->transEntitySelector('Not subscribed'),
                ],
                'group' => 'filter_by',
            ],
            'by_guest' => [
                'label' => $this->transEntitySelector('Account type'),
                'icon' => 'person_off',
                'value_type' => 'select',
                'options' => [
                    'guest' => $this->transEntitySelector('Guest'),
                    'registered' => $this->transEntitySelector('Registered'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Customer Groups block
     */
    protected function getCustomerGroupSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All customer groups'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific groups'),
                'icon' => 'group',
                'value_type' => 'entity_search',
                'search_entity' => 'customer_groups',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_price_display' => [
                'label' => $this->transEntitySelector('Price display'),
                'icon' => 'payments',
                'value_type' => 'select',
                'options' => [
                    'tax_excl' => $this->transEntitySelector('Tax excluded'),
                    'tax_incl' => $this->transEntitySelector('Tax included'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Carriers block
     */
    protected function getCarrierSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All carriers'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific carriers'),
                'icon' => 'local_shipping',
                'value_type' => 'entity_search',
                'search_entity' => 'carriers',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
            'by_shipping_handling' => [
                'label' => $this->transEntitySelector('Shipping handling'),
                'icon' => 'settings',
                'value_type' => 'select',
                'options' => [
                    'with_handling' => $this->transEntitySelector('With handling'),
                    'without_handling' => $this->transEntitySelector('Without handling'),
                ],
                'group' => 'filter_by',
            ],
            'by_free_shipping' => [
                'label' => $this->transEntitySelector('Free shipping'),
                'icon' => 'redeem',
                'value_type' => 'select',
                'options' => [
                    'free' => $this->transEntitySelector('Free shipping'),
                    'paid' => $this->transEntitySelector('Paid shipping'),
                ],
                'group' => 'filter_by',
            ],
            'by_zone' => [
                'label' => $this->transEntitySelector('By zone'),
                'icon' => 'public',
                'value_type' => 'entity_search',
                'search_entity' => 'zones',
                'group' => 'filter_by',
            ],
            'by_customer_group' => [
                'label' => $this->transEntitySelector('By customer group'),
                'icon' => 'group',
                'value_type' => 'entity_search',
                'search_entity' => 'customer_groups',
                'group' => 'filter_by',
            ],
            'by_price_range' => [
                'label' => $this->transEntitySelector('Shipping price range'),
                'icon' => 'payments',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
            'by_weight_range' => [
                'label' => $this->transEntitySelector('Max weight'),
                'icon' => 'dashboard',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Zones block
     */
    protected function getZoneSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All zones'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific zones'),
                'icon' => 'public',
                'value_type' => 'entity_search',
                'search_entity' => 'zones',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Countries block
     */
    protected function getCountrySelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All countries'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific countries'),
                'icon' => 'flag',
                'value_type' => 'entity_search',
                'search_entity' => 'countries',
                'group' => 'select_by',
            ],
            'by_zone' => [
                'label' => $this->transEntitySelector('By zone'),
                'icon' => 'public',
                'value_type' => 'entity_search',
                'search_entity' => 'zones',
                'group' => 'filter_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
            'by_contains_states' => [
                'label' => $this->transEntitySelector('Has states'),
                'icon' => 'location_on',
                'value_type' => 'select',
                'options' => [
                    'yes' => $this->transEntitySelector('Yes'),
                    'no' => $this->transEntitySelector('No'),
                ],
                'group' => 'filter_by',
            ],
            'by_need_zip_code' => [
                'label' => $this->transEntitySelector('Requires ZIP code'),
                'icon' => 'email',
                'value_type' => 'select',
                'options' => [
                    'yes' => $this->transEntitySelector('Yes'),
                    'no' => $this->transEntitySelector('No'),
                ],
                'group' => 'filter_by',
            ],
            'by_zip_format' => [
                'label' => $this->transEntitySelector('ZIP format contains'),
                'icon' => 'qr_code',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_need_identification' => [
                'label' => $this->transEntitySelector('Requires ID number'),
                'icon' => 'credit_card',
                'value_type' => 'select',
                'options' => [
                    'yes' => $this->transEntitySelector('Yes'),
                    'no' => $this->transEntitySelector('No'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Currencies block
     */
    protected function getCurrencySelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All currencies'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific currencies'),
                'icon' => 'payments',
                'value_type' => 'entity_search',
                'search_entity' => 'currencies',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name/code contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Languages block
     */
    protected function getLanguageSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All languages'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific languages'),
                'icon' => 'language',
                'value_type' => 'entity_search',
                'search_entity' => 'languages',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name/code contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
            'by_rtl' => [
                'label' => $this->transEntitySelector('Text direction'),
                'icon' => 'format_align_left',
                'value_type' => 'select',
                'options' => [
                    'ltr' => $this->transEntitySelector('LTR (Left-to-right)'),
                    'rtl' => $this->transEntitySelector('RTL (Right-to-left)'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Shops block
     */
    protected function getShopSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All shops'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific shops'),
                'icon' => 'shopping_cart',
                'value_type' => 'entity_search',
                'search_entity' => 'shops',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Employee Profiles block
     */
    protected function getProfileSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All profiles'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific profiles'),
                'icon' => 'key',
                'value_type' => 'entity_search',
                'search_entity' => 'profiles',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Order States block
     */
    protected function getOrderStateSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All order states'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific states'),
                'icon' => 'task_alt',
                'value_type' => 'entity_search',
                'search_entity' => 'order_states',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_paid' => [
                'label' => $this->transEntitySelector('Payment status'),
                'icon' => 'payments',
                'value_type' => 'select',
                'options' => [
                    'paid' => $this->transEntitySelector('Paid'),
                    'unpaid' => $this->transEntitySelector('Unpaid'),
                ],
                'group' => 'filter_by',
            ],
            'by_shipped' => [
                'label' => $this->transEntitySelector('Shipping status'),
                'icon' => 'local_shipping',
                'value_type' => 'select',
                'options' => [
                    'shipped' => $this->transEntitySelector('Shipped'),
                    'not_shipped' => $this->transEntitySelector('Not shipped'),
                ],
                'group' => 'filter_by',
            ],
            'by_delivery' => [
                'label' => $this->transEntitySelector('Delivery status'),
                'icon' => 'check_circle',
                'value_type' => 'select',
                'options' => [
                    'delivered' => $this->transEntitySelector('Delivered'),
                    'not_delivered' => $this->transEntitySelector('Not delivered'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Selection methods for Taxes block
     */
    protected function getTaxSelectionMethods()
    {
        return [
            'all' => [
                'label' => $this->transEntitySelector('All taxes'),
                'icon' => 'star',
                'value_type' => 'none',
                'group' => '',
            ],
            'specific' => [
                'label' => $this->transEntitySelector('Specific taxes'),
                'icon' => 'payments',
                'value_type' => 'entity_search',
                'search_entity' => 'taxes',
                'group' => 'select_by',
            ],
            'by_name_pattern' => [
                'label' => $this->transEntitySelector('Name contains'),
                'icon' => 'text_fields',
                'value_type' => 'pattern',
                'group' => 'filter_by',
            ],
            'by_rate_range' => [
                'label' => $this->transEntitySelector('Rate range'),
                'icon' => 'calculate',
                'value_type' => 'numeric_range',
                'group' => 'filter_by',
            ],
            'by_active_status' => [
                'label' => $this->transEntitySelector('Status'),
                'icon' => 'toggle_on',
                'value_type' => 'select',
                'options' => [
                    'active' => $this->transEntitySelector('Active'),
                    'inactive' => $this->transEntitySelector('Inactive'),
                ],
                'group' => 'filter_by',
            ],
        ];
    }

    /**
     * Render target conditions HTML
     */

    /**
     * Render the entity selector HTML widget
     * Delegates to EntitySelectorRenderer
     *
     * @param array $config Widget configuration
     * @param array|string $savedData Previously saved data
     * @return string HTML output
     */
    protected function renderEntitySelectorHtml($config = [], $savedData = [])
    {
        return $this->getEntitySelectorRenderer()->renderEntitySelectorHtml($config, $savedData);
    }

    /**
     * Render a target block
     * Delegates to EntitySelectorRenderer
     */
    protected function renderTargetBlock($blockType, $blockDef, $blockData, $isActive = true, $mode = 'multi', $emptyMeansAll = true)
    {
        return $this->getEntitySelectorRenderer()->renderTargetBlock($blockType, $blockDef, $blockData, $isActive, $mode, $emptyMeansAll);
    }

    /**
     * Render result modifiers section
     * Delegates to EntitySelectorRenderer
     */
    protected function renderResultModifiers($blockType, $blockData)
    {
        return $this->getEntitySelectorRenderer()->renderResultModifiers($blockType, $blockData);
    }

    /**
     * Render a target group
     * Delegates to EntitySelectorRenderer
     */
    protected function renderTargetGroup($blockType, $blockDef, $group, $groupIndex, $mode = 'multi')
    {
        return $this->getEntitySelectorRenderer()->renderTargetGroup($blockType, $blockDef, $group, $groupIndex, $mode);
    }

    /**
     * Render group modifiers
     * Delegates to EntitySelectorRenderer
     */
    protected function renderGroupModifiers($blockType, $group)
    {
        return $this->getEntitySelectorRenderer()->renderGroupModifiers($blockType, $group);
    }

    /**
     * Render method options
     * Delegates to EntitySelectorRenderer
     */
    protected function renderMethodOptions($methods, $selectedMethod, $excludeAll = false)
    {
        return $this->getEntitySelectorRenderer()->renderMethodOptions($methods, $selectedMethod, $excludeAll);
    }

    /**
     * Render method option
     * Delegates to EntitySelectorRenderer
     */
    protected function renderMethodOption($methodKey, $methodDef, $selected = '')
    {
        return $this->getEntitySelectorRenderer()->renderMethodOption($methodKey, $methodDef, $selected);
    }

    /**
     * Render exclude row
     * Delegates to EntitySelectorRenderer
     */
    protected function renderExcludeRow($blockType, $methods, $exclude, $excludeIndex)
    {
        return $this->getEntitySelectorRenderer()->renderExcludeRow($blockType, $methods, $exclude, $excludeIndex);
    }

    /**
     * Render value picker
     * Delegates to EntitySelectorRenderer
     */
    protected function renderValuePicker($section, $methods, $selectedMethod, $values)
    {
        return $this->getEntitySelectorRenderer()->renderValuePicker($section, $methods, $selectedMethod, $values);
    }
    /**
     * AJAX: Get full category tree structure for tree view display
     */
    protected function ajaxGetCategoryTree()
    {
        $entityType = Tools::getValue('entity_type', 'categories');
        $idLang = (int) $this->context->language->id;
        $idShop = (int) $this->context->shop->id;

        // Dispatch to appropriate tree builder based on entity type
        if ($entityType === 'cms_categories') {
            $this->ajaxGetCmsCategoryTree($idLang, $idShop);
            return;
        }

        // Default: Product categories
        // Get shop's root category (Home) - we exclude this and the system root (id=1)
        $rootCategoryId = (int) \Configuration::get('PS_ROOT_CATEGORY');
        $homeCategory = (int) \Configuration::get('PS_HOME_CATEGORY');

        // Fetch all categories with hierarchy data, ordered by nested set (nleft)
        $sql = 'SELECT c.id_category, c.id_parent, c.level_depth, c.nleft, c.nright, c.active,
            cl.name,
            (SELECT COUNT(DISTINCT cp.id_product)
             FROM ' . _DB_PREFIX_ . 'category_product cp
             INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON cp.id_product = ps.id_product
                AND ps.id_shop = ' . (int) $idShop . ' AND ps.active = 1
             WHERE cp.id_category = c.id_category) as product_count,
            COALESCE((SELECT SUM(od.product_quantity)
             FROM ' . _DB_PREFIX_ . 'category_product cp3
             INNER JOIN ' . _DB_PREFIX_ . 'order_detail od ON od.product_id = cp3.id_product
             INNER JOIN ' . _DB_PREFIX_ . 'orders o ON o.id_order = od.id_order AND o.id_shop = ' . (int) $idShop . '
             WHERE cp3.id_category = c.id_category AND o.valid = 1), 0) as total_sales
            FROM ' . _DB_PREFIX_ . 'category c
            INNER JOIN ' . _DB_PREFIX_ . 'category_lang cl ON cl.id_category = c.id_category
                AND cl.id_lang = ' . (int) $idLang . ' AND cl.id_shop = ' . (int) $idShop . '
            INNER JOIN ' . _DB_PREFIX_ . 'category_shop cs ON cs.id_category = c.id_category
                AND cs.id_shop = ' . (int) $idShop . '
            WHERE c.id_category NOT IN (' . $rootCategoryId . ', ' . $homeCategory . ')
            ORDER BY c.nleft ASC';

        $results = Db::getInstance()->executeS($sql);

        if (!$results) {
            $this->ajaxDie(json_encode([
                'success' => true,
                'categories' => [],
            ]));
            return;
        }

        // Build tree structure with has_children flag
        $categories = [];
        foreach ($results as $row) {
            $hasChildren = ((int) $row['nright'] - (int) $row['nleft']) > 1;
            $productCount = (int) $row['product_count'];
            $totalSales = (int) $row['total_sales'];

            $subtitle = 'Level ' . $row['level_depth'];
            if ($productCount > 0) {
                $subtitle .= ' • ' . $productCount . ' product' . ($productCount !== 1 ? 's' : '');
            }
            if ($totalSales > 0) {
                $subtitle .= ' • ' . $totalSales . ' sold';
            }
            if (!(int) $row['active']) {
                $subtitle .= ' • Inactive';
            }

            $categories[] = [
                'id' => (int) $row['id_category'],
                'parent_id' => (int) $row['id_parent'],
                'name' => $row['name'],
                'level' => (int) $row['level_depth'],
                'nleft' => (int) $row['nleft'],
                'nright' => (int) $row['nright'],
                'has_children' => $hasChildren,
                'active' => (bool) $row['active'],
                'product_count' => $productCount,
                'total_sales' => $totalSales,
                'subtitle' => $subtitle,
            ];
        }

        $this->ajaxDie(json_encode([
            'success' => true,
            'categories' => $categories,
        ]));
    }

    /**
     * Get CMS category tree
     */
    protected function ajaxGetCmsCategoryTree($idLang, $idShop)
    {
        // Fetch all CMS categories with hierarchy data
        $sql = 'SELECT cc.id_cms_category, cc.id_parent, cc.level_depth, cc.active,
            ccl.name,
            (SELECT COUNT(*)
             FROM ' . _DB_PREFIX_ . 'cms c
             INNER JOIN ' . _DB_PREFIX_ . 'cms_shop cs ON c.id_cms = cs.id_cms AND cs.id_shop = ' . (int) $idShop . '
             WHERE c.id_cms_category = cc.id_cms_category AND c.active = 1) as page_count
            FROM ' . _DB_PREFIX_ . 'cms_category cc
            INNER JOIN ' . _DB_PREFIX_ . 'cms_category_lang ccl ON ccl.id_cms_category = cc.id_cms_category
                AND ccl.id_lang = ' . (int) $idLang . ' AND ccl.id_shop = ' . (int) $idShop . '
            INNER JOIN ' . _DB_PREFIX_ . 'cms_category_shop ccs ON ccs.id_cms_category = cc.id_cms_category
                AND ccs.id_shop = ' . (int) $idShop . '
            ORDER BY cc.level_depth ASC, ccl.name ASC';

        $results = Db::getInstance()->executeS($sql);

        if (!$results) {
            $this->ajaxDie(json_encode([
                'success' => true,
                'categories' => [],
            ]));
            return;
        }

        // Build tree structure - CMS categories don't have nleft/nright, so we use parent_id
        $categories = [];
        $childrenCount = [];

        // First pass: count children for each category
        foreach ($results as $row) {
            $parentId = (int) $row['id_parent'];
            if (!isset($childrenCount[$parentId])) {
                $childrenCount[$parentId] = 0;
            }
            $childrenCount[$parentId]++;
        }

        // Second pass: build category array
        foreach ($results as $row) {
            $id = (int) $row['id_cms_category'];
            $hasChildren = isset($childrenCount[$id]) && $childrenCount[$id] > 0;
            $pageCount = (int) $row['page_count'];

            $subtitle = '';
            if ($pageCount > 0) {
                $subtitle = $pageCount . ' page' . ($pageCount !== 1 ? 's' : '');
            }
            if (!(int) $row['active']) {
                $subtitle .= ($subtitle ? ' • ' : '') . 'Inactive';
            }

            $categories[] = [
                'id' => $id,
                'parent_id' => (int) $row['id_parent'],
                'name' => $row['name'],
                'level' => (int) $row['level_depth'],
                'nleft' => 0, // CMS categories don't have nested set, but we need these for JS
                'nright' => 0,
                'has_children' => $hasChildren,
                'active' => (bool) $row['active'],
                'page_count' => $pageCount,
                'subtitle' => $subtitle,
            ];
        }

        $this->ajaxDie(json_encode([
            'success' => true,
            'categories' => $categories,
        ]));
    }

    protected function searchTargetManufacturers($query, $idLang, $idShop, $limit = 20, $offset = 0, $filters = [])
    {
        // Build inner query with aggregates (product count, sales quantity, turnover, dates)
        $innerSql = 'SELECT m.id_manufacturer as id, m.name, m.date_add, m.date_upd,
            (SELECT COUNT(DISTINCT p.id_product) FROM ' . _DB_PREFIX_ . 'product p
            INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON p.id_product = ps.id_product AND ps.id_shop = ' . (int) $idShop . ' AND ps.active = 1
            WHERE p.id_manufacturer = m.id_manufacturer) as product_count,
            (SELECT COALESCE(SUM(od.product_quantity), 0) FROM ' . _DB_PREFIX_ . 'order_detail od
            INNER JOIN ' . _DB_PREFIX_ . 'orders o ON od.id_order = o.id_order AND o.id_shop = ' . (int) $idShop . '
            INNER JOIN ' . _DB_PREFIX_ . 'product p ON od.product_id = p.id_product
            WHERE p.id_manufacturer = m.id_manufacturer AND o.valid = 1) as total_sales,
            (SELECT COALESCE(SUM(od.total_price_tax_excl), 0) FROM ' . _DB_PREFIX_ . 'order_detail od
            INNER JOIN ' . _DB_PREFIX_ . 'orders o ON od.id_order = o.id_order AND o.id_shop = ' . (int) $idShop . '
            INNER JOIN ' . _DB_PREFIX_ . 'product p ON od.product_id = p.id_product
            WHERE p.id_manufacturer = m.id_manufacturer AND o.valid = 1) as turnover,
            (SELECT MAX(p.date_add) FROM ' . _DB_PREFIX_ . 'product p
            INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON p.id_product = ps.id_product AND ps.id_shop = ' . (int) $idShop . '
            WHERE p.id_manufacturer = m.id_manufacturer) as last_product_date
            FROM ' . _DB_PREFIX_ . 'manufacturer m
            INNER JOIN ' . _DB_PREFIX_ . 'manufacturer_shop ms ON ms.id_manufacturer = m.id_manufacturer AND ms.id_shop = ' . (int) $idShop . '
            WHERE 1=1';

        // Active filter (default: active only)
        if (!empty($filters['active_only'])) {
            $innerSql .= ' AND m.active = 1';
        }

        if (!empty($query)) {
            $innerSql .= " AND m.name LIKE '%" . pSQL($query) . "%'";
        }

        // Wrap in outer query to filter by computed columns
        $sql = 'SELECT * FROM (' . $innerSql . ') AS manufacturers WHERE 1=1';

        // Product count filter
        if (isset($filters['product_count_min']) && $filters['product_count_min'] !== null) {
            $sql .= ' AND product_count >= ' . (int) $filters['product_count_min'];
        }
        if (isset($filters['product_count_max']) && $filters['product_count_max'] !== null) {
            $sql .= ' AND product_count <= ' . (int) $filters['product_count_max'];
        }
        // Sales quantity filter
        if (isset($filters['sales_min']) && $filters['sales_min'] !== null) {
            $sql .= ' AND total_sales >= ' . (int) $filters['sales_min'];
        }
        if (isset($filters['sales_max']) && $filters['sales_max'] !== null) {
            $sql .= ' AND total_sales <= ' . (int) $filters['sales_max'];
        }
        // Turnover filter
        if (isset($filters['turnover_min']) && $filters['turnover_min'] !== null) {
            $sql .= ' AND turnover >= ' . (float) $filters['turnover_min'];
        }
        if (isset($filters['turnover_max']) && $filters['turnover_max'] !== null) {
            $sql .= ' AND turnover <= ' . (float) $filters['turnover_max'];
        }
        // Date added filter
        if (!empty($filters['date_add_from'])) {
            $sql .= " AND date_add >= '" . pSQL($filters['date_add_from']) . " 00:00:00'";
        }
        if (!empty($filters['date_add_to'])) {
            $sql .= " AND date_add <= '" . pSQL($filters['date_add_to']) . " 23:59:59'";
        }
        // Last product date filter
        if (!empty($filters['last_product_from'])) {
            $sql .= " AND last_product_date >= '" . pSQL($filters['last_product_from']) . " 00:00:00'";
        }
        if (!empty($filters['last_product_to'])) {
            $sql .= " AND last_product_date <= '" . pSQL($filters['last_product_to']) . " 23:59:59'";
        }

        $sql .= ' ORDER BY name ASC LIMIT ' . (int) $offset . ', ' . (int) $limit;

        $results = Db::getInstance()->executeS($sql);

        $currency = \Context::getContext()->currency;
        $currencySign = $currency->sign ?? $currency->symbol ?? '';

        return array_map(function ($row) use ($currencySign) {
            $productCount = (int) $row['product_count'];
            $totalSales = (int) $row['total_sales'];
            $turnover = (float) $row['turnover'];

            // Build subtitle: products, sales, revenue
            $parts = [];
            $parts[] = $productCount . ' product' . ($productCount !== 1 ? 's' : '');
            if ($totalSales > 0) {
                $parts[] = $totalSales . ' sold';
            }
            if ($turnover > 0) {
                $parts[] = $currencySign . number_format($turnover, 0);
            }

            return [
                'id' => (int) $row['id'],
                'name' => $row['name'],
                'subtitle' => implode(' • ', $parts),
                'product_count' => $productCount,
                'total_sales' => $totalSales,
                'turnover' => $turnover,
            ];
        }, $results ?: []);
    }

    protected function countTargetManufacturers($query, $idLang, $idShop, $filters = [])
    {
        $innerSql = 'SELECT m.id_manufacturer, m.date_add,
            (SELECT COUNT(DISTINCT p.id_product) FROM ' . _DB_PREFIX_ . 'product p
            INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON p.id_product = ps.id_product AND ps.id_shop = ' . (int) $idShop . ' AND ps.active = 1
            WHERE p.id_manufacturer = m.id_manufacturer) as product_count,
            (SELECT COALESCE(SUM(od.product_quantity), 0) FROM ' . _DB_PREFIX_ . 'order_detail od
            INNER JOIN ' . _DB_PREFIX_ . 'orders o ON od.id_order = o.id_order AND o.id_shop = ' . (int) $idShop . '
            INNER JOIN ' . _DB_PREFIX_ . 'product p ON od.product_id = p.id_product
            WHERE p.id_manufacturer = m.id_manufacturer AND o.valid = 1) as total_sales,
            (SELECT COALESCE(SUM(od.total_price_tax_excl), 0) FROM ' . _DB_PREFIX_ . 'order_detail od
            INNER JOIN ' . _DB_PREFIX_ . 'orders o ON od.id_order = o.id_order AND o.id_shop = ' . (int) $idShop . '
            INNER JOIN ' . _DB_PREFIX_ . 'product p ON od.product_id = p.id_product
            WHERE p.id_manufacturer = m.id_manufacturer AND o.valid = 1) as turnover,
            (SELECT MAX(p.date_add) FROM ' . _DB_PREFIX_ . 'product p
            INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON p.id_product = ps.id_product AND ps.id_shop = ' . (int) $idShop . '
            WHERE p.id_manufacturer = m.id_manufacturer) as last_product_date
            FROM ' . _DB_PREFIX_ . 'manufacturer m
            INNER JOIN ' . _DB_PREFIX_ . 'manufacturer_shop ms ON ms.id_manufacturer = m.id_manufacturer AND ms.id_shop = ' . (int) $idShop . '
            WHERE 1=1';

        // Active filter
        if (!empty($filters['active_only'])) {
            $innerSql .= ' AND m.active = 1';
        }

        if (!empty($query)) {
            $innerSql .= " AND m.name LIKE '%" . pSQL($query) . "%'";
        }

        // Wrap in outer query for filtering
        $sql = 'SELECT COUNT(*) FROM (' . $innerSql . ') AS manufacturers WHERE 1=1';

        // Product count filter
        if (isset($filters['product_count_min']) && $filters['product_count_min'] !== null) {
            $sql .= ' AND product_count >= ' . (int) $filters['product_count_min'];
        }
        if (isset($filters['product_count_max']) && $filters['product_count_max'] !== null) {
            $sql .= ' AND product_count <= ' . (int) $filters['product_count_max'];
        }
        // Sales quantity filter
        if (isset($filters['sales_min']) && $filters['sales_min'] !== null) {
            $sql .= ' AND total_sales >= ' . (int) $filters['sales_min'];
        }
        if (isset($filters['sales_max']) && $filters['sales_max'] !== null) {
            $sql .= ' AND total_sales <= ' . (int) $filters['sales_max'];
        }
        // Turnover filter
        if (isset($filters['turnover_min']) && $filters['turnover_min'] !== null) {
            $sql .= ' AND turnover >= ' . (float) $filters['turnover_min'];
        }
        if (isset($filters['turnover_max']) && $filters['turnover_max'] !== null) {
            $sql .= ' AND turnover <= ' . (float) $filters['turnover_max'];
        }
        // Date added filter
        if (!empty($filters['date_add_from'])) {
            $sql .= " AND date_add >= '" . pSQL($filters['date_add_from']) . " 00:00:00'";
        }
        if (!empty($filters['date_add_to'])) {
            $sql .= " AND date_add <= '" . pSQL($filters['date_add_to']) . " 23:59:59'";
        }
        // Last product date filter
        if (!empty($filters['last_product_from'])) {
            $sql .= " AND last_product_date >= '" . pSQL($filters['last_product_from']) . " 00:00:00'";
        }
        if (!empty($filters['last_product_to'])) {
            $sql .= " AND last_product_date <= '" . pSQL($filters['last_product_to']) . " 23:59:59'";
        }

        return (int) Db::getInstance()->getValue($sql);
    }

    protected function getTargetManufacturersByIds($ids, $idLang)
    {
        if (empty($ids)) {
            return [];
        }

        $sql = new DbQuery();
        $sql->select('m.id_manufacturer as id, m.name');
        $sql->from('manufacturer', 'm');
        $sql->where('m.id_manufacturer IN (' . implode(',', array_map('intval', $ids)) . ')');
        $sql->orderBy('FIELD(m.id_manufacturer, ' . implode(',', array_map('intval', $ids)) . ')');

        $results = Db::getInstance()->executeS($sql);

        return array_map(function ($row) {
            return [
                'id' => (int) $row['id'],
                'name' => $row['name'],
            ];
        }, $results ?: []);
    }

    protected function searchTargetSuppliers($query, $idLang, $idShop, $limit = 20, $offset = 0, $filters = [])
    {
        // Build inner query with computed columns
        $innerSql = 'SELECT s.id_supplier as id, s.name, s.active, s.date_add,
            (SELECT COUNT(DISTINCT psu.id_product) FROM ' . _DB_PREFIX_ . 'product_supplier psu
            INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON psu.id_product = ps.id_product AND ps.id_shop = ' . (int) $idShop . ' AND ps.active = 1
            WHERE psu.id_supplier = s.id_supplier) as product_count,
            COALESCE((SELECT SUM(od.product_quantity)
                FROM ' . _DB_PREFIX_ . 'product_supplier psu2
                INNER JOIN ' . _DB_PREFIX_ . 'order_detail od ON od.product_id = psu2.id_product
                INNER JOIN ' . _DB_PREFIX_ . 'orders o ON o.id_order = od.id_order AND o.id_shop = ' . (int) $idShop . '
                WHERE psu2.id_supplier = s.id_supplier AND o.valid = 1), 0) as total_sales,
            COALESCE((SELECT SUM(od.total_price_tax_excl)
                FROM ' . _DB_PREFIX_ . 'product_supplier psu3
                INNER JOIN ' . _DB_PREFIX_ . 'order_detail od ON od.product_id = psu3.id_product
                INNER JOIN ' . _DB_PREFIX_ . 'orders o ON o.id_order = od.id_order AND o.id_shop = ' . (int) $idShop . '
                WHERE psu3.id_supplier = s.id_supplier AND o.valid = 1), 0) as total_turnover,
            (SELECT MAX(p.date_add)
                FROM ' . _DB_PREFIX_ . 'product_supplier psu4
                INNER JOIN ' . _DB_PREFIX_ . 'product p ON p.id_product = psu4.id_product
                INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON p.id_product = ps.id_product AND ps.id_shop = ' . (int) $idShop . ' AND ps.active = 1
                WHERE psu4.id_supplier = s.id_supplier) as last_product_date
            FROM ' . _DB_PREFIX_ . 'supplier s
            INNER JOIN ' . _DB_PREFIX_ . 'supplier_shop ss ON ss.id_supplier = s.id_supplier AND ss.id_shop = ' . (int) $idShop . '
            WHERE 1=1';

        // Active filter
        if (!empty($filters['active_only'])) {
            $innerSql .= ' AND s.active = 1';
        }

        // Date added filters
        if (!empty($filters['date_add_from'])) {
            $innerSql .= " AND s.date_add >= '" . pSQL($filters['date_add_from']) . " 00:00:00'";
        }
        if (!empty($filters['date_add_to'])) {
            $innerSql .= " AND s.date_add <= '" . pSQL($filters['date_add_to']) . " 23:59:59'";
        }

        if (!empty($query)) {
            $innerSql .= " AND s.name LIKE '%" . pSQL($query) . "%'";
        }

        // Wrap in outer query to filter by computed columns
        $sql = 'SELECT * FROM (' . $innerSql . ') AS suppliers WHERE 1=1';

        // Product count range filter
        if (isset($filters['product_count_min']) && $filters['product_count_min'] !== null) {
            $sql .= ' AND product_count >= ' . (int) $filters['product_count_min'];
        }
        if (isset($filters['product_count_max']) && $filters['product_count_max'] !== null) {
            $sql .= ' AND product_count <= ' . (int) $filters['product_count_max'];
        }

        // Sales range filter
        if (isset($filters['sales_min']) && $filters['sales_min'] !== null) {
            $sql .= ' AND total_sales >= ' . (int) $filters['sales_min'];
        }
        if (isset($filters['sales_max']) && $filters['sales_max'] !== null) {
            $sql .= ' AND total_sales <= ' . (int) $filters['sales_max'];
        }

        // Turnover range filter
        if (isset($filters['turnover_min']) && $filters['turnover_min'] !== null) {
            $sql .= ' AND total_turnover >= ' . (float) $filters['turnover_min'];
        }
        if (isset($filters['turnover_max']) && $filters['turnover_max'] !== null) {
            $sql .= ' AND total_turnover <= ' . (float) $filters['turnover_max'];
        }

        // Last product date filters
        if (!empty($filters['last_product_from'])) {
            $sql .= " AND last_product_date >= '" . pSQL($filters['last_product_from']) . " 00:00:00'";
        }
        if (!empty($filters['last_product_to'])) {
            $sql .= " AND last_product_date <= '" . pSQL($filters['last_product_to']) . " 23:59:59'";
        }

        $sql .= ' ORDER BY name ASC LIMIT ' . (int) $offset . ', ' . (int) $limit;

        $results = Db::getInstance()->executeS($sql);

        return array_map(function ($row) {
            $productCount = (int) $row['product_count'];
            $totalSales = (int) $row['total_sales'];
            $subtitle = $productCount . ' product' . ($productCount !== 1 ? 's' : '');
            if ($totalSales > 0) {
                $subtitle .= ' • ' . $totalSales . ' sold';
            }
            if (!(int) $row['active']) {
                $subtitle .= ' • Inactive';
            }
            return [
                'id' => (int) $row['id'],
                'name' => $row['name'],
                'subtitle' => $subtitle,
                'product_count' => $productCount,
                'total_sales' => $totalSales,
            ];
        }, $results ?: []);
    }

    protected function countTargetSuppliers($query, $idLang, $idShop, $filters = [])
    {
        // Build inner query with computed columns
        $innerSql = 'SELECT s.id_supplier,
            (SELECT COUNT(DISTINCT psu.id_product) FROM ' . _DB_PREFIX_ . 'product_supplier psu
            INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON psu.id_product = ps.id_product AND ps.id_shop = ' . (int) $idShop . ' AND ps.active = 1
            WHERE psu.id_supplier = s.id_supplier) as product_count,
            COALESCE((SELECT SUM(od.product_quantity)
                FROM ' . _DB_PREFIX_ . 'product_supplier psu2
                INNER JOIN ' . _DB_PREFIX_ . 'order_detail od ON od.product_id = psu2.id_product
                INNER JOIN ' . _DB_PREFIX_ . 'orders o ON o.id_order = od.id_order AND o.id_shop = ' . (int) $idShop . '
                WHERE psu2.id_supplier = s.id_supplier AND o.valid = 1), 0) as total_sales,
            COALESCE((SELECT SUM(od.total_price_tax_excl)
                FROM ' . _DB_PREFIX_ . 'product_supplier psu3
                INNER JOIN ' . _DB_PREFIX_ . 'order_detail od ON od.product_id = psu3.id_product
                INNER JOIN ' . _DB_PREFIX_ . 'orders o ON o.id_order = od.id_order AND o.id_shop = ' . (int) $idShop . '
                WHERE psu3.id_supplier = s.id_supplier AND o.valid = 1), 0) as total_turnover,
            (SELECT MAX(p.date_add)
                FROM ' . _DB_PREFIX_ . 'product_supplier psu4
                INNER JOIN ' . _DB_PREFIX_ . 'product p ON p.id_product = psu4.id_product
                INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON p.id_product = ps.id_product AND ps.id_shop = ' . (int) $idShop . ' AND ps.active = 1
                WHERE psu4.id_supplier = s.id_supplier) as last_product_date
            FROM ' . _DB_PREFIX_ . 'supplier s
            INNER JOIN ' . _DB_PREFIX_ . 'supplier_shop ss ON ss.id_supplier = s.id_supplier AND ss.id_shop = ' . (int) $idShop . '
            WHERE 1=1';

        // Active filter
        if (!empty($filters['active_only'])) {
            $innerSql .= ' AND s.active = 1';
        }

        // Date added filters
        if (!empty($filters['date_add_from'])) {
            $innerSql .= " AND s.date_add >= '" . pSQL($filters['date_add_from']) . " 00:00:00'";
        }
        if (!empty($filters['date_add_to'])) {
            $innerSql .= " AND s.date_add <= '" . pSQL($filters['date_add_to']) . " 23:59:59'";
        }

        if (!empty($query)) {
            $innerSql .= " AND s.name LIKE '%" . pSQL($query) . "%'";
        }

        // Wrap in outer query to filter by computed columns
        $sql = 'SELECT COUNT(*) FROM (' . $innerSql . ') AS suppliers WHERE 1=1';

        // Product count range filter
        if (isset($filters['product_count_min']) && $filters['product_count_min'] !== null) {
            $sql .= ' AND product_count >= ' . (int) $filters['product_count_min'];
        }
        if (isset($filters['product_count_max']) && $filters['product_count_max'] !== null) {
            $sql .= ' AND product_count <= ' . (int) $filters['product_count_max'];
        }

        // Sales range filter
        if (isset($filters['sales_min']) && $filters['sales_min'] !== null) {
            $sql .= ' AND total_sales >= ' . (int) $filters['sales_min'];
        }
        if (isset($filters['sales_max']) && $filters['sales_max'] !== null) {
            $sql .= ' AND total_sales <= ' . (int) $filters['sales_max'];
        }

        // Turnover range filter
        if (isset($filters['turnover_min']) && $filters['turnover_min'] !== null) {
            $sql .= ' AND total_turnover >= ' . (float) $filters['turnover_min'];
        }
        if (isset($filters['turnover_max']) && $filters['turnover_max'] !== null) {
            $sql .= ' AND total_turnover <= ' . (float) $filters['turnover_max'];
        }

        // Last product date filters
        if (!empty($filters['last_product_from'])) {
            $sql .= " AND last_product_date >= '" . pSQL($filters['last_product_from']) . " 00:00:00'";
        }
        if (!empty($filters['last_product_to'])) {
            $sql .= " AND last_product_date <= '" . pSQL($filters['last_product_to']) . " 23:59:59'";
        }

        return (int) Db::getInstance()->getValue($sql);
    }

    protected function getTargetSuppliersByIds($ids, $idLang)
    {
        if (empty($ids)) {
            return [];
        }

        $sql = new DbQuery();
        $sql->select('s.id_supplier as id, s.name');
        $sql->from('supplier', 's');
        $sql->where('s.id_supplier IN (' . implode(',', array_map('intval', $ids)) . ')');
        $sql->orderBy('FIELD(s.id_supplier, ' . implode(',', array_map('intval', $ids)) . ')');

        $results = Db::getInstance()->executeS($sql);

        return array_map(function ($row) {
            return [
                'id' => (int) $row['id'],
                'name' => $row['name'],
            ];
        }, $results ?: []);
    }

    /**
     * AJAX: Get filterable attributes and features for product search
     */
    protected function ajaxGetTargetFilterableAttributes()
    {
        $idLang = (int) $this->context->language->id;
        $idShop = (int) $this->context->shop->id;

        $attributes = [];
        $features = [];

        // Get attribute groups with their values and product counts
        $attrGroups = Db::getInstance()->executeS('
            SELECT ag.id_attribute_group, agl.name
            FROM ' . _DB_PREFIX_ . 'attribute_group ag
            INNER JOIN ' . _DB_PREFIX_ . 'attribute_group_lang agl
                ON ag.id_attribute_group = agl.id_attribute_group AND agl.id_lang = ' . $idLang . '
            INNER JOIN ' . _DB_PREFIX_ . 'attribute_group_shop ags
                ON ag.id_attribute_group = ags.id_attribute_group AND ags.id_shop = ' . $idShop . '
            ORDER BY ag.position ASC
        ');

        foreach ($attrGroups ?: [] as $group) {
            // Get total product count for this attribute group (distinct products that have ANY attribute in this group)
            $groupProductCount = (int) Db::getInstance()->getValue('
                SELECT COUNT(DISTINCT pa.id_product)
                FROM ' . _DB_PREFIX_ . 'product_attribute pa
                INNER JOIN ' . _DB_PREFIX_ . 'product_attribute_combination pac ON pac.id_product_attribute = pa.id_product_attribute
                INNER JOIN ' . _DB_PREFIX_ . 'attribute a ON a.id_attribute = pac.id_attribute
                INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON ps.id_product = pa.id_product AND ps.id_shop = ' . $idShop . '
                WHERE a.id_attribute_group = ' . (int) $group['id_attribute_group'] . ' AND ps.active = 1
            ');

            // Get attribute values with product counts
            $values = Db::getInstance()->executeS('
                SELECT a.id_attribute, al.name, a.color,
                       (SELECT COUNT(DISTINCT pa.id_product)
                        FROM ' . _DB_PREFIX_ . 'product_attribute pa
                        INNER JOIN ' . _DB_PREFIX_ . 'product_attribute_combination pac ON pac.id_product_attribute = pa.id_product_attribute
                        INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON ps.id_product = pa.id_product AND ps.id_shop = ' . $idShop . '
                        WHERE pac.id_attribute = a.id_attribute AND ps.active = 1
                       ) as product_count
                FROM ' . _DB_PREFIX_ . 'attribute a
                INNER JOIN ' . _DB_PREFIX_ . 'attribute_lang al
                    ON a.id_attribute = al.id_attribute AND al.id_lang = ' . $idLang . '
                INNER JOIN ' . _DB_PREFIX_ . 'attribute_shop ash
                    ON a.id_attribute = ash.id_attribute AND ash.id_shop = ' . $idShop . '
                WHERE a.id_attribute_group = ' . (int) $group['id_attribute_group'] . '
                ORDER BY a.position ASC
            ');

            $attributes[] = [
                'id' => (int) $group['id_attribute_group'],
                'name' => $group['name'],
                'count' => $groupProductCount,
                'values' => array_map(function ($v) {
                    return [
                        'id' => (int) $v['id_attribute'],
                        'name' => $v['name'],
                        'color' => $v['color'] ?: null,
                        'count' => (int) $v['product_count'],
                    ];
                }, $values ?: []),
            ];
        }

        // Get feature groups with their values and product counts
        $featGroups = Db::getInstance()->executeS('
            SELECT f.id_feature, fl.name
            FROM ' . _DB_PREFIX_ . 'feature f
            INNER JOIN ' . _DB_PREFIX_ . 'feature_lang fl
                ON f.id_feature = fl.id_feature AND fl.id_lang = ' . $idLang . '
            INNER JOIN ' . _DB_PREFIX_ . 'feature_shop fs
                ON f.id_feature = fs.id_feature AND fs.id_shop = ' . $idShop . '
            ORDER BY f.position ASC
        ');

        foreach ($featGroups ?: [] as $group) {
            // Get total product count for this feature group (distinct products that have ANY value in this feature)
            $groupProductCount = (int) Db::getInstance()->getValue('
                SELECT COUNT(DISTINCT fp.id_product)
                FROM ' . _DB_PREFIX_ . 'feature_product fp
                INNER JOIN ' . _DB_PREFIX_ . 'feature_value fv ON fv.id_feature_value = fp.id_feature_value
                INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON ps.id_product = fp.id_product AND ps.id_shop = ' . $idShop . '
                WHERE fv.id_feature = ' . (int) $group['id_feature'] . ' AND ps.active = 1
            ');

            // Get feature values with product counts
            $values = Db::getInstance()->executeS('
                SELECT fv.id_feature_value, fvl.value as name,
                       (SELECT COUNT(DISTINCT fp2.id_product)
                        FROM ' . _DB_PREFIX_ . 'feature_product fp2
                        INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON ps.id_product = fp2.id_product AND ps.id_shop = ' . $idShop . '
                        WHERE fp2.id_feature_value = fv.id_feature_value AND ps.active = 1
                       ) as product_count
                FROM ' . _DB_PREFIX_ . 'feature_value fv
                INNER JOIN ' . _DB_PREFIX_ . 'feature_value_lang fvl
                    ON fv.id_feature_value = fvl.id_feature_value AND fvl.id_lang = ' . $idLang . '
                WHERE fv.id_feature = ' . (int) $group['id_feature'] . '
                ORDER BY fvl.value ASC
            ');

            $features[] = [
                'id' => (int) $group['id_feature'],
                'name' => $group['name'],
                'count' => $groupProductCount,
                'values' => array_map(function ($v) {
                    return [
                        'id' => (int) $v['id_feature_value'],
                        'name' => $v['name'],
                        'count' => (int) $v['product_count'],
                    ];
                }, $values ?: []),
            ];
        }

        $this->ajaxDie(json_encode([
            'success' => true,
            'data' => [
                'attributes' => $attributes,
                'features' => $features,
            ],
        ]));
    }

    /**
     * AJAX: Get attribute groups for dropdown filter
     */
    protected function ajaxGetAttributeGroups()
    {
        $idLang = (int) $this->context->language->id;
        $idShop = (int) $this->context->shop->id;

        $groups = Db::getInstance()->executeS('
            SELECT ag.id_attribute_group as id, agl.name,
                   (SELECT COUNT(DISTINCT a.id_attribute)
                    FROM ' . _DB_PREFIX_ . 'attribute a
                    INNER JOIN ' . _DB_PREFIX_ . 'attribute_shop ash ON ash.id_attribute = a.id_attribute AND ash.id_shop = ' . $idShop . '
                    WHERE a.id_attribute_group = ag.id_attribute_group
                   ) as count
            FROM ' . _DB_PREFIX_ . 'attribute_group ag
            INNER JOIN ' . _DB_PREFIX_ . 'attribute_group_lang agl
                ON ag.id_attribute_group = agl.id_attribute_group AND agl.id_lang = ' . $idLang . '
            INNER JOIN ' . _DB_PREFIX_ . 'attribute_group_shop ags
                ON ag.id_attribute_group = ags.id_attribute_group AND ags.id_shop = ' . $idShop . '
            ORDER BY ag.position ASC
        ');

        $this->ajaxDie(json_encode([
            'success' => true,
            'groups' => $groups ?: [],
        ]));
    }

    /**
     * AJAX: Get attribute values for a specific attribute group
     */
    protected function ajaxGetAttributeValues()
    {
        $idAttributeGroup = (int) Tools::getValue('id_attribute_group');

        if (!$idAttributeGroup) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'message' => $this->transEntitySelector('Attribute group ID is required.'),
            ]));
            return;
        }

        $idLang = (int) $this->context->language->id;
        $idShop = (int) $this->context->shop->id;

        // Get values with product count for each
        $values = Db::getInstance()->executeS('
            SELECT a.id_attribute as id, al.name,
                   (SELECT COUNT(DISTINCT pa.id_product)
                    FROM ' . _DB_PREFIX_ . 'product_attribute_combination pac
                    INNER JOIN ' . _DB_PREFIX_ . 'product_attribute pa ON pac.id_product_attribute = pa.id_product_attribute
                    INNER JOIN ' . _DB_PREFIX_ . 'product_shop ps ON pa.id_product = ps.id_product AND ps.id_shop = ' . $idShop . ' AND ps.active = 1
                    WHERE pac.id_attribute = a.id_attribute
                   ) as product_count
            FROM ' . _DB_PREFIX_ . 'attribute a
            INNER JOIN ' . _DB_PREFIX_ . 'attribute_lang al
                ON a.id_attribute = al.id_attribute AND al.id_lang = ' . $idLang . '
            INNER JOIN ' . _DB_PREFIX_ . 'attribute_shop ash
                ON a.id_attribute = ash.id_attribute AND ash.id_shop = ' . $idShop . '
            WHERE a.id_attribute_group = ' . $idAttributeGroup . '
            ORDER BY a.position ASC, al.name ASC
        ');

        $this->ajaxDie(json_encode([
            'success' => true,
            'values' => $values ?: [],
        ]));
    }

    /**
     * AJAX: Get feature groups for dropdown filter
     */
    protected function ajaxGetFeatureGroups()
    {
        $idLang = (int) $this->context->language->id;
        $idShop = (int) $this->context->shop->id;

        $groups = Db::getInstance()->executeS('
            SELECT f.id_feature as id, fl.name,
                   (SELECT COUNT(DISTINCT fv.id_feature_value)
                    FROM ' . _DB_PREFIX_ . 'feature_value fv
                    WHERE fv.id_feature = f.id_feature
                   ) as count
            FROM ' . _DB_PREFIX_ . 'feature f
            INNER JOIN ' . _DB_PREFIX_ . 'feature_lang fl
                ON f.id_feature = fl.id_feature AND fl.id_lang = ' . $idLang . '
            INNER JOIN ' . _DB_PREFIX_ . 'feature_shop fs
                ON f.id_feature = fs.id_feature AND fs.id_shop = ' . $idShop . '
            ORDER BY f.position ASC
        ');

        $this->ajaxDie(json_encode([
            'success' => true,
            'groups' => $groups ?: [],
        ]));
    }

    /**
     * AJAX: Get holidays preview for a country
     */
    protected function ajaxGetHolidaysPreview()
    {
        header('Content-Type: application/json; charset=utf-8');
        $idCountry = (int) Tools::getValue('id_country');

        if (!$idCountry) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'message' => 'Country ID is required.',
            ]));
            return;
        }

        // Check if PublicHoliday class exists
        if (!class_exists('MyPrestaRocks\\PublicHolidays\\PublicHoliday')) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'message' => 'Holiday system not available.',
            ]));
            return;
        }

        $idLang = (int) $this->context->language->id;
        $currentYear = (int) date('Y');

        // Get country info
        $country = new \Country($idCountry, $idLang);
        if (!\Validate::isLoadedObject($country)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'message' => 'Country not found.',
            ]));
            return;
        }

        // Get holidays for current and next year
        $holidays = [];
        try {
            $holidaysThisYear = \MyPrestaRocks\PublicHolidays\PublicHoliday::getHolidaysForCountry(
                $idCountry,
                $currentYear,
                $idLang
            );
            $holidaysNextYear = \MyPrestaRocks\PublicHolidays\PublicHoliday::getHolidaysForCountry(
                $idCountry,
                $currentYear + 1,
                $idLang
            );

            $allHolidays = array_merge($holidaysThisYear ?: [], $holidaysNextYear ?: []);

            // Filter to show only future holidays and format
            $today = date('Y-m-d');
            foreach ($allHolidays as $holiday) {
                if ($holiday['holiday_date'] >= $today) {
                    $holidays[] = [
                        'date' => $holiday['holiday_date'],
                        'date_formatted' => \Tools::displayDate($holiday['holiday_date'], null, false),
                        'day_of_week' => date('l', strtotime($holiday['holiday_date'])),
                        'name' => $holiday['display_name'] ?? $holiday['name'],
                        'type' => $holiday['type'],
                        'recurring' => (bool) $holiday['recurring'],
                    ];
                }
            }

            // Sort by date
            usort($holidays, function ($a, $b) {
                return strcmp($a['date'], $b['date']);
            });

            // Limit to next 20 holidays
            $holidays = array_slice($holidays, 0, 20);
        } catch (\Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'message' => $e->getMessage(),
            ]));
            return;
        }

        $this->ajaxDie(json_encode([
            'success' => true,
            'country' => [
                'id' => $idCountry,
                'name' => $country->name,
                'iso_code' => $country->iso_code,
            ],
            'holidays' => $holidays,
            'total_count' => count($holidays),
        ], JSON_UNESCAPED_UNICODE));
    }

    /**
     * AJAX: Get holidays for multiple countries
     * Used for the condition-match-count badge and combined preview
     */
    protected function ajaxGetHolidaysForCountries()
    {
        header('Content-Type: application/json; charset=utf-8');
        $countryIds = Tools::getValue('country_ids');
        $countOnly = (bool) Tools::getValue('count_only', false);

        // Parse country IDs (can be comma-separated string or array)
        if (is_string($countryIds)) {
            $countryIds = array_filter(array_map('intval', explode(',', $countryIds)));
        } elseif (is_array($countryIds)) {
            $countryIds = array_filter(array_map('intval', $countryIds));
        } else {
            $countryIds = [];
        }

        if (empty($countryIds)) {
            $this->ajaxDie(json_encode([
                'success' => true,
                'holidays' => [],
                'total_count' => 0,
                'countries' => [],
            ]));
            return;
        }

        // Check if PublicHoliday class exists
        if (!class_exists('MyPrestaRocks\\PublicHolidays\\PublicHoliday')) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'message' => 'Holiday system not available.',
            ]));
            return;
        }

        $idLang = (int) $this->context->language->id;
        $currentYear = (int) date('Y');
        $today = date('Y-m-d');

        $allHolidays = [];
        $countriesInfo = [];

        try {
            foreach ($countryIds as $idCountry) {
                $country = new \Country($idCountry, $idLang);
                if (!\Validate::isLoadedObject($country)) {
                    continue;
                }

                $countriesInfo[$idCountry] = [
                    'id' => $idCountry,
                    'name' => $country->name,
                    'iso_code' => $country->iso_code,
                ];

                // Get holidays for current and next year
                $holidaysThisYear = \MyPrestaRocks\PublicHolidays\PublicHoliday::getHolidaysForCountry(
                    $idCountry,
                    $currentYear,
                    $idLang
                );
                $holidaysNextYear = \MyPrestaRocks\PublicHolidays\PublicHoliday::getHolidaysForCountry(
                    $idCountry,
                    $currentYear + 1,
                    $idLang
                );

                $countryHolidays = array_merge($holidaysThisYear ?: [], $holidaysNextYear ?: []);

                // Filter to show only future holidays
                foreach ($countryHolidays as $holiday) {
                    if ($holiday['holiday_date'] >= $today) {
                        $allHolidays[] = [
                            'date' => $holiday['holiday_date'],
                            'date_formatted' => \Tools::displayDate($holiday['holiday_date'], null, false),
                            'day_of_week' => date('l', strtotime($holiday['holiday_date'])),
                            'name' => $holiday['display_name'] ?? $holiday['name'],
                            'type' => $holiday['type'],
                            'recurring' => (bool) $holiday['recurring'],
                            'country_id' => $idCountry,
                            'country_name' => $country->name,
                            'country_iso' => $country->iso_code,
                        ];
                    }
                }
            }

            // Sort by date
            usort($allHolidays, function ($a, $b) {
                return strcmp($a['date'], $b['date']);
            });

            $totalCount = count($allHolidays);

            // If only count requested, return early
            if ($countOnly) {
                $this->ajaxDie(json_encode([
                    'success' => true,
                    'total_count' => $totalCount,
                    'countries' => array_values($countriesInfo),
                ], JSON_UNESCAPED_UNICODE));
                return;
            }

            // Limit to next 50 holidays for full preview
            $allHolidays = array_slice($allHolidays, 0, 50);
        } catch (\Exception $e) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'message' => $e->getMessage(),
            ]));
            return;
        }

        $this->ajaxDie(json_encode([
            'success' => true,
            'holidays' => $allHolidays,
            'total_count' => $totalCount,
            'countries' => array_values($countriesInfo),
        ], JSON_UNESCAPED_UNICODE));
    }

    /**
     * AJAX: Get zones for country filter dropdown
     */
    protected function ajaxGetZonesForFilter()
    {
        $idLang = (int) $this->context->language->id;

        $zones = \Zone::getZones(true);
        $result = [];

        foreach ($zones as $zone) {
            $result[] = [
                'id' => (int) $zone['id_zone'],
                'name' => $zone['name'],
            ];
        }

        $this->ajaxDie(json_encode([
            'success' => true,
            'zones' => $result,
        ], JSON_UNESCAPED_UNICODE));
    }
}
