/**
 * Projet                    : MageStock
 * Logiciel                  : MageStock
 * Fichier                   : assets/js/pages/update.js
 * Rôle du fichier           : Interface de découverte distante, téléchargement, analyse et installation des mises à jour signées
 * Auteur                    : Régis KREMER
 * Version du fichier        : 2.6.124
 * Version générale          : 2.6.124
 * Lot                       : MAGESTOCK UPDATE CENTER INSTALLER PROXY RESPONSIVE RELEASE FINAL1
 * Base requise              : MageStock 2.6.123
 * Création                  : 2026-07-21
 * Dernière modification     : 2026-07-25
 * Dépendances               : Api, i18n.js, api/update.php, views/update.php
 * Données touchées          : aucune directement
 * Permissions               : update.manage côté serveur
 * Sécurité                  : origine distante imposée côté serveur, aucune installation automatique
 * Compatibilité             : navigateurs modernes, PC, smartphone et tablette
 *
 * Résumé :
 * - déclenche une recherche automatique limitée par le cache serveur et une recherche manuelle forcée ;
 * - présente la version distante, sa signature et le paquet compatible sans exposer son URL ;
 * - sépare le téléchargement/analyse de la confirmation d’installation ;
 * - synchronise la progression, la confiance et les actions de récupération de la nouvelle interface 2.6.124.
 *
 * Historique :
 * - 2.6.110 — centre UPDATE et précontrôle du système de fichiers.
 * - 2.6.117 — présentation du moteur cumulatif.
 * - 2.6.118 — découverte distante signée MageKorp Hub.
 * - 2.6.124 — centre de maintenance guidé, responsive et accessible.
 */
(function () {
    'use strict';

    const $ = (id) => document.getElementById(id);
    const tr = (key, fallback, params = null) => typeof window.t === 'function'
        ? window.t(key, params || {}, fallback)
        : String(fallback).replace(/\{([a-zA-Z0-9_.-]+)\}/g, (match, token) => params?.[token] ?? match);

    const statusLabels = {
        completed: ['update.status.up_to_date', 'À jour'],
        idle: ['update.status.up_to_date', 'À jour'],
        pending_manual: ['update.status.pending_manual', 'Mise à jour à finaliser'],
        inconsistent: ['update.status.inconsistent', 'Installation incohérente'],
        verified: ['update.status.verified', 'Paquet vérifié'],
        uploaded: ['update.status.uploaded', 'Paquet envoyé'],
        preflight_failed: ['update.status.preflight_failed', 'Serveur non prêt'],
        backup_created: ['update.status.backup_created', 'Sauvegarde créée'],
        staged: ['update.status.staged', 'Fichiers préparés'],
        files_replaced: ['update.status.files_replaced', 'Fichiers remplacés'],
        migrations_running: ['update.status.migrations_running', 'Migrations en cours'],
        health_check: ['update.status.health_check', 'Contrôle de santé'],
        rollback_required: ['update.status.rollback_required', 'Restauration requise']
    };
    const modeLabels = {
        exact: ['update.mode.exact', 'Direct'],
        cumulative: ['update.mode.cumulative', 'Cumulatif'],
        bridge: ['update.mode.bridge', 'Pont de compatibilité'],
        distribution: ['update.mode.distribution', 'Distribution complète']
    };
    const methodLabels = {
        remote: ['update.method.remote', 'MageKorp Hub'],
        upload: ['update.method.upload', 'Import manuel'],
        manual_deploy: ['update.method.manual_deploy', 'Dépôt manuel'],
        recovery: ['update.method.recovery', 'Récupération']
    };

    let currentStatus = null;
    let automaticDiscoveryAttempted = false;

    function esc(value) {
        return String(value ?? '').replace(/[&<>"']/g, (character) => ({
            '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;'
        }[character]));
    }

    function bytes(value) {
        const number = Number(value);
        if (!Number.isFinite(number) || number < 0) return '—';
        const units = ['o', 'Ko', 'Mo', 'Go', 'To'];
        let current = number;
        let index = 0;
        while (current >= 1024 && index < units.length - 1) {
            current /= 1024;
            index += 1;
        }
        const rendered = current >= 10 || index === 0 ? Math.round(current) : current.toFixed(1).replace('.', ',');
        return `${rendered} ${units[index]}`;
    }

    function dateTime(value) {
        if (!value) return '—';
        const parsed = new Date(value);
        if (Number.isNaN(parsed.getTime())) return String(value);
        try {
            return new Intl.DateTimeFormat(document.documentElement.lang || undefined, {
                dateStyle: 'medium', timeStyle: 'short'
            }).format(parsed);
        } catch (error) {
            return parsed.toLocaleString();
        }
    }

    function statusLabel(status) {
        const def = statusLabels[status];
        return def ? tr(def[0], def[1]) : status || '—';
    }

    function modeLabel(mode) {
        const def = modeLabels[mode];
        return def ? tr(def[0], def[1]) : mode || '—';
    }

    function methodLabel(method) {
        const def = methodLabels[method];
        return def ? tr(def[0], def[1]) : method || '—';
    }

    function sourceRange(analysis) {
        const minimum = analysis.minimumSourceVersion || analysis.installedVersion || '—';
        const maximum = analysis.maximumSourceVersion || minimum;
        return minimum === maximum ? minimum : tr('update.analysis.range', '{minimum} à {maximum}', { minimum, maximum });
    }

    function notify(message, type = 'info') {
        const element = $('updMainAlert');
        if (!element) return;
        element.className = `update-alert ${type}`;
        element.textContent = message;
    }

    function setBusy(button, busy, fallback) {
        if (!button) return;
        if (busy) {
            button.dataset.previousText = button.textContent;
            button.textContent = tr('update.working', 'Traitement…');
            button.disabled = true;
        } else {
            button.textContent = button.dataset.previousText || fallback || button.textContent;
            button.disabled = false;
            delete button.dataset.previousText;
        }
    }

    function safeMageKorpUrl(value) {
        try {
            const url = new URL(String(value));
            return url.protocol === 'https:' && url.hostname === 'magekorp.com' && (url.port === '' || url.port === '443')
                ? url.toString()
                : null;
        } catch (error) {
            return null;
        }
    }

    async function load() {
        try {
            currentStatus = await Api.get('api/update.php');
            render(currentStatus);
            if (!automaticDiscoveryAttempted && canAutomaticDiscover(currentStatus)) {
                automaticDiscoveryAttempted = true;
                await discover(false, true);
            }
        } catch (error) {
            notify(error.message, 'error');
        }
    }

    function canAutomaticDiscover(data) {
        const state = data?.state || {};
        const transaction = data?.transaction || {};
        const busy = ['backup_created', 'staged', 'files_replaced', 'migrations_running', 'health_check', 'rollback_required'].includes(state.updateStatus);
        return !busy && !transaction.packageAvailable;
    }

    function setFlowState(id, state) {
        const element = $(id);
        if (!element) return;
        element.classList.remove('is-active', 'is-complete', 'is-warning');
        if (state) element.classList.add(`is-${state}`);
    }

    function renderFlow(data) {
        const state = data?.state || {};
        const transaction = data?.transaction || {};
        const status = state.updateStatus || 'completed';
        const hasPackage = Boolean(transaction.analysis && transaction.packageAvailable);
        const verified = hasPackage && ['verified', 'uploaded', 'backup_created', 'staged', 'files_replaced', 'migrations_running', 'health_check', 'rollback_required'].includes(status);
        const backupDone = ['backup_created', 'staged', 'files_replaced', 'migrations_running', 'health_check', 'rollback_required'].includes(status);
        const installing = ['staged', 'files_replaced', 'migrations_running', 'health_check'].includes(status);
        const warning = ['preflight_failed', 'rollback_required', 'inconsistent', 'pending_manual'].includes(status);

        setFlowState('updFlowSearch', hasPackage || backupDone || installing ? 'complete' : 'active');
        setFlowState('updFlowVerify', verified || backupDone || installing ? 'complete' : (hasPackage ? 'active' : null));
        setFlowState('updFlowBackup', backupDone || installing ? 'complete' : (verified ? 'active' : null));
        setFlowState('updFlowInstall', status === 'completed' && state.lastUpdateAt ? 'complete' : (installing ? 'active' : null));
        if (warning) {
            const target = status === 'preflight_failed' ? 'updFlowVerify' : (status === 'rollback_required' || status === 'inconsistent' ? 'updFlowInstall' : 'updFlowSearch');
            setFlowState(target, 'warning');
        }
    }

    function renderOperationalState(data, manual, busy, canResume, canRollback) {
        const state = data?.state || {};
        const release = data?.release || {};
        const status = statusLabel(state.updateStatus);
        const overall = $('updOverallStatus');
        if (overall) overall.textContent = status;

        const trust = $('updTrustState');
        if (trust) trust.textContent = data?.signatureReady
            ? `${release.keyId || 'Ed25519'} · ${tr('update.status.verified', 'Vérifiée')}`
            : tr('update.status.inconsistent', 'Non provisionnée');

        const engine = $('updEngineState');
        if (engine) engine.textContent = data?.canUpload
            ? `${tr('update.analysis.server_ready', 'Serveur prêt')} · ${data.zipReader || 'ZIP'}`
            : tr('update.analysis.install_blocked', 'Installation bloquée');

        const idle = $('updRecoveryIdle');
        if (idle) idle.hidden = manual || canResume || canRollback || busy;
        renderFlow(data);
    }

    function render(data) {
        const state = data.state || {};
        const transaction = data.transaction || {};
        $('updInstalledVersion').textContent = state.installedVersion || '—';
        $('updStatus').textContent = statusLabel(state.updateStatus);
        $('updTarget').textContent = state.targetVersion || '—';
        $('updMethod').textContent = methodLabel(state.method || transaction.method);
        $('updPackageMode').textContent = modeLabel(transaction.analysis?.packageMode || state.packageMode);
        $('updLastDate').textContent = dateTime(state.lastUpdateAt || state.lastCheckedAt);

        const manual = state.updateStatus === 'pending_manual';
        const busy = ['backup_created', 'staged', 'files_replaced', 'migrations_running', 'health_check', 'rollback_required'].includes(state.updateStatus);
        const canResume = busy && Boolean(transaction.packageAvailable || transaction.rollbackAvailable);
        const canRollback = busy && Boolean(transaction.rollbackAvailable || transaction.orphanRecoverable);
        $('updFinalizeManual').hidden = !manual;
        $('updResume').hidden = !canResume;
        $('updRollback').hidden = !canRollback;
        renderOperationalState(data, manual, busy, canResume, canRollback);

        const preflight = transaction.analysis?.preflight || null;
        const ready = Boolean(preflight?.ready);
        const hasPackage = Boolean(transaction.analysis && transaction.packageAvailable);
        const canApply = hasPackage && ready && ['verified', 'uploaded'].includes(transaction.status);
        $('updApply').hidden = !canApply;
        $('updApply').disabled = !canApply;
        $('updPreflight').hidden = !(hasPackage && !ready);
        $('updPreflight').disabled = !hasPackage;

        if (transaction.analysis) {
            showAnalysis(transaction.analysis, transaction.method === 'remote'
                ? tr('update.package.source_remote', 'Téléchargé depuis MageKorp Hub')
                : tr('update.package.source_manual', 'Import manuel'));
        } else {
            $('updPackageCard').hidden = true;
        }

        renderDiscovery(data.discovery || {});

        if (data.issues?.length) notify(data.issues.join(' '), 'error');
        else if (manual) notify(tr('update.notice.manual', 'De nouveaux fichiers ont été détectés. Finalisez la mise à jour avant de reprendre l’activité.'), 'warning');
        else if (transaction.orphanRecoverable) notify(tr('update.notice.orphan', 'Un ancien état sans archive ni sauvegarde a été détecté. Cliquez sur Restaurer.'), 'warning');
        else if (hasPackage && !ready) notify(tr('update.notice.blocked', 'Le paquet est authentique, mais le serveur ne peut pas encore remplacer tous les fichiers.'), 'error');
        else if (busy) notify(tr('update.notice.interrupted', 'Une mise à jour interrompue nécessite une action administrateur.'), 'warning');
        else if (hasPackage && ready) notify(tr('update.notice.ready', 'Paquet signé, version source compatible et système de fichiers prêts.'), 'success');
        else notify(tr('update.notice.idle', 'Le système est prêt. La recherche distante ne déclenche jamais l’installation automatiquement.'), 'success');

        const history = data.history || [];
        const historyCount = $('updHistoryCount');
        if (historyCount) historyCount.textContent = String(history.length);
        $('updHistory').innerHTML = history.length
            ? history.map((entry) => `
                <article>
                    <strong>${esc(entry.fromVersion || '—')} → ${esc(entry.toVersion || '—')}</strong>
                    <span>${esc(entry.status || '—')} · ${esc(methodLabel(entry.method))} · ${esc(modeLabel(entry.packageMode))}</span>
                    <small>${esc(dateTime(entry.completedAt || entry.startedAt || ''))}</small>
                    ${entry.error ? `<p>${esc(entry.error)}</p>` : ''}
                </article>`).join('')
            : `<p class="text-muted">${esc(tr('update.history.empty', 'Aucune mise à jour enregistrée.'))}</p>`;
    }

    function normalizeDiscovery(discovery) {
        if (discovery?.result && typeof discovery.result === 'object') {
            return {
                ...discovery.result,
                status: discovery.status || discovery.result.status,
                checkedAt: discovery.checkedAt || discovery.result.checkedAt,
                nextAutomaticCheckAt: discovery.nextAutomaticCheckAt,
                lastError: discovery.lastError || null
            };
        }
        return discovery || {};
    }

    function renderDiscovery(discovery) {
        const result = normalizeDiscovery(discovery);
        const status = result.status || 'never_checked';
        const badge = $('updDiscoveryStatus');
        const container = $('updDiscoveryResult');
        const actions = $('updDiscoveryActions');
        const download = $('updDownloadRemote');
        const notes = $('updReleaseNotes');
        badge.className = 'update-discovery-status neutral';
        actions.hidden = true;
        container.hidden = false;
        notes.hidden = true;

        if (status === 'available' && result.updateAvailable) {
            badge.className = 'update-discovery-status available';
            badge.textContent = tr('update.discovery.available_badge', 'Nouvelle version disponible');
            const artifact = result.artifact || {};
            const sourceRangeText = result.minimumSourceVersion || result.maximumSourceVersion
                ? sourceRange({
                    installedVersion: result.currentVersion,
                    minimumSourceVersion: result.minimumSourceVersion || result.currentVersion,
                    maximumSourceVersion: result.maximumSourceVersion || result.minimumSourceVersion || result.currentVersion
                })
                : result.currentVersion || '—';
            container.innerHTML = `
                <div class="update-discovery-version">
                    <span>${esc(tr('update.discovery.version', 'MageStock {version}', { version: result.latestVersion || '—' }))}</span>
                    ${result.mandatory ? `<strong class="update-discovery-flag">${esc(tr('update.discovery.mandatory', 'Importante'))}</strong>` : ''}
                </div>
                <dl>
                    <div><dt>${esc(tr('update.discovery.published', 'Publication'))}</dt><dd>${esc(dateTime(result.publishedAt))}</dd></div>
                    <div><dt>${esc(tr('update.discovery.compatible_sources', 'Sources compatibles'))}</dt><dd>${esc(sourceRangeText)}</dd></div>
                    <div><dt>${esc(tr('update.discovery.package', 'Paquet sélectionné'))}</dt><dd>${esc(artifact.filename || '—')}</dd></div>
                    <div><dt>${esc(tr('update.discovery.size', 'Taille'))}</dt><dd>${esc(bytes(artifact.sizeBytes))}</dd></div>
                    <div><dt>${esc(tr('update.discovery.signature', 'Confiance'))}</dt><dd>${esc(result.trust?.keyId || artifact.keyId || 'Ed25519')}</dd></div>
                    <div><dt>${esc(tr('update.discovery.checked', 'Dernier contrôle'))}</dt><dd>${esc(dateTime(result.checkedAt))}</dd></div>
                </dl>
                ${result.serverReadyForDownload === false
                    ? `<p class="update-discovery-warning">${esc(tr('update.discovery.disk_blocked', 'Espace disque insuffisant pour télécharger et préparer ce paquet.'))}</p>`
                    : `<p class="update-discovery-ok">${esc(tr('update.discovery.verified', 'Manifeste authentifié, origine autorisée et paquet compatible avec cette installation.'))}</p>`}`;
            actions.hidden = false;
            download.disabled = result.serverReadyForDownload === false;
            const releaseNotes = safeMageKorpUrl(result.links?.releaseNotes);
            if (releaseNotes) {
                notes.href = releaseNotes;
                notes.hidden = false;
            }
            return;
        }

        if (status === 'up_to_date') {
            badge.className = 'update-discovery-status current';
            badge.textContent = tr('update.discovery.current_badge', 'Version actuelle');
            container.innerHTML = `<p class="update-discovery-empty">${esc(tr('update.discovery.current', 'Aucune version plus récente n’est publiée sur le canal stable.'))}</p>`;
            return;
        }

        if (status === 'unavailable') {
            badge.className = 'update-discovery-status unavailable';
            badge.textContent = tr('update.discovery.unavailable_badge', 'Service indisponible');
            container.innerHTML = `<p class="update-discovery-error">${esc(result.lastError || tr('update.discovery.unavailable', 'Le catalogue officiel ne peut pas être joint. L’import manuel reste disponible.'))}</p>`;
            return;
        }

        badge.textContent = tr('update.discovery.never_checked', 'Pas encore vérifié');
        container.innerHTML = `<p class="update-discovery-empty">${esc(tr('update.discovery.prompt', 'Lancez une recherche pour vérifier le canal stable officiel.'))}</p>`;
    }

    function blockerHtml(blocker) {
        const paths = Array.isArray(blocker.affectedPaths) ? blocker.affectedPaths : (blocker.path ? [blocker.path] : []);
        const shown = paths.slice(0, 12);
        const extra = paths.length > shown.length ? `<li>+ ${paths.length - shown.length} ${esc(tr('update.analysis.more_files', 'autre(s) fichier(s)'))}</li>` : '';
        return `<article class="update-preflight-item">
            <strong>${esc(blocker.directory || blocker.path || blocker.code || tr('update.analysis.blocker', 'Blocage'))}</strong>
            <p>${esc(blocker.reason || tr('update.analysis.impossible', 'Opération impossible.'))}</p>
            ${shown.length ? `<ul>${shown.map((path) => `<li><code>${esc(path)}</code></li>`).join('')}${extra}</ul>` : ''}
            ${blocker.mode ? `<small>${esc(tr('update.analysis.permissions', 'Permissions détectées : {mode}', { mode: blocker.mode }))}</small>` : ''}
            ${blocker.recommendation ? `<p class="update-preflight-help">${esc(blocker.recommendation)}</p>` : ''}
        </article>`;
    }

    function showAnalysis(analysis, sourceLabel = '') {
        const card = $('updPackageCard');
        const element = $('updAnalysis');
        const preflight = analysis.preflight || { ready: false, blockers: [] };
        card.hidden = false;
        $('updPackageSource').textContent = sourceLabel;
        const free = preflight.freeBytes == null ? tr('update.analysis.unknown', 'Non déterminé') : bytes(preflight.freeBytes);
        const required = bytes(preflight.requiredBytes);
        const status = preflight.ready
            ? `<span class="update-preflight-status ready">${esc(tr('update.analysis.server_ready', 'Serveur prêt'))}</span>`
            : `<span class="update-preflight-status blocked">${esc(tr('update.analysis.install_blocked', 'Installation bloquée'))}</span>`;
        const blockers = Array.isArray(preflight.blockers) ? preflight.blockers : [];
        const schema = analysis.schemaVersion ? `v${analysis.schemaVersion}` : tr('update.analysis.legacy', 'historique');
        element.innerHTML = `
            <div class="update-analysis-head"><h3>${esc(tr('update.analysis.verified_package', 'Paquet officiel vérifié'))}</h3>${status}</div>
            <dl>
                <div><dt>${esc(tr('update.analysis.type', 'Type'))}</dt><dd>${esc(modeLabel(analysis.packageMode || analysis.packageType))}</dd></div>
                <div><dt>${esc(tr('update.analysis.schema', 'Schéma'))}</dt><dd>${esc(schema)}</dd></div>
                <div><dt>${esc(tr('update.analysis.installed', 'Version installée'))}</dt><dd>${esc(analysis.installedVersion)}</dd></div>
                <div><dt>${esc(tr('update.analysis.sources', 'Versions sources admises'))}</dt><dd>${esc(sourceRange(analysis))}</dd></div>
                <div><dt>${esc(tr('update.analysis.target', 'Version cible'))}</dt><dd>${esc(analysis.targetVersion)}</dd></div>
                <div><dt>${esc(tr('update.analysis.baseline', 'Baseline migrations'))}</dt><dd>${esc(analysis.migrationBaseline || '—')}</dd></div>
                <div><dt>${esc(tr('update.analysis.migrations', 'Migrations déclarées'))}</dt><dd>${esc(analysis.migrationCount || 0)}</dd></div>
                <div><dt>${esc(tr('update.analysis.files', 'Fichiers ciblés'))}</dt><dd>${esc(analysis.fileCount)}</dd></div>
                <div><dt>${esc(tr('update.analysis.signature', 'Signature'))}</dt><dd>${esc(analysis.keyId || 'Ed25519')}</dd></div>
                <div><dt>${esc(tr('update.analysis.required_space', 'Espace estimé'))}</dt><dd>${required}</dd></div>
                <div><dt>${esc(tr('update.analysis.free_space', 'Espace disponible'))}</dt><dd>${free}</dd></div>
            </dl>
            ${blockers.length
                ? `<section class="update-preflight-list"><h4>${esc(tr('update.analysis.corrections', 'Corrections nécessaires avant installation'))}</h4>${blockers.map(blockerHtml).join('')}</section>`
                : `<p class="update-preflight-ok">${esc(tr('update.analysis.ready_text', 'Signature, plage source, fichiers et permissions sont valides. L’installation reste soumise à votre confirmation.'))}</p>`}`;
        card.scrollIntoView({ behavior: 'smooth', block: 'start' });
    }

    async function discover(force, silent = false) {
        const button = $('updDiscover');
        if (!silent) notify(tr('update.discovery.checking', 'Vérification du catalogue officiel, de la signature et des versions compatibles…'), 'info');
        setBusy(button, true, tr('update.discovery.search', 'Rechercher les mises à jour'));
        try {
            const response = await Api.post('api/update.php?action=discover', { force });
            renderDiscovery(response.cache || response.discovery || {});
            if (!silent) {
                const result = response.discovery || {};
                if (result.status === 'available') notify(tr('update.discovery.found', 'Une mise à jour officielle compatible est disponible.'), 'success');
                else notify(tr('update.discovery.none', 'MageStock utilise déjà la dernière version publiée.'), 'success');
            }
        } catch (error) {
            renderDiscovery({ status: 'unavailable', lastError: error.message });
            if (!silent) notify(error.message, 'error');
        } finally {
            setBusy(button, false, tr('update.discovery.search', 'Rechercher les mises à jour'));
        }
    }

    $('updDiscover')?.addEventListener('click', () => discover(true, false));

    $('updDownloadRemote')?.addEventListener('click', async () => {
        const button = $('updDownloadRemote');
        setBusy(button, true, tr('update.discovery.download', 'Télécharger et analyser'));
        notify(tr('update.discovery.downloading', 'Téléchargement contrôlé, vérification SHA-256 et analyse de la signature interne…'), 'info');
        try {
            const result = await Api.post('api/update.php?action=downloadRemote', {});
            showAnalysis(result.analysis, tr('update.package.source_remote', 'Téléchargé depuis MageKorp Hub'));
            notify(result.analysis?.preflight?.ready
                ? tr('update.discovery.download_ready', 'Paquet officiel téléchargé, vérifié et prêt à être installé après confirmation.')
                : tr('update.discovery.download_blocked', 'Paquet officiel vérifié, mais le précontrôle du serveur bloque son installation.'),
            result.analysis?.preflight?.ready ? 'success' : 'error');
            automaticDiscoveryAttempted = true;
            currentStatus = await Api.get('api/update.php');
            render(currentStatus);
        } catch (error) {
            notify(error.message, 'error');
        } finally {
            setBusy(button, false, tr('update.discovery.download', 'Télécharger et analyser'));
        }
    });

    $('updPackage')?.addEventListener('change', (event) => {
        const file = event.target.files?.[0];
        const label = $('updSelectedFile');
        if (!label) return;
        label.textContent = file ? `${file.name} · ${bytes(file.size)}` : tr('update.manual.limit', '128 Mo maximum');
    });

    $('updUploadForm')?.addEventListener('submit', async (event) => {
        event.preventDefault();
        const input = $('updPackage');
        const button = $('updUploadSubmit');
        if (!input.files?.[0]) return;
        const formData = new FormData();
        formData.append('package', input.files[0]);
        setBusy(button, true, tr('update.manual.analyze', 'Analyser le paquet'));
        notify(tr('update.manual.checking', 'Signature, compatibilité de version, intégrité et permissions en cours de contrôle…'), 'info');
        try {
            const result = await Api.postForm('api/update.php?action=upload', formData);
            showAnalysis(result.analysis, tr('update.package.source_manual', 'Import manuel'));
            notify(
                result.analysis?.preflight?.ready ? tr('update.manual.ready', 'Paquet validé et serveur prêt.') : tr('update.manual.blocked', 'Paquet valide, mais installation bloquée par les permissions listées.'),
                result.analysis?.preflight?.ready ? 'success' : 'error'
            );
            await load();
        } catch (error) {
            notify(error.message, 'error');
        } finally {
            setBusy(button, false, tr('update.manual.analyze', 'Analyser le paquet'));
        }
    });

    $('updPreflight')?.addEventListener('click', async () => {
        notify(tr('update.preflight.running', 'Nouveau contrôle des permissions en cours…'), 'info');
        try {
            const result = await Api.post('api/update.php?action=preflight', {});
            showAnalysis(result.analysis, currentStatus?.transaction?.method === 'remote'
                ? tr('update.package.source_remote', 'Téléchargé depuis MageKorp Hub')
                : tr('update.package.source_manual', 'Import manuel'));
            notify(result.preflight?.ready ? tr('update.preflight.ready', 'Le serveur est maintenant prêt.') : tr('update.preflight.blocked', 'Des blocages persistent.'), result.preflight?.ready ? 'success' : 'error');
            await load();
        } catch (error) {
            notify(error.message, 'error');
        }
    });

    $('updApply')?.addEventListener('click', async () => {
        if (!confirm(tr('update.confirm.install', 'Sauvegarder puis installer cette mise à jour ?'))) return;
        const button = $('updApply');
        setBusy(button, true, tr('update.package.install', 'Installer la mise à jour'));
        notify(tr('update.install.running', 'Dernier précontrôle puis mise à jour en cours.'), 'warning');
        try {
            await Api.post('api/update.php?action=apply', {});
            notify(tr('update.install.success', 'Mise à jour réussie. Rechargement…'), 'success');
            setTimeout(() => location.reload(), 900);
        } catch (error) {
            notify(error.message, 'error');
            setBusy(button, false, tr('update.package.install', 'Installer la mise à jour'));
            await load();
        }
    });

    $('updFinalizeManual')?.addEventListener('click', async () => {
        if (!confirm(tr('update.confirm.finalize_manual', 'Finaliser les fichiers déposés manuellement ?'))) return;
        try {
            await Api.post('api/update.php?action=finalizeManual', {});
            location.reload();
        } catch (error) {
            notify(error.message, 'error');
        }
    });

    $('updResume')?.addEventListener('click', async () => {
        try {
            await Api.post('api/update.php?action=resume', {});
            location.reload();
        } catch (error) {
            notify(error.message, 'error');
        }
    });

    $('updRollback')?.addEventListener('click', async () => {
        if (!confirm(tr('update.confirm.rollback', 'Restaurer le point précédent ?'))) return;
        try {
            await Api.post('api/update.php?action=rollback', {});
            location.reload();
        } catch (error) {
            notify(error.message, 'error');
        }
    });

    $('updIntegrity')?.addEventListener('click', async () => {
        try {
            const result = await Api.post('api/update.php?action=integrity', {});
            notify(result.ok ? tr('update.integrity.ok', 'Intégrité validée.') : tr('update.integrity.failed', 'Des contrôles ont échoué.'), result.ok ? 'success' : 'error');
        } catch (error) {
            notify(error.message, 'error');
        }
    });

    document.addEventListener('DOMContentLoaded', () => {
        if (typeof window.applyI18n === 'function') window.applyI18n($('pgUpdate'));
        load();
    });
}());
