<?php
namespace PrestaShop\Module\PolarisPrestaConnector;

/**
 * Gestionnaire de catégories
 * 
 */
class CategoriesManager
{
    /**
     * Synchroniseur
     * 
     * @var Syncer
     */
    private Syncer $syncer;

    /**
     * Catégories dynamiques gérées
     */
    public array $dynCats;

    /**
     * Mapping des catégories dynamiques
     */
    private array $mapping;

    /**
     * Liste exhaustive des catégories Prestashop de destination de mappage
     */
    private array $mappedCats;

    /**
     * Cache des catégories Prestashop
     */
    private array|null $psCatsCache = null;

    /**
     * Retourne les types de catégories dynamiques disponibles pour le mapping
     * 
     */
    public static function & getDynCatsTypes(SyncConfiguration $cfg) : array
    {        
        $cat_types = $cfg->get(SyncConfiguration::CFG_DYN_CAT);

        $meta_categories = $cfg->get(SyncConfiguration::CFG_META_CATEGORIES) ?? null;        
        if ($meta_categories)
        {
            foreach ($meta_categories as $mc_id => $mc_data)
                if ($mc_data)
                    $cat_types[$mc_id] = $mc_data[0];
        }

        return $cat_types;
    }

    /** 
     * Nouvelle instance
     * 
     * @param Syncer $syncer Synchroniseur
     */
    public function __construct(Syncer $syncer, ?array $productsId = null)
    {
        $this->syncer = $syncer;
        // Chargement des mappings, pour chaque catégorie dynamique
        $mapping = [];
        $cfg = $this->syncer->module->getCfg();

        $this->dynCats = self::getDynCatsTypes($cfg);

        foreach ($this->dynCats as $cat => $name)
        {
            $mapping[$cat] = $cfg->get(SyncConfiguration::CFG_MAP_DYN_CAT . $cat);
            if (!isset($mapping[$cat]))
                $mapping[$cat] = [];

            // On convertit les ID en int
            foreach ($mapping[$cat] as $code => $id_category)
            {
                $intmap = [];
                if (is_array($id_category))
                {
                    foreach ($id_category as $id)
                        $intmap[] = (int) $id;
                }
                else
                    $intmap[] = (int) $id_category;

                $mapping[$cat][$code] = $intmap;
            }
        }
        
        $this->mapping = $mapping;
        $this->mappedCats = [];
        foreach ($mapping as $cat => $map)
        {
            foreach ($map as $code => $id_category)
            {
                if (is_array($id_category))
                {
                    foreach ($id_category as $id)
                        $this->mappedCats[$id] = true;
                }
                else
                    $this->mappedCats[$id_category] = true;
            }
        }
    }

    /*
     * Détermine si l'id de la catégorie renseignée est le résultat
     * probable d'un mappage
     */
    public function isMappedCategory(int $id_category) : bool
    {
        return isset($this->mappedCats[$id_category]) && $this->mappedCats[$id_category];
    }

    /**
     * Traduit un code de catégorie en ID de catégories Prestashop selon le mapping prévu
     * 
     * @param string $category Catégorie dynamique
     * @param string $code Code
     * @param string|null $libelle Libellé de la catégorie (optionnel, pour aider à l'auto-création)
     * @return array<int>ID de catégories
     */
    public function translateCode($category, $code, $libelle = null) : array
    {
        if (isset($this->mapping[$category]))
        {
            $id_category = 0;
        
            if (isset($this->mapping[$category][$code]))
            {
                $id_category = $this->mapping[$category][$code];
            }
            else if ($libelle) {
                // Code absent de la liste, auto-création ?
                $cfg = $this->syncer->module->getCfg();                

                // Cas spécial => entièrement vide, on crée au moins le catch-all
                if (count($this->mapping[$category]) == 0)
                {
                    $this->mapping[$category]['*'] = 0;
                    $cfg->set(SyncConfiguration::CFG_MAP_DYN_CAT . $category, ['*' => 0]);   // par défaut, on mappe sur rien
                    $cfg->set(SyncConfiguration::CFG_MAP_DYN_CAT . $category . '_name', ['*' => 'Toutes les entrées']);   // par défaut, on mappe sur rien
                }

                $this->mapping[$category][$code] = 0;   // par défaut, on mappe sur rien
                
                $cfg->setMap(SyncConfiguration::CFG_MAP_DYN_CAT . $category, $code, 0);                
                $cfg->setMap(SyncConfiguration::CFG_MAP_DYN_CAT . $category . '_name', $code, $code . ' - ' . $libelle);
            }

            if ($id_category)
            {
                if (is_array($id_category))
                {
                    if (count($id_category) > 0 && (count($id_category) > 1 || $id_category[0] != 0))
                        return $id_category;
                    if ($code != '*')
                        return $this->translateCode($category, '*');  // catch-all !
                }
                else
                    return [$id_category];
            }
            else if ($code != '*')
                return $this->translateCode($category, '*');  // catch-all !            
        }

        return [];
    }    

    /**
     * Contrôle que les catégories passées en paramètre sont les mêmes que celles du produit
     * 
     * @param array<int> $cats Catégories à vérifier
     * @param array<int> $productCats Catégories du produit
     * @return bool Vrai si les catégories sont les mêmes     
     */
    public function isCatsTheSame(array $cats, array $productCats) : bool
    {
        // On compare que les tableaux sont équivalents (quelque soit l'ordre)        
        if (count($productCats) != count($cats))
            return false;

        foreach ($cats as $cat)
        {
            if (!in_array($cat, $productCats))
                return false;
        }

        foreach ($productCats as $cat)
        {
            if (!in_array($cat, $cats))
                return false;
        }

        return true;    // Ca a l'air ok !
    }

    /**
     * Obtient un tableau de parents pour une catégorie prestashop, dans l'ordre de la hiérarchie (mère, grand-mère, etc...)
     * 
     * @param int $id_category ID de la catégorie Prestashop
     * @return array parents
     */
    public function getPsCategoryTree(int $id_category) : array
    {
        // Cache non chargé, on le charge
        if ($this->psCatsCache === null)
        {
            $this->psCatsCache = [];

            $result = \Db::getInstance()->executeS('SELECT c.`id_category`, c.`id_parent` FROM `' . _DB_PREFIX_ . 'category` c');

            foreach ($result as $row)
                $this->psCatsCache[$row['id_category']] = $row['id_parent'];
        }

        // Construction de l'arbre de catégories
        $tree = [];
        while ($id_category > 0)
        {
            $tree[] = $id_category;
            $id_category = isset($this->psCatsCache[$id_category]) ? $this->psCatsCache[$id_category] : 0;
        }

        return $tree;
    }
}