<?php
namespace PrestaShop\Module\PolarisPrestaConnector;

use Exception;
use PolarisPrestaConnector;

/**
 * Configuration du module
 */
class AutoUpdater
{    
    /**
     * Module
     */
    private PolarisPrestaConnector $module;
   
    /**
     * Instanciation
     */
    public function __construct(PolarisPrestaConnector $module)
    {
        $this->module = $module;
    }

    /**
     * Supprime un répertoire et tout son contenu
     * 
     */
    public function deleteDirectory($dir) {
        // Vérifie si le répertoire existe
        if (!file_exists($dir)) {
            return false;
        }
    
        // Vérifie si c'est bien un répertoire
        if (!is_dir($dir)) {
            return false;
        }
    
        // Parcourt tous les fichiers et sous-répertoires
        $items = array_diff(scandir($dir), ['.', '..']);
        foreach ($items as $item) {
            $path = $dir . DIRECTORY_SEPARATOR . $item;
    
            // Si c'est un répertoire, on appelle la fonction récursivement
            if (is_dir($path)) {
                $this->deleteDirectory($path);
            } else {
                // Sinon, on supprime le fichier
                unlink($path);
            }
        }
    
        // Enfin, on supprime le répertoire vide
        return rmdir($dir);
    }

    /**
     * Recherche une version plus récente du module
     * sur le serveur
     * 
     */
    public function searchUpdate()
    {
        try
        {
            $moduleName = $this->module->name;
            $version = explode('.', $this->module->installed_version);
            $major = "v".$version[0];
            
            $branch = "stable";

            // On teste à la racine du site pour voir si un fichier 'TESTING' existe. Si c'est le cas, on utilise la branche "testing"
            if (file_exists(_PS_ROOT_DIR_ . '/TESTING'))
                $branch = "testing";
                
            $update_path = _PS_MODULE_DIR_. $moduleName . '/updates/';

            // Si le répertoire n'existe pas, produire une erreur
            if (!file_exists($update_path))
                throw new \Exception("Le répertoire de mise à jour n'existe pas !");

            // On commence par déterminer la date de la dernière version téléchargée.
            // La dernière version téléchargée est dans le fichier "latest.zip"
            $lastDate = 0;
            $lastFile = $update_path . 'latest.zip';
            if (file_exists($lastFile))
                $lastDate = filemtime($lastFile);

            // On va chercher la dernière version du module sur le serveur, on charge le fichier

            // https://ftp.vega-net.net/dists/prestaconnector/stable/prestashopX/prestaconnector.zip
            $url = 'https://ftp.vega-net.net/dists/' . $moduleName . '/' . $branch . '/' . $major . '/' . $moduleName . '.zip';

            // On télécharge le fichier du serveur si la version du serveur est plus récente que notre version
            // en utilisant le header if_modified_since
            $ch = curl_init($url);            
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_FILETIME, true);
            //curl_setopt($ch, CURLOPT_HEADER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('If-Modified-Since: ' . gmdate('D, d M Y H:i:s', $lastDate) . ' GMT'));
            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);            
            curl_close($ch);
            
