<?php
namespace PrestaShop\Module\LCVPrestaConnector;

/**
 * Gestionnaire de photos produit
 * 
 */
class ProductPhotosManager
{
    /**
     * Liste des photos de produits, rangé par id_product, puis par id_product_attribute
     * 
     * @var array<int, array<int, array<int></int>>> Liste des id_photo produits, par id_product_attribute, puis id_image
     */
    private array $productPhotoList;

    /**
     * Mappage des photos 'id_product' => ['photo' => 'id_image']
     * 
     * @var array<int, array<string, int>> Mappage des photos par produit
     */
    private array $photoMap;

    /** 
     * Instancie un nouveau gestionnaire de photos
     * 
     * @param array<int> $productsId produits gérés, ou aucun pour tout charger
    */
    public function __construct(Syncer $syncer, ?array $productsId = null)
    {
        $db = \Db::getInstance();

        // Chargement des photos produits
        $this->productPhotoList = [];
        $sql = 'SELECT `id_product`, `id_image` FROM `' . _DB_PREFIX_ . 'image`';
        if ($productsId !== null) {
            $sql .= ' WHERE `id_product` IN (' . implode(',', $productsId) . ')';
        }
        $sql .= ' ORDER BY `id_product`, `position`';
        $res = $db->executeS($sql);
        if ($res === false)
            throw new \Exception('Erreur lors du chargement des photos produits');
        
        foreach ($res as $row)
            $this->productPhotoList[$row['id_product']][0][] = $row['id_image'];

        // Chargement des photos produits attributs
        $sql = 'SELECT a.`id_product`, i.`id_product_attribute`, i.`id_image` FROM `' . _DB_PREFIX_ . 'product_attribute_image` AS i
                LEFT JOIN `' . _DB_PREFIX_ . 'image` AS img ON img.`id_image` = i.`id_image`
                LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute` a ON a.`id_product_attribute` = i.`id_product_attribute`                
                ';
        if ($productsId !== null) {
            $sql .= ' WHERE a.`id_product` IN (' . implode(',', $productsId) . ')';
        } 
        $sql .= ' ORDER BY a.`id_product`, i.`id_product_attribute`, img.`position`';
        $res = $db->executeS($sql);
        if ($res === false)
            throw new \Exception('Erreur lors du chargement des photos produits attributs');
        foreach ($res as $row)
            $this->productPhotoList[$row['id_product']][$row['id_product_attribute']][] = $row['id_image'];

        // Puis on charge les maps de photo
        $this->photoMap = [];
        $sql = 'SELECT `photo`, `id_product`, `id_image` FROM `' . $syncer->module->TblPrefix . 'image`';
        if ($productsId !== null) {
            $sql .= " WHERE `id_product` IN (" . implode(',', $productsId) . ")";
        }
        $res = $db->executeS($sql);
        if ($res === false)
            throw new \Exception('Erreur lors du chargement des maps de photos');
        foreach ($res as $row)
        {
            if (!isset($this->photoMap[$row['id_product']]))
                $this->photoMap[$row['id_product']] = [];

            $this->photoMap[$row['id_product']][$row['photo']] = $row['id_image'];
        }
    }

    /*
    * Reconstruction des miniatures produit
    */
    protected function rebuildThumbnails($syncer, $imagesPath, $objectId, $imageId, $imageFormat = 'jpg')
    {
        $res = true;
        $generate_hight_dpi_images = (bool) \Configuration::get('PS_HIGHT_DPI');

        /* Génération des thumbnails */
        foreach (\ImageType::getImagesTypes("products") as $k => $image_type) 
        {
            $src = $imagesPath . $imageId . '.' . $imageFormat;				
            $dst = $imagesPath . $imageId . '-' . stripslashes($image_type['name']) . '.' . $imageFormat;

            // Nettoyage
            foreach (glob($imagesPath . $imageId . '-' . stripslashes($image_type['name']) . '.*', GLOB_NOSORT) as $file)
                @unlink($file);

            if (!\ImageManager::resize($src, $dst, (int)$image_type['width'], (int)$image_type['height']))
            {
                $syncer->audit(sprintf("[WRN] Impossible de générer le thumbnail %s", $dst));
                $res = false;
            }

            if ($generate_hight_dpi_images) 
            {
                $dst = $imagesPath . $imageId . '-' . stripslashes($image_type['name']) . '2x.' . $imageFormat;
                @unlink($dst);
                if (!\ImageManager::resize($src, $dst, (int)$image_type['width']*2, (int)$image_type['height']*2))
                {
                    $syncer->audit(sprintf("[WRN] Impossible de générer le thumbnail %s", $dst));
                    $res = false;
                }
            }
        }

        /* Ménage dans les tmp/mini */
        $miniName = "product";
        foreach (glob(_PS_TMP_IMG_DIR_.$miniName.'_'.$objectId.'_*.*', GLOB_NOSORT) as $file)
            @unlink($file);
        foreach (glob(_PS_TMP_IMG_DIR_.$miniName.'_mini_'.$objectId.'_*.*', GLOB_NOSORT) as $file)
            @unlink($file);

        return $res;
    }

    /**
     * Obtient les photos actuellement positionnées pour la déclinaison d'un produit
     * 
     * @param int $id_product Identifiant du produit
     * @param int $id_product_attribute Identifiant de la déclinaison, 0 pour tout le produit, -1  pour parcourir réellement les déclinaisons
     * @return array<int> Liste des id_image
     */
    public function getPhotosForAttr(int $id_product, int $id_product_attribute)
    {
        if (array_key_exists($id_product, $this->productPhotoList))
        {
            if ($id_product_attribute <= 0)
            {
                $photo = [];
                foreach ($this->productPhotoList[$id_product] as $id_product_attribute => $photos)
                {
                    if ($id_product_attribute > 0)
                    {
                        foreach ($photos as $photo_id)
                            if (!in_array($photo_id, $photo))
                                $photo[] = $photo_id;
                    }
                }
                return $photo;
            }
            else if (array_key_exists($id_product_attribute, $this->productPhotoList[$id_product]))
                return $this->productPhotoList[$id_product][$id_product_attribute];
        }
        return [];
    }

    /**
     * Efface une photo d'un produit
     * 
     * @param int $id_product Identifiant du produit
     * @param int $id_image Identifiant de l'image
     */
    public function deletePhoto(int $id_product, int $id_image)
    {
        if (array_key_exists($id_product, $this->productPhotoList))
            $this->productPhotoList[$id_product][0] = array_diff($this->productPhotoList[$id_product][0], [$id_image]);
    }

    /**
     * Détermine si les photos actuellements positionnées pour la déclinaison d'un produit sont les mêmes (et dans le même ordre) que celles fournies 
     * dans le tableau $photoList.
     * Passer 0 en id_product_attribute pour faire la comparaison au niveau du produit
     * 
     * @param int $id_product Identifiant du produit
     * @param int $id_product_attribute Identifiant de la déclinaison
     * @param array<int> $photoList Liste des photos à comparer
     */
    public function arePhotosEqual(int $id_product, int $id_product_attribute, array & $photoList)
    {
        $photos = $this->getPhotosForAttr($id_product, $id_product_attribute);

        if (count($photos) != count($photoList))
            return false;

        for ($i = 0; $i < count($photos); $i++)
        {
            if ($photos[$i] != $photoList[$i])
                return false;
        }

        return true;
    }

    /**
     * Télécharge les photos manquantes pour un produit
     * 
     * @param Syncer $syncer Synchroniseur
     * @param BridgeAdapter $bridge Pont de connexion
     * @param int $id_product Identifiant du produit PrestaShop
     * @param array<string> $photoList Liste des photos à télécharger
     * @param int $position Ordre de démarrage des photos
     */
    public function translatePhotos(Syncer $syncer, BridgeAdapter $bridge, \Product $product, array $photoList, int $position = 0)
    {
        // On mappe chaque $photoList
        $mappedPhotoList = [];
        
        foreach ($photoList as $photo) {
            // Si pas de photo ou la photo n'existe plus !
            $image_id = null;
            if (array_key_exists($product->id, $this->photoMap) && array_key_exists($photo, $this->photoMap[$product->id]))
                $image_id = $this->photoMap[$product->id][$photo];

            $photo_path = $image_id ? _PS_ROOT_DIR_ . '/img/p/' . \Image::getImgFolderStatic($image_id) . $image_id . '.jpg' : null;
            if (!$image_id || !file_exists($photo_path))
            {
                $syncer->audit(sprintf('Photo #%s manquante, téléchargement...', $photo));
                // Téléchargement
                $url = $bridge->getPhotoUrl($photo);
                if (!$url)
                {
                    $syncer->audit(sprintf('[WRN] Impossible de trouver l\'URL de la photo : ignorée', $photo));
                    continue;
                }

                // Téléchargement de la photo dans un fichier temporaire
                // Mais on gère les erreurs de téléchargement
                $img = @file_get_contents($url);
                if ($img === false)
                {
                    $syncer->audit(sprintf('[WRN] Impossible de télécharger la photo %s : ignorée', $url));
                    continue;
                }                

                // On a la photo, on l'insère dans la base
                $psImg = null;

                if ($image_id)
                {
                    $psImg = new \Image($image_id);
                    if ($psImg->id != $image_id)
                        $image_id = null;   // Et bien non, elle n'est plus là !
                }

                if (!$image_id)
                {
                    $psImg = new \Image();
                    $psImg->id_product = $product->id;
                    $psImg->position = $position;
                    $psImg->cover = null;
                    $psImg->add();
                    $image_id = $psImg->id;
                }

                // Et on sauvegarde le fichier
                $path = _PS_ROOT_DIR_ . '/img/p/' . \Image::getImgFolderStatic($image_id);
                if (!is_dir($path))
                    mkdir($path, 0777, true);
                file_put_contents($path . $image_id . '.jpg', $img);

                // Maintenant qu'on la numéro de la photo, on l'insère dans le map                
                if (!isset($this->photoMap[$product->id]))
                    $this->photoMap[$product->id] = [];
                $this->photoMap[$product->id][$photo] = (int) $image_id;

                // Et dans la liste des photos !
                $this->productPhotoList[$product->id][0][] = (int) $image_id;                

                // Et dans la table de correspondance
                $db = \Db::getInstance();
                // Insertion ou remplacement de la photo s'il elle existe avec REPLACE                
                if ($db->execute('REPLACE INTO `' . $syncer->module->TblPrefix . 'image` (`photo`, `id_product`, `id_image`) VALUES (\''.$db->_escape($photo).'\', '.$product->id.', '.$image_id.')') === false)
                    throw new \Exception('Erreur lors de l\'insertion de la photo');                

                // Calcul des thumbnails ici
                $this->rebuildThumbnails($syncer, $path, $product->id, $image_id, $psImg->image_format);

                // Plugin, watermark ?
                try
                {
				    \Hook::exec('actionWatermark', array('id_image' => $image_id, 'id_product' => $product->id));
                }
                catch (\Exception $e)
                {
                    // Ne rien faire !
                }
            }

            $mappedPhotoList[$photo] = $image_id;
            $position++;
        }

        return $mappedPhotoList;
    }
}