<?php

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

use PrestaShop\Module\PolarisPrestaTheme\Classes\AutoUpdater;
use PrestaShop\Module\PolarisPrestaTheme\Config\ConfigTheme;
use PrestaShop\Module\PolarisPrestaTheme\Installer\Install;
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
use PrestaShop\Module\PolarisPrestaTheme\Classes\ThemeTools;
use PrestaShop\PrestaShop\Adapter\Presenter\Product\ProductListingPresenter;
use PrestaShop\PrestaShop\Adapter\Image\ImageRetriever;
use PrestaShop\PrestaShop\Adapter\Product\PriceFormatter;
use PrestaShop\PrestaShop\Adapter\Product\ProductColorsRetriever;

class PolarisPrestaTheme extends Module
{    
    /**
     * Numéro de version du module
     * 
     * @var string
     */
    public const MODULE_VERSION = '1.4.6';

    /**
     * Version installée
     */
    public string $installed_version = "";

    /**
     * Préfixe général du module utilisé pour stocker des informations dans les différentes tables de prestashop sans conflit de nommage
     * 
     * @var string
     */
    public const MODULE_PREFIX = 'pt_';

    /**
     * La page actuelle est en cache
     * 
     * @var boolean
     */
    private $_PageCached = false;

    /**
     * Configuration du cache
     */
    private $_CacheCfg = null;

     /**
     * Configuration du thème
     */
    private $_Config = null;

    /**
     * Cache de langue
     */
    private $_LangCache = [];


     /**
     * Instanciation du module
     */
    public function __construct()
    {        
        $this->name = strtolower(get_class($this)); // Doit correspondre au nom du module, dans notre cas : monmodule
        $this->tab = 'front_office_features'; // Correspond à l'onglet de catégorisation du module, pour tous les connaitre : https://devdocs.prestashop.com/1.7/modules/creation/
        $this->author = 'VEGA Informatique'; // L'auteur
        $this->version = self::MODULE_VERSION;              // Version disponible du module
        $this->installed_version = self::MODULE_VERSION;    // Version actuelle du module
        $this->ps_versions_compliancy = [ // Permet de limiter les versions compatibles
            'min' => '8.0',
            'max' => _PS_VERSION_
        ];
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->l('PolarisPrestaTheme'); // Nom d'affichage
        $this->description = $this->l('Fourniture et gestion d\'un thème universel adaptable pour les clients de VEGA Informatique.'); // Description du module
        $this->confirmUninstall = $this->l('Êtes-vous sûr de vouloir désinstaller ce module ?');     

        // On déclenche les mises à jour nécessaires
        $currentlyInstalledVersion = Configuration::get(self::MODULE_PREFIX . "VERSION");
        if (!$currentlyInstalledVersion || version_compare($currentlyInstalledVersion, self::MODULE_VERSION) < 0 || isset($_REQUEST["force_update"]))
        {
            $this->update();
            Configuration::updateValue(self::MODULE_PREFIX . "VERSION", self::MODULE_VERSION);
        }

        // Chargement de la configuration du cache
        $this->_CacheCfg = $this->loadCacheCfg();
    }

    /**
     * Charge la configuration du cache
     */
    public function loadCacheCfg() : array
    {
        $cacheCfg = [
            "ENABLED" => false,
            "INVALID_PRODUCT_CACHE" => true,
            "CACHE_MAX_AGE" => 3600,
        ];

        $savedCfg = Configuration::get(self::MODULE_PREFIX . "CACHE_CFG");
        if ($savedCfg)
        {
            $savedCfg = json_decode($savedCfg, true);
            if ($savedCfg)
            {
                // Pour chacun des propriétés du cache, on regarde si elle existe dans la sauvegarde
                foreach (array_keys($cacheCfg) as $key)
                {
                    if (isset($savedCfg[$key]))
                        $cacheCfg[$key] = $savedCfg[$key];
                }                
            }
        }

        return $cacheCfg;
    }

    /**
     * Retourne la configuration du cache chargée !
     */
    public function getCacheCfg() : array
    {
        return $this->_CacheCfg;
    }
    
    /**
     * Installation du module
     * 
     * @return bool
     */
    public function install(): bool
    {
        // Ajout de l'autoload car il n'existe pas encore à cet endroit !
        require_once _PS_MODULE_DIR_ . $this->name . '/vendor/autoload.php';

        return (parent::install()
            && Install::installTheme($this)
            && Install::installEmployeeProfile()
            && Install::hidePrestaShopBackOfficeIdentity($this)
            && Install::installTab($this)
            && Install::deleteModule()
            && Install::registerHooks($this)
            && Install::wishlistDb($this)
        );
    }

    /**
     * update
     * Mise à jour de l'environnement du module :
     *  - base de données
     *  - hooks
     *  - thème ...
     *
     * @return bool vrai si pas de soucis, faux si erreurs
     */
    public function update(): bool
    {
        // Ajout de l'autoload car il n'existe pas encore à cet endroit !
        require_once _PS_MODULE_DIR_ . $this->name . '/vendor/autoload.php';

        if (Install::registerHooks($this))
            return true;
        
        return false;
    }


    /**
    * Désinstallation du module
    */
    public function uninstall(): bool
    {
        return (parent::uninstall()
            && Install::restorePrestaShopBackOfficeIdentity()
            && Install::uninstallTab()
            && Install::uninstallTheme()
        );
    }


    /**
     * hookDisplayBackOfficeHeader
     * 
     * Ajoute le CSS du module uniquement dans la pages de notre module
     * 
     */
    public function hookDisplayBackOfficeHeader(){

        // Récupérer l'URL actuelle
        $current_url = $_SERVER['REQUEST_URI'];
        

        // Vérifier si le module est présent dans l'URL
        if (str_contains($current_url, $this->name)) {
            // Ajouter le CSS seulement sur les pages du module
            $this->context->controller->addCSS($this->_path . 'views/css/adminPresta_theme.css');
            $this->context->controller->addCSS(_PS_BO_ALL_THEMES_DIR_ . 'new-theme/js/components/form/choice-tree.js');
            $this->context->controller->addJS($this->_path . 'views/js/admin.js');
       }

       // Toutes les 24h, on regarde s'il n'y a pas une mise à jour du module
        $lastCheck = \Configuration::get($this->name . '_last_update_check');
        if (!$lastCheck || (time() - $lastCheck) > 86400)
        {                
            // On enregistre la date de la dernière vérification
            \Configuration::updateValue($this->name . '_last_update_check', time());

            $updater = new AutoUpdater($this);
            $updater->searchUpdate();
        }
        
    }
    
    /*
    * Page de configuration
    */
    public function getContent()
    {
        Tools::redirectAdmin(
            SymfonyContainer::getInstance()->get('router')->generate($this->name . '_changelog')
        );
    }

    /**
     * getConfig
     * 
     * Retourne la configuration du module
     * 
     * @return ConfigTheme
     */
    public function getConfig()
    {        
        if (!$this->_Config)
            $this->_Config = ConfigTheme::load();

        if (!$this->_Config)
            throw new \Exception('Impossible de charger la configuration du module PolarisPrestaTheme');

        return $this->_Config;
    }

    /**
     * Renvoi vrai si la page actuelle devrait être mise en cache
     */
    public function isPageCached()
    {
        return $this->_PageCached;
    }


    
   public function hookDisplayHome()
    {
        
        $config = $this->getConfig();
        $blocksData = [];

        $orderedBlocks = $config->configBlockHome ?? [];

        foreach ($orderedBlocks as $block) {

            switch ($block['type']) {

                /* =============================
                * SLIDER
                * ============================= */
                case 'slider_group':

                    $param = $config->configSliderGroup[$block['ref']] ?? null;

                    if (!$param || !$param->isVisible || $param->width ) {
                        break;
                    }
                  

                    $blocksData[] = [
                        'type'   => 'slider_group',
                        'slider' => $param,
                        'slides' => $param->items ?? [],
                    ];

                break;


                /* =============================
                * CATEGORIES
                * ============================= */
                case 'category_group':

                    $param = $config->configCategoryGroup[$block['ref']] ?? null;

                    if (!$param || !$param->isVisible) {
                        break;
                    }

                    

                    $blocksData[] = [
                        'type'       => 'category_group',
                        'group'      => $param,
                        'categories' => $param->items ?? [],
                    ];

                break;


                
                /* =============================
                * PRODUIT GROUP
                * ============================= */


                case 'product_group':

                    $param = $config->configProductHomeGroup[$block['ref']] ?? null;

                    if (!$param || !$param->isVisible) {
                        break;
                    }

                    // 👇 Les données sont dans items[0] (singleItemMode)
                    $item = $param->items[0] ?? null;

                    

                    if (!$item) {
                        break;
                    }

                    $products = [];

                    switch ($item['typeProduit']) {

                    
                        case 'new':
                            $raw = Product::getNewProducts(
                                (int)$this->context->language->id,
                                1,
                                $item['nbProduit']
                            );
                            $products = $this->hydrateProducts($raw);
                        break;

                        case 'sale':
                            $raw = Product::getPricesDrop(
                                (int)$this->context->language->id,
                                1,
                                $item['nbProduit']
                            );
                            $products = $this->hydrateProducts($raw);
                        break;

                        case 'custom':
                            
                            $ids = !empty($item['productIds'])
                                ? array_filter(array_map('intval', explode(',', $item['productIds'])))
                                : [];

                            $raw = [];
                            foreach ($ids as $idProduct) {
                                $p = new Product(
                                    $idProduct,
                                    false,
                                    (int)$this->context->language->id
                                );
                                if (!$p->id || !$p->active) {
                                    continue;
                                }
                                $raw[] = [
                                    'id_product'          => $p->id,
                                    'link_rewrite'        => is_array($p->link_rewrite)
                                                                ? $p->link_rewrite[(int)$this->context->language->id]
                                                                : $p->link_rewrite,
                                    'id_category_default' => $p->id_category_default,
                                    'name'                => is_array($p->name)
                                                                ? $p->name[(int)$this->context->language->id]
                                                                : $p->name,
                                ];
                            }
                            $products = $this->hydrateProducts($raw);
                        break;
                    }

                    $blocksData[] = [
                        'type'     => 'product_group',
                        'param'    => $param,
                        'item'     => $item,      // 👈 title, typeProduit etc. accessibles dans Twig
                        'products' => $products,
                    ];

                break;


                
                /* =============================
                * Marque
                * ============================= */
                case 'brand':

                    $param = $config->configBrand[$block['ref']] ?? null;
                    

                    if (!$param || !$param->isVisible) {
                        break;
                    }
                    

                    $blocksData[] = [
                        'type'     => 'brand',
                        'brands' => $param ?? [],
                    ];
                    
                break;

                /* =============================
                * Reassurance
                * ============================= */

                case 'Reassurance_group':

                    $param = $config->configReassuranceGroup[$block['ref']] ?? null;

                    if (!$param || !$param->isVisible || $param->hook != 'home') {
                        break;
                    }


                    $blocksData[] = [
                        'type'       => 'reassurance_group',
                        'group'      => $param,
                        'reassurances' => $param->items ?? [],
                    ];

                break;


                /* =============================
                * About
                * ============================= */

                case 'About_group':

                    $param = $config->configAboutGroup[$block['ref']] ?? null;

                    if (!$param || !$param->isVisible) {
                        break;
                    }


                    $blocksData[] = [
                        'type'       => 'about_group',
                        'group'      => $param,
                        'about' => $param->items ?? [],
                    ];

                break;
            }
        }

        $this->context->smarty->assign([
            'orderedBlocks' => $blocksData
        ]);

        return $this->display(__FILE__, '/views/templates/hook/home.tpl');
    }


    /* ======================================================
     * HYDRATE PRODUITS (COMMUN PROMO / NOUVEAUX)
     * ====================================================== */
    private function hydrateProducts($products)
    {
        if (empty($products)) {
            return [];
        }

        $assembler = new ProductAssembler($this->context);
        $presenter  = new ProductListingPresenter(
            new ImageRetriever($this->context->link),
            $this->context->link,
            new PriceFormatter(),
            new ProductColorsRetriever(),
            $this->context->getTranslator()
        );

        $presenterFactory = new \ProductPresenterFactory($this->context);
        $presentationSettings = $presenterFactory->getPresentationSettings();

        $presented = [];
        foreach ($products as $rawProduct) {
            $assembled = $assembler->assembleProduct($rawProduct);
            $presented[] = $presenter->present(
                $presentationSettings,
                $assembled,
                $this->context->language
            );
        }

        return $presented;
    }

    /**
     * hookDisplayFooterBefore
     * 
     * Affiche le contenu reassurance avant le footer
     * 
     * @return string
     */
    public function hookDisplayFooterBefore(){
        $config = $this->getConfig();
         
        foreach($config->configReassuranceGroup as $reassurance){
             
            if($reassurance->hook === 'footerbefore'){
               
                $this->context->smarty->assign([
                    'reassurances' => $reassurance->items
                ]);

                return $this->display(__FILE__, '/views/templates/hook/reassurance.tpl');
            }

        }

        
    }


    /**
     * hookDisplayHomeSliderFullWidth
     * 
     * Affiche le slider en pleine largeur
     * 
     * @return string
     */
    public function hookDisplayHomeSliderFullWidth(){
         $config = $this->getConfig();

        foreach ($config->configSliderGroup as $slider) {

            if (!$slider->width || !$slider->isVisible) {
                continue;
            }

            $this->context->smarty->assign([
                'slider' => $slider,
                'slides' => $slider->items ?? []
            ]);


             return $this->display(__FILE__, '/views/templates/hook/slider.tpl');
            
        }
    }


    /**
     * hookDisplayDeliveryEstimation
     * 
     * Affiche l'estimation de livraison sur la page produit 
     * 
     * @return string
     */
    public function hookDisplayDeliveryEstimation()
    {
       
        $data =ThemeTools::getShippingEstimator();
        

         $this->context->smarty->assign([
            'delivery' => $data['delivery'], // Estimated delivery date
            'shippingTime' => $data['shippingTime'], // Shipping cutoff time
        ]);
        
        return $this->display(__FILE__, '/views/templates/hook/delivery_estimation.tpl');
        
    }

    public function hookDisplayShippingEstimatorCheckout(array $params)
    {
        
        $carrierId = $params['carrier']; // Get the selected carrier ID
        $data = ThemeTools::getShippingEstimator();

        
        // Assign variables to Smarty template
        $this->context->smarty->assign([
            'delivery' => $data['deliverys'][$carrierId], // Delivery for this carrier
            'shippingTime' => $data['shippingTime'],      // Shipping cutoff
        ]);

        // Display the checkout template
        return $this->display(__FILE__, '/views/templates/hook/shippingestimatorcheckout.tpl');
    }


    /**
     * hookDisplayFreeDelivery
     * 
     * Affiche le message de livraison gratuite en fonction du montant dans le panier du client 
     * 
     * @return string
     */
    public function hookDisplayFreeDelivery(){
        $config = $this->getConfig();

        $livraison = $config->configLivraison[0];
        
        $cart = $this->context->cart;
        $minimumAmout =  $livraison->freeDelivery;
        $cartTotal = $cart->getOrderTotal(true, 4);
        $amount_left = $minimumAmout- $cartTotal;
        $this->context->smarty->assign([
            'amount_left' => $amount_left,
            'free'=>$minimumAmout
        ]);
        return $this->display(__FILE__, '/views/templates/hook/free-delivery.tpl');
    }


    /**
     * hookDisplaySociaux
     * 
     * Affiche les icones de réseaux sociaux
     * 
     * @return string
     */
    public function hookDisplaySociaux(){
        $config = $this->getConfig();

        if (count($config->configResaux) > 0)
        {
            $this->context->smarty->assign([
                'sociaux' => $config->configResaux,
            ]);

            return $this->display(__FILE__, '/views/templates/hook/sociaux.tpl');
        }
        else
            return '';
    }

    /**
     * hookDisplayFilter
     * 
     * degreffe le module ps_facetedsearch du hook displayLeftColumn (hook de base) et le greffe sur le hook displayFilter (hook personnalisé)
     * 
     * @return string
     */
    public function hookDisplayFilter(){

        $module = Module::getInstanceByName('ps_facetedsearch');
        if (Validate::isLoadedObject($module)) {
            // Dégreffer le module du hook displayLeftColumn
            $module->unregisterHook('displayLeftColumn');
        
            // Greffer le module sur ton hook personnalisé
            $module->registerHook('displayFilter');
        }
        else
            throw new Exception('Le module ps_facetedsearch n\'est pas installé');        
    }


    /**
     * hookDisplayHeader
     * 
     * Permet de passer des variables globale qui sont accessible dans le front end sur n'importe quelle page 
     * 
     */
    public function hookDisplayHeader(){
        $config = $this->getConfig();

        $base_url = \Context::getContext()->shop->getBaseURL();

        // recuperer l'ID du gtag dans la configuration            
        $gtag_id = $config->configTracking[0]->gtag ?? null;
        $facebookPixelId = $config->configTracking[0]->fbPixel ?? null;
        // si l'ID du gtag est défini, on l'ajoute dans les variables globales js
        if ($gtag_id) {
                Media::addJsDef([
                    'gtag_id' => $gtag_id,
                    'fb_id' => $facebookPixelId,
                ]);
        }

        $this->context->controller->registerJavascript(
            'module-prestatheme-wishlist-btn', // identifiant unique
            'modules/' . $this->name . '/views/js/wishlist-btn.js',
            [
                'position' => 'bottom',   // ou 'head' si besoin
                'priority' => 150,        // >100 = après jQuery
            ]
        );

        $this->context->controller->registerJavascript(
            'module-prestatheme-estimation', // identifiant unique
            'modules/' . $this->name . '/views/js/estimation.js',
            [
                'position' => 'bottom',   // ou 'head' si besoin
                'priority' => 150,        // >100 = après jQuery
            ]
        );
            
        // Assigner les variables globales
        $this->context->smarty->assign([
            'catalogue' => $config->configCatalogue,
            'sliderParameter' => $config->configSliderGroup,
            'productParameter'=> $config->configProduct,
            'bandeauParameter'=> $config->configTopBandeauParameter,
            'bandeaux' => $config->configTopBandeau,
            'base_url' => $base_url,
        ]);
    }


    /**
     * hookDisplayBandeau
     * 
     * Affiche le bandeau en haut de la page
     * 
     * @return string
     */
    public function hookDisplayBandeau(){
        $config = $this->getConfig();

        if (isset($config))
        {
            $this->context->smarty->assign([
                'bandeaux' => $config->configTopBandeau,
            ]);


            return $this->display(__FILE__, '/views/templates/hook/bandeau.tpl');
        }
        else
            return ''; // Si la configuration du bandeau est vide, ne rien afficher
    }

    /**
     * hookDisplayBandeau
     * 
     * Affiche le pop up de cookie
     * 
     * @return string
     */
    public function hookDisplayCookie()
    {
        $this->context->controller->addJS($this->_path . 'views/js/cookie.js');
        return $this->display(__FILE__, '/views/templates/hook/cookie.tpl');
    }


    /**
     * hookDisplayPopup
     * 
     * Affiche le pop up newsletter
     * 
     * @return string
     */
    public function hookDisplayPopup(){
        $config = $this->getConfig();
        $this->context->smarty->assign([
                'popup' => $config->configPopUp,
            ]);
        return $this->display(__FILE__, '/views/templates/hook/popup.tpl');
    }

    public function hookDisplayMagasin(){
        $config = $this->getConfig();

        if (isset($config))
        {
            $this->context->smarty->assign([
                'stores' => $config->configStore,
            ]);

            return $this->display(__FILE__, '/views/templates/hook/store.tpl');
        }
        else
            return ''; // Si la configuration du magasin est vide, ne rien afficher
    }

    public function hookDisplayPopupStore(){
        $config = $this->getConfig();
        if (isset($config))
        {
            $this->context->smarty->assign([
                'stores' => $config->configStore,
            ]);

            return $this->display(__FILE__, '/views/templates/hook/popup_store.tpl');
        }
        else
            return ''; // Si la configuration du magasin est vide, ne rien afficher
    }

    public function hookDisplayNav2(){
        
        $this->context->smarty->assign(array(
            'link' => $this->context->link->getModuleLink($this->name, 'wishlist', array(), true)
        ));
        return $this->display(__FILE__, '/views/templates/hook/nav-wishlist.tpl');
    }

    public function hookDisplayWishlistButton($params){
        if (!isset($params['product'])) {
            return '';
        }

        $product = $params['product'];
        $idCustomer = (int)$this->context->customer->id;

        // Vérifie si le produit est déjà dans la wishlist
        $isInWishlist = false;
        if ($idCustomer && isset($product['id_product'])) {
            $isInWishlist = (bool)Db::getInstance()->getValue('
                SELECT COUNT(*) 
                FROM `'._DB_PREFIX_.'wishlist`
                WHERE id_customer = '.(int)$idCustomer.' 
                AND id_product = '.(int)$product['id_product']
            );
        }

        $this->context->smarty->assign([
            'product' => $product,
            'is_in_wishlist' => $isInWishlist,
        ]);

        return $this->display(__FILE__, 'views/templates/hook/wishlist-button.tpl');
    }

    
    public function hookDisplayCustomerAccount()
    {
        $this->context->smarty->assign(array(
            'link' => $this->context->link->getModuleLink('prestatheme', 'wishlist', array(), true)
        ));

        return $this->display(__FILE__, 'views/templates/hook/account-wishlist.tpl');
    }



    /**
     * hookDisplayTop
     * 
     * Affiche le menu en haut de la page
     * 
     * @return string
     */
    public function hookDisplayTop(){
        $config = $this->getConfig();

        if (isset($config))
        {
            $this->context->smarty->assign([
                'menus' => $config->configMenuParent,
            ]);

            return $this->display(__FILE__, '/views/templates/hook/menu.tpl');
        }
        else
            return ''; // Si la configuration du menu est vide, ne rien afficher
    }


    /**
     * hookDisplayAfterBodyOpeningTag
     * 
     * Affiche le code noscript pour le gtag dans le body
     * 
     * @return string
     */
    public function hookDisplayAfterBodyOpeningTag(){
        $config = $this->getConfig();
        
        //Récupération des IDs de tracking
        $googleTagId = $config->configTracking[0]->gtag ?? null;
        $facebookPixelId = $config->configTracking[0]->fbPixel ?? null;

        //Assignation Smarty uniquement si un ID est présent
        if ($googleTagId || $facebookPixelId) {
            $this->context->smarty->assign([
                'gtag_id' => $googleTagId,
                'fb_id' => $facebookPixelId,
            
            ]);

            return $this->display(__FILE__, '/views/templates/hook/noscript-tracking.tpl');
        }         
    }

    /**
     * Gestion du cache :
     * Juste après avoir rafraîchit le stock, on réinitialise la page produit.
     */
    public function hookActionObjectStockAvailableUpdateAfter($params)
    {
        if ($params["object"]->id_product_attribute == 0)
            $this->resetProductCache(new Product($params["object"]->id_product));
    }

    /**
     * Gestion du cache :
     * une fois avoir construit toute la page, on injecte les données dynamiques pour le cache
     */
    public function hookDisplayBeforeBodyClosingTag($params)
    {
        if (!$this->isPageCached()) {
            echo '<div id="data_cache" data-var="' . $this->buildPopulateDynData() . '"></div>';
        }
        else {
            // Si la page est en cache, on n'affiche rien
            echo '<!-- Cached generated page -->';
        }
    }