            // Si le code est 200, alors on a une nouvelle version
            // On sauvegarde l'archive dans le fichier latest.download. Si ce fichier existe, on l'écrase
            if ($httpCode == 200)
            {                
                \PrestaShopLogger::addLog('PolarisPrestaConnector : une nouvelle mise à jour a été trouvé, téléchargement !', 1);
                
                file_put_contents($update_path . 'latest.download', $response);

                // Si le fichier 'latest.zip' existe, on le supprime
                if (file_exists($update_path . 'latest.zip'))
                    unlink($update_path . 'latest.zip');

                // Si c'est réussi on renomme le fichier en .zip
                rename($update_path . 'latest.download', $update_path . 'latest.zip');

                // On supprime le répertoire latest s'il existe
                $this->deleteDirectory($update_path . 'latest');                
                
                // On décompresse l'archive ZIP dans le répertoire latest
                $zip = new \ZipArchive;
                if ($zip->open($update_path . 'latest.zip') === TRUE) {
                    $zip->extractTo($update_path . 'latest');
                    $zip->close();
                } else {
                    throw new \Exception("Impossible d'ouvrir l'archive ZIP");
                }

                // Extraction ensuite de la version du module proposé depuis l'archive
                // la version est dans le fichier prestaconnector.php
                // c'est une ligne qui ressemble à : $this->version = '0.0.1';
                $version = '';
                $file = $update_path . 'latest/' . $moduleName . '/' . $moduleName . '.php';
                if (file_exists($file))
                {
                    $content = file_get_contents($file);
                    if (preg_match('/public const MODULE_VERSION = \'([0-9\.]+)\'/', $content, $matches))
                        $version = $matches[1];
                }

                if (!$version)
                    throw new \Exception("Impossible de trouver la version du module dans l'archive");

                // Ok, on va donc générer le fichier upgrade du module pour réaliser la mise à jour
                // pour cela, on copie le fichier updates/upgrade.php dans le répertoire upgrade de notre module
                // sous le nom upgrade-$version.php.
                // Si ce répertoire n'existe pas, on le crée, si le fichier existe déjà, on le remplace
                $upgradeFile = $update_path . 'latest/' . $moduleName . '/updates/upgrade.php';
                if (!file_exists($upgradeFile))                
                    throw new \Exception("Impossible de trouver le fichier upgrade.php dans l'archive");

                // transformer la version 1.0.1 en 1_0_1
                $str_version = str_replace('.', '_', $version);
                // On modifie ensuite dans le fichier upgrade la ligne qui ressemble à 
                // 'function upgrade_module($module)'
                // pour la remplacer par une ligne qui ressemble à 
                // 'function upgrade_module_"$str_version"($module)'
                $content = file_get_contents($upgradeFile);
                $content = preg_replace('/function upgrade_module\(\$module\)/', 'function upgrade_module_' . $str_version . '($module)', $content);
                file_put_contents($upgradeFile, $content);                

                // Puis, création du répertoire upgrade
                $upgradeDir = _PS_MODULE_DIR_ . $moduleName . '/upgrade/';
                if (!file_exists($upgradeDir))
                    mkdir($upgradeDir);

                // Copie du fichier
                copy($upgradeFile, $upgradeDir . 'upgrade-' . $version . '.php');

                // Enfin, on enregistre la version de la mise à jour
                // dans le fichier prestaconnector de notre module pour remplacer la version actuelle par la version disponible
                $file = _PS_MODULE_DIR_ . $moduleName . '/' . $moduleName . '.php';
                $content = file_get_contents($file);
                $content = preg_replace('/\$this->version = \'([0-9\.]+)\'/', '$this->version = \'' . $version . '\'', $content);
                $content = preg_replace('/\$this->version = self::MODULE_VERSION/', '$this->version = \'' . $version . '\'', $content);

                // Injection ChangeLog ?
                if (preg_match('/\$this->description = ([^\n]+)/', file_get_contents($update_path . 'latest/' . $moduleName . '/' . $moduleName . '.php'), $matches))
                    $content = preg_replace('/\$this->description = ([^\n]+)/', '$this->description = ' . $matches[1], $content);

                file_put_contents($file, $content);

                // Et on copie le fichier composer.json de l'archive dans le répertoire du module
                // pour remplacer le fichier composer.json du module
                $composerFile = $update_path . 'latest/' . $moduleName . '/composer.json';
                if (file_exists($composerFile))
                {
                    $content = file_get_contents($composerFile);
                    file_put_contents(_PS_MODULE_DIR_ . $moduleName . '/composer.json', $content);
                }
                else
                    throw new \Exception("Impossible de trouver le fichier composer.json dans l'archive");

                // On met à jour la version du module
                $this->module->version = $version;                
            }
            else if ($httpCode == 304 || $httpCode == 404)
            {
                // Pas de mise à jour
            }
            else if ($httpCode >= 502 && $httpCode < 504)
            {
                // erreur temporaire du serveur, on ne fait rien
            }
            else
                throw new \Exception("Erreur $httpCode lors du contact du serveur de mise à jour !");
        }
        catch (Exception $e)
        {
            // Journalisation de l'erreur
            \PrestaShopLogger::addLog('PolarisPrestaConnector : Erreur à la recherche de mise à jour du module : '.$e->getMessage(), 3);
        }
    }    
}