/**
 * Déclenche une opération asynchronise depuis un bouton, devant le mettre à jour !
 * 
 * @param {*} elt 
 * @param {*} url 
 */
async function polarisprestaconnector_ajaxButtonReactionCall(elt, url, rawResult = false) {

    // On commence par remplacer le texte du bouton par un loader
    // On cache l'élément (display : none)
    const before = elt.style.display;
    elt.style.display = 'none';

    // On ajoute un loader qu'on affiche juste à côté de l'élément
    const spinner = document.createElement('div');
    spinner.className = 'spinner';
    spinner.style.width = '18px';
    spinner.style.height = '18px';

    const loader = document.createElement('div');
    loader.className = elt.className + ' disabled';
    loader.appendChild(spinner);

    let result = false;

    // Insertion du loader juste après l'élement
    elt.parentNode.insertBefore(loader, elt.nextSibling); 

    try
    {
        const baseUrl = $('body').data('base-url');
        const token = $('body').data('token');

        // Appel ajax pour synchroniser le produit
        const response = await fetch(`${baseUrl}/modules/polarisprestaconnector/${url}&_token=${token}`, {
            method: 'GET',
            JSON: true,
            headers: {
                'Content-Type': 'application/json'
            }
        });        

        if (!response.ok)
            throw new Error('Erreur HTTP : ' + response);

        // Normalement, la réponse devrait contenir un JSON avec une propriété 'success' à vrai
        // Si ce n'est pas le cas, on lève une erreur
        const data = await response.json();
        if (data.success)
        {
            // On renvoie l'objet de réponse complet (au lieu d'un simple booléen) afin que les
            // appelants puissent exploiter des données additionnelles (ex. HTML à réafficher).
            // Reste compatible avec les appels booléens : un objet est « truthy », false sinon.
            result = data;
        }
        else
        {
            console.error(data);
            // En mode rawResult, on renvoie la réponse complète même en échec, pour laisser
            // l'appelant décider (afficher lui-même l'erreur, rafraîchir un affichage…).
            // Sinon, on affiche l'erreur ici comme historiquement.
            if (rawResult)
                result = data;
            else
                showErrorMessage(data.message);
        }
    }
    catch (e)
    {
        console.error(e);
        showErrorMessage('Erreur : ' + e.message);
    }
    finally
    {
        // On montre l'élément !
        elt.style.display = before;
        // On supprime le loader
        loader.remove();
    }

    return result;
}

/**
 * Déclenche une resynchronisation globale
 */
async function polarisprestaconnector_resyncAll() {
    console.debug('Resynchronisation totale demandée !');

    // Si un bouton avec un id 'page-header-desc-configuration-resync' existe, on le désactive et on change son nom
    const elt = document.getElementById('page-header-desc-configuration-resync');
    
    if (await polarisprestaconnector_ajaxButtonReactionCall(elt, 'resync_all?'))
        elt.innerHTML = 'Resynchro. demandée !';
}

/**
 * Lance une synchronisation
 *  
 */
async function polarisprestaconnector_run(cronGuardToken) {
    console.debug('Lancement de la synchronisation !');

    const elt = document.getElementById('page-header-desc-configuration-run');    

    elt.className = elt.className + ' disabled';
    // On change l'icône du bouton pour un spinner : 
    // pour ça, on cherche dans les fils la balise "i" et on change son contenu
    const i = elt.querySelector('i');
    if (i !== null)
        i.innerHTML = 'sync';

    // Appel ajax vers la tâche cron, sans rien d'autre !
    const baseUrl = $('body').data('base-url');
    const encodedGuardToken = encodeURIComponent(cronGuardToken);
    await fetch(`${baseUrl}/modules/polarisprestaconnector/cron?guard=${encodedGuardToken}`, {
        method: 'GET',
        JSON: true,
        headers: {
            'Content-Type': 'application/json'
        }
    });
}

/**
 * Met à jour l'environnement
 */
async function polarisprestaconnector_pullenv() {
    console.debug('Téléchargement de l\'environnement depuis le backoffice !');

    const elt = document.getElementById('page-header-desc-configuration-pullenv');

    await polarisprestaconnector_ajaxButtonReactionCall(elt, 'configuration/pullenv?');
}

/**
 * Déclenche en arrière-plan le tirage de l'environnement s'il n'a pas encore été réalisé
 * (typiquement juste après l'association). Sans nettoyage des mappages (clean=0) afin de
 * préserver une configuration existante, et au plus une fois par session de navigation pour
 * éviter des tirages concurrents. Si l'utilisateur quitte la page avant la fin, le filet de
 * sécurité de la synchronisation (Syncer) reprendra le tirage.
 */
async function polarisprestaconnector_pullenvBackground() {
    const nav = document.querySelector('.pc-nav');
    if (!nav || nav.dataset.envPulled !== '0')
        return;

    // Une seule tentative automatique par session de navigation
    if (sessionStorage.getItem('polarisprestaconnector_pullenv_attempted') === '1')
        return;
    sessionStorage.setItem('polarisprestaconnector_pullenv_attempted', '1');

    const elt = document.getElementById('page-header-desc-configuration-pullenv');
    if (!elt)
        return;

    console.debug('Tirage de l\'environnement en arrière-plan...');
    await polarisprestaconnector_ajaxButtonReactionCall(elt, 'configuration/pullenv?clean=0');
}

/**
 * Obtient la progression de la synchronisation en cours ou null si aucune
 * 
 */
async function polarisprestaconnector_getSyncProgress() {

    try
    {
        const baseUrl = $('body').data('base-url');
        const token = $('body').data('token');

        // Appel ajax pour synchroniser le produit
        const response = await fetch(`${baseUrl}/modules/polarisprestaconnector/syncprogress?&_token=${token}`, {
            method: 'GET',
            JSON: true,
            headers: {
                'Content-Type': 'application/json'
            }
        });        

        if (!response.ok)
            throw new Error('Erreur HTTP : ' + response);

        // Normalement, la réponse devrait contenir un JSON avec une propriété 'success' à vrai
        // Si ce n'est pas le cas, on lève une erreur
        const data = await response.json();
        if (data.success)
        {
            return data.progress;
        }
        else
        {
            console.error(data);
            // On montre l'erreur avec les fonctions JS prévu par PrestaShop
            showErrorMessage(data.message);            
        }
    }
    catch (e)
    {
        console.error(e);
        showErrorMessage('Erreur : ' + e.message);
    }

    return null;
}

/**
 * Vérifie la progression de la synchronisation en cours à intervalles réguliers
 */
async function polarisprestaconnector_adminSyncProgressCheck() {

    const elt = document.getElementById('page-header-desc-configuration-run');    
    if (!elt)
        return;

    const result = await polarisprestaconnector_getSyncProgress();
    if (result !== null && result.progress && result.progress.running)
    {
        // On ajoute disabled aux classes CSS s'il n'y est pas déjà 
        if (!elt.className.includes('disabled'))
        {
            elt.className = elt.className + ' disabled';
            // On change l'icône du bouton pour un spinner : 
            // pour ça, on cherche dans les fils la balise "i" et on change son contenu
            const i = elt.querySelector('i');
            if (i !== null)
                i.innerHTML = 'sync';
        }
    }
    else
    {
        // On retire "disabled" des classes CSS
        if (elt.className.includes('disabled'))
        {
            elt.className = elt.className.replace('disabled', '');
            // On change l'icône du bouton pour une icône play
            const i = elt.querySelector('i');
            if (i !== null)
                i.innerHTML = 'play_circle';
        }
    }
}

/**
 * Affiche la progression de la synchronisation en cours
 */
async function polarisprestaconnector_dashboardSyncProgressShow(progress)
{
    const elt = document.getElementById('polarisprestaconnector_dashboard_progress');    
    if (!elt)
        return;

    if (progress)
    {
        if (progress.running)
        {            
            let html = '<p><strong>Synchronisation en cours</strong>, démarrée depuis ';
            
            // On marque la durée de la synchronisation, du style : démarrée depuis 24minutes et 30 secondes...
            const startTimestamp = progress.startTime * 1000;
            const nowTimestamp = Date.now();
            const diffDate = new Date(nowTimestamp - startTimestamp);

            if (diffDate.getTime() > 60000)
            {
                if (diffDate.getTime() > 3600000)
                    html += `${diffDate.getUTCHours()}h et ${diffDate.getUTCMinutes()}mins`;
                else
                    html += `${diffDate.getUTCMinutes()}mins`;
            }
            else
                html += 'quelques instants';

            html += '</p>';

            html += '<ul>';
            // foreach pour parcourir les clés de l'objet progress.steps
            for (const key in progress.steps)
            {
                const step = progress.steps[key];

                if ( (step.e ?? false) && (step.errMsg ?? false) )
                    html += '<li style="list-style-type: \'❌\'; text-decoration: line-through; color: crimson;"> ' + step.errMsg + ' </li>';
                else if (step.e ?? false)
                    html += '<li style="list-style-type: \'✅\'; text-decoration: line-through; color: darkgray;"> ' + step.m + ' </li>';
                else if (step.d > 0)
                    html += '<li class="blinking-text" style="list-style-type: \'🔄\';">' + step.m + '(' + step.d + ')' + '</li>';
                else
                    html += '<li class="blinking-text" style="list-style-type: \'🔄\';">' + step.m + '</li>';
            }

            html += '</ul>';

            //const perc = progress.stepNb > 0 ? Math.round(progress.stepDone * 100 / progress.stepNb) : 0;

            
            /*html += `<div class="progress"><div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" 
                        style="width: ${perc}%"></div></div>`;*/                        

            elt.innerHTML = html;
        }
        else
        {
            if (progress.endTime)
            {            
                const endTimestamp = progress.endTime * 1000;
                const nowTimestamp = Date.now();
                const ellapsedTime = (nowTimestamp - endTimestamp) / 1000;

                let diffStr = '';            
                if (ellapsedTime < 60)
                    diffStr = 'moins d\'une minute';
                else if (ellapsedTime < 3600)
                    diffStr = Math.floor(ellapsedTime / 60) + ' minutes';
                else if (ellapsedTime < 86400 * 3)
                    diffStr = Math.floor(ellapsedTime / 3600) + ' heures';
                else
                    diffStr = Math.floor(ellapsedTime / 86400) + ' jours';

                elt.innerHTML = '<p>Dernière synchronisation il y a ' + diffStr + '.</p>';

                // On scanne les messages à la recherche d'erreurs
                const errors = [];
                for (const [stepKey, stepData] of Object.entries(progress.steps)) {
                    if (stepData.errMsg) {
                        errors.push({
                            step: stepKey,
                            message: stepData.errMsg
                        });
                    }
                }

                if (errors.length > 0) {
                    // On ajoute les erreurs en orange en bas du message, max 120 caractères par message d'erreur, avec une icône d'erreur
                    let errorHtml = '<div class="sync-error"><p><strong>Erreurs rencontrées lors de la dernière synchronisation :</strong></p><ul>';
                    errors.forEach(error => {
                        let message = error.message;
                        if (message.length > 120) {
                            message = message.substring(0, 120) + '…';
                        }
                        errorHtml += `<li><strong>${error.step}:</strong> ${message}</li>`;
                    });
                    errorHtml += '</ul></div>';

                    elt.innerHTML = elt.innerHTML + errorHtml;
                }
            } 
            else
            {
                elt.innerHTML = '<p>Pas encore synchronisé.</p>';
            }

            if (progress.lastErrorMsg)
            {
                // Tronquer le message d'erreur à 100 caractères
                if (progress.lastErrorMsg.length > 120)
                    progress.lastErrorMsg = progress.lastErrorMsg.substring(0, 120) + '…';
                elt.innerHTML = '<div class="sync-error"><p>' + progress.lastErrorMsg + '</p></div>' + elt.innerHTML;
            }

            // La synchro était marquée "en cours" mais le process est mort avant la fin :
            // arrêt anormal (erreur fatale, mémoire saturée…). On l'affiche en tête.
            if (progress.aborted)
            {
                elt.innerHTML = '<div class="sync-error"><p><strong>⚠️ La dernière synchronisation '
                    + 's\'est interrompue anormalement</strong> (le traitement s\'est arrêté avant la fin). '
                    + 'La cause est probablement dans les journaux PHP (erreur fatale, mémoire saturée…).</p>'
                    + '</div>' + elt.innerHTML;
            }
        }
    }
    else
    {
        elt.innerHTML = '<p>Impossible d\'obtenir l\'état de la synchronisation</p>';
    }
}

/**
 * Recharge la page en forçant une navigation « fraîche ».
 *
 * Un simple document.location.reload() laisse le navigateur restaurer l'état des champs du
 * formulaire produit (form state restoration / bfcache) : les anciennes valeurs saisies sont
 * réinjectées par-dessus le HTML pourtant à jour renvoyé par le serveur. En changeant l'URL
 * (paramètre anti-cache) et en utilisant replace(), on force une vraie navigation, sans
 * restauration ni entrée d'historique superflue.
 */
function polarisprestaconnector_reloadFresh() {
    const url = new URL(window.location.href);
    url.searchParams.set('_pcrefresh', Date.now());
    window.location.replace(url.toString());
}

/**
 * Déclenche la synchronisation d'un produit spécifique
 * @param {*} e évènement déclencheur
 * @param {*} id_product
 *
 */
async function polarisprestaconnector_syncProduct(e, id_product) {

    console.debug('Synchronisation du produit ' + id_product);
    const elt = e.currentTarget;

    if (await polarisprestaconnector_ajaxButtonReactionCall(elt, `sync_product?id_product=${id_product}`))
        polarisprestaconnector_reloadFresh();
}

/**
 * Déclenche la synchronisation d'une commande spécifique
 * @param {*} elt élement déclencheur
 * @param {*} id_product 
 * 
 */
async function polarisprestaconnector_exportOrder(elt, order_reference) {

    console.debug('Exportation de la commande ' + order_reference);

    // rawResult=true : on veut la réponse complète même en cas d'échec d'export, car le journal
    // gagne alors une entrée « erreur » que l'on souhaite afficher immédiatement.
    const result = await polarisprestaconnector_ajaxButtonReactionCall(elt, `export_order?order_reference=${order_reference}`, true);
    if (!result)
        return; // erreur réseau/HTTP : déjà tracée en console

    // Rafraîchit le panneau d'export (statut + journal) in-place — que l'export ait réussi ou
    // échoué ; repli sur un rechargement de page si le HTML ou le panneau est absent.
    const panel = document.getElementById('pcExportPanel');
    if (panel && result.html) {
        panel.outerHTML = result.html;

        // L'état général a pu changer : on met aussi à jour la pastille de couleur et l'infobulle
        // du lien d'onglet, qui n'est pas re-rendu par le rafraîchissement du panneau.
        const dot = document.querySelector('#pcExportTab .pc-export-dot');
        if (dot && result.statusColor)
            dot.style.background = result.statusColor;
        const link = document.getElementById('pcExportTab');
        if (link && result.statusLabel)
            link.title = result.statusLabel;
    } else {
        document.location.reload();
    }

    // Retour utilisateur : message de succès (« ré-exportée » / « déjà à jour ») ou d'erreur.
    if (result.message) {
        if (result.success && typeof showSuccessMessage === 'function')
            showSuccessMessage(result.message);
        else if (!result.success && typeof showErrorMessage === 'function')
            showErrorMessage(result.message);
    }
}

/**
 * Déclenche la synchronisation d'un produit spécifique
 * @param {*} e évènement déclencheur
 * @param {*} id_product 
 * 
 */
async function polarisprestaconnector_toggleLockProduct(e, id_product) {

    console.debug('Lock/Unlock produit ' + id_product);
    const elt = e.currentTarget;
    const action = elt.innerHTML.includes('lock_open') ? 'lock' : 'unlock';

    // Verrouillage / déverrouillage du produit
    if (!await polarisprestaconnector_ajaxButtonReactionCall(elt, `lock_product?id_product=${id_product}&action=${action}`))
        return;

    // En rendant la main au backoffice (déverrouillage), on resynchronise aussitôt le produit
    // pour appliquer ses données et écraser les éventuelles saisies manuelles. La synchro ignore
    // les produits verrouillés, d'où l'ordre : déverrouiller d'abord, synchroniser ensuite.
    // Si la synchro échoue, on laisse le message d'erreur visible sans recharger.
    if (action === 'unlock'
        && !await polarisprestaconnector_ajaxButtonReactionCall(elt, `sync_product?id_product=${id_product}`))
        return;

    polarisprestaconnector_reloadFresh();
}