    /**
     * Gestion du cache :
     * ajout de la bibliothèque JS permettant d'utiliser le cache
     * 
     */
    public function hookActionFrontControllerSetMedia($params)
    {
        // On every pages
        $this->context->controller->registerJavascript(
            'dyncachejs',
            'modules/'.$this->name.'/assets/dyncache.js',
            [
                'position' => 'foot',
                'inline' => false,
                'priority' => 10,
            ]
        );
    }

    /**
     * Gestion du cache : 
     * Quand un produit est mis à jour, on réinitialise le cache de la page produit
     * 
     */
    public function hookActionObjectProductUpdateAfter($params)
    {
        if ($this->_CacheCfg["ENABLED"])
            $this->resetProductCache($params["object"]);
    }

    /**
     * Gestion du cache : 
     * Selon le contrôleur appelé, on décide si on doit mettre en cache la page ou pas
     * 
     */
    public function hookActionDispatcher($params)
    {
        switch ($params["controller_class"]) {
            case "IndexController":
            case "NewProductsController":
            case "BestSalesController":
            case "PricesDropController":
            case "CategoryController":
            case "CmsController":
            case "ContactController":
            case "SitemapController":
            case "StoresController":
            case "ProductController":
            // Modules safe en GET ...
            case "anbloglistModuleFrontController":
            case "anblogblogModuleFrontController":
                if ($_SERVER["REQUEST_METHOD"] == "GET" || $_SERVER["REQUEST_METHOD"] == "HEAD")
                    $this->_PageCached = true;
                break;

            default:
                // DEBUG : var_dump($params["controller_class"]);
                break;
        }

        if ($this->context?->customer?->isLogged())
        {
            $this->_PageCached = false; // Pas de cache pour les pages des clients connectés

            // Cookie pour signaler qu'on ne doit pas mettre en cache la page en direct, sans passer par les fonctions de Prestashop
            setcookie(
                'PS_LOG_IN',      // nom du cookie
                '1',              // valeur (ex: "1" pour connecté)
                time() + 365 * 24 * 3600,    // durée de vie (1 an)
                '/',              // chemin ("/" pour tout le site)
                null,             // domaine (null = domaine courant)
                false,            // HTTPS uniquement (true si SSL obligatoire)
                true              // accessible uniquement via HTTP (pas JS)
            );
        }
        else
        {
            // Si on a le cookie PS_LOG_IN, on le désactive
            if (isset($_COOKIE['PS_LOG_IN'])) {
                setcookie('PS_LOG_IN', '', time() - 3600, '/');
            }
        }

        // Si on doit mettre la page en cache
        if ($this->_CacheCfg["ENABLED"] && $this->_PageCached) 
        {
            // On n'utilise pas de session pour les pages misent en cache, donc on désactive les sessions
            // de manière élégante = les appels ne font rien et ne retourne rien !
            session_set_save_handler(
                function() { return true; }, // open
                function() { return true; }, // close
                function() { return ''; },   // read
                function() { return true; }, // write
                function() { return true; }, // destroy
                function() { return true; }  // gc
            );

            // Tous les caches générés sont publics, donc accessibles par tous les utilisateurs
            // Mais on envoi '' pour désactiver totalement l'envoi automatique des entêtes de cache pour les gérer nous même
            session_cache_limiter("");  // session_start() ne doit pas produire de header de cache exotique
            session_start();            // On démarre la session pour être sûr de pouvoir overrider le cache                                

            // Et on installe notre préférence :
            // - cache de X secondes
            // - cache public pour le navigateur et les proxy. Ces deux derniers doivent revalider à l'issu du temps imparti
            header(sprintf("Cache-Control: public, max-age=%d, must-revalidate, proxy-revalidate, stale-while-revalidate", $this->_CacheCfg["CACHE_MAX_AGE"] ?? 3600), true);
            
            // Pas de PHPSESSID ni de cookie sur ces pages merci !!
            header_remove("Set-Cookie");

            // On écrit rien !
            $this->context->cookie->disallowWriting();
        }
    }

    /*
    * obj {
        prop1 = "ma propriété 1";
        prop2 = "ma propriété 2";
        prop3 = "ma propriété 3";
    }

    => dynDataExport($obj, ["prop1", "prop3"]);
    ----> [ "prop1" => "ma propriété 1", "prop3" => "ma propriété 3", ]
    */
    private function dynDataExport($obj, $props): array
    {
        $data = [];

        foreach ($props as $prop_name)
            $data[$prop_name] = $obj->$prop_name;

        return $data;
    }

    /**
     * Prépare les données dynamiques à envoyer au navigateur
     */
    private function buildPopulateDynData()
    {
        $data = [
            "customer" => ($this->context && $this->context->customer && $this->context->customer->isLogged()) ? $this->dynDataExport($this->context->customer, ["firstname", "lastname", "email"]) : null,
            "cart" => [
                "products_count" => empty($this->context->cookie->id_cart) ? 0 : Cart::getNbProducts($this->context->cookie->id_cart)
            ]
        ];

        // Initiales du client ?
        if ($data["customer"])
        {
            $initials = "";
            if (isset($data["customer"]["lastname"][0]))
                $initials .= strtoupper($data["customer"]["lastname"][0]);
            if (isset($data["customer"]["firstname"][0]))
                $initials .= strtoupper($data["customer"]["firstname"][0]);            

            $data["customer"]["initials"] = $initials;
        }

        return base64_encode(json_encode($data));
    }

    /**
     * Get langs and cache it
     */
    public function getLangs() : array
    {
        if (!($this->_LangCache ?? false))
            $this->_LangCache = Language::getLanguages(true);

        return $this->_LangCache;
    }
    
    /**
     * Réinitialise le cache pour un produit particulier.
     * La réinitiation du cache se fait en ajoutant /purge-cache/ au début de l'URL du produit et en ajoutant cette URL à
     * un fichier à la racine du site du nom de "purge-cache-urls.txt", 
     * ce fichier sera appelé par un script externe (cron ou manuel) qui va parcourir toutes les URL et forcer la régénération du cache
     *
     * @param Product $id_product
     * @return void
     */
    private function resetProductCache(Product $product)
    {
        if (!$this->_CacheCfg["INVALID_PRODUCT_CACHE"])
            return;

        $pa = [];
        $result = Db::getInstance()->executeS('SELECT id_product_attribute FROM `' . _DB_PREFIX_ . 'product_attribute` WHERE `id_product` = ' . (int) $product->id);
        foreach ($result as $row)
            $pa[] = (int) $row["id_product_attribute"];

        $urls = [];
        foreach ($this->getLangs() as $lang)
        {
            $category = Category::getLinkRewrite($product->id_category_default, (int) $lang["id_lang"]);
            $url = $this->context->link->getProductLink($product->id, 
                                        $product->link_rewrite, 
                                        $category, 
                                        $product->ean13,
                                        (int) $lang["id_lang"],
                                    );

            // Remplacement de la base de l'URL pour ajouter /purge-cache/ au début
            $url = str_replace(_PS_BASE_URL_, _PS_BASE_URL_ . '/purge-cache', $url);
            $urls[$url] = $url;

            foreach ($pa as $ipa)
            {
                $url = $this->context->link->getProductLink($product->id, 
                                        $product->link_rewrite, 
                                        $category, 
                                        $product->ean13,
                                        (int) $lang["id_lang"],
                                        null,
                                        $ipa, 
                                        true,
                                        false,
                                        true
                                    );

                // Remplacement de la base de l'URL pour ajouter /purge-cache/ au début
                $url = str_replace(_PS_BASE_URL_, _PS_BASE_URL_ . '/purge-cache', $url);

                $urls[$url] = $url;
            }
        }

        // Regénération du cache sur les pages trouvées par appel CURL
        $final_urllist = implode(PHP_EOL, $urls);
        file_put_contents(_PS_ROOT_DIR_ . '/var/purge-cache-urls.txt', $final_urllist . PHP_EOL, FILE_APPEND | LOCK_EX);
    }

}