/**
 * OMAG - Gestionnaire de mises à jour
 *
 * Gère le téléchargement, l'installation et le rollback des mises à jour
 */

const fs = require('fs-extra');
const path = require('path');
const axios = require('axios');
const https = require('https');
const crypto = require('crypto');
const AdmZip = require('adm-zip');
const { exec } = require('child_process');
const util = require('util');
const execPromise = util.promisify(exec);

class UpdateManager {
  constructor() {
    this.projectRoot = path.join(__dirname, '..');
    this.updateDir = path.join(this.projectRoot, 'maj_web');
    // Temp et backup en DEHORS du backend pour eviter les problemes de copie recursive
    this.tempDir = path.join(this.projectRoot, '..', 'update-temp');
    this.backupDir = path.join(this.projectRoot, '..', 'backups');
    this.versionFile = path.join(this.projectRoot, 'version.json');
    // Fichier de statut persistant pour survivre aux redémarrages
    this.statusFile = path.join(this.updateDir, 'update-status.json');
    const config = require('../config/defaults');
    this.updateServerUrl = config.get("UPDATE_SERVER_URL") || 'https://updates.omag.ma';

    // Chemin du frontend (même logique que index.js)
    // En production: ../frontend, en dev: ../omagweb/dist
    this.frontendDir = this.detectFrontendPath();

    // S'assurer que les dossiers existent
    this.ensureDirectories();
  }

  /**
   * Détecte le chemin du frontend (même logique que index.js)
   */
  detectFrontendPath() {
    const possiblePaths = [
      path.join(this.projectRoot, '..', 'frontend'),
      path.join(this.projectRoot, '..', 'omagweb', 'dist')
    ];

    for (const testPath of possiblePaths) {
      if (fs.existsSync(testPath)) {
        console.log('[UPDATE] Frontend détecté:', testPath);
        return testPath;
      }
    }

    // Par défaut, utiliser ../frontend (sera créé si nécessaire)
    console.log('[UPDATE] Frontend par défaut:', possiblePaths[0]);
    return possiblePaths[0];
  }

  async ensureDirectories() {
    await fs.ensureDir(this.tempDir);
    await fs.ensureDir(this.backupDir);
  }

  /**
   * Sauvegarde le statut de mise à jour dans un fichier persistant
   * Ce fichier survit aux redémarrages du serveur
   */
  async saveStatus(status) {
    try {
      await fs.writeJson(this.statusFile, {
        ...status,
        timestamp: Date.now()
      }, { spaces: 2 });
    } catch (error) {
      console.error('[UPDATE] Erreur sauvegarde statut:', error.message);
    }
  }

  /**
   * Lit le statut de mise à jour depuis le fichier persistant
   */
  async getStatus() {
    try {
      if (fs.existsSync(this.statusFile)) {
        const status = await fs.readJson(this.statusFile);
        return status;
      }
    } catch (error) {
      console.error('[UPDATE] Erreur lecture statut:', error.message);
    }
    return null;
  }

  /**
   * Efface le fichier de statut
   */
  async clearStatus() {
    try {
      if (fs.existsSync(this.statusFile)) {
        await fs.remove(this.statusFile);
      }
    } catch (error) {
      console.error('[UPDATE] Erreur suppression statut:', error.message);
    }
  }

  /**
   * Récupère la version actuelle
   */
  getCurrentVersion() {
    try {
      if (fs.existsSync(this.versionFile)) {
        const versionData = fs.readJsonSync(this.versionFile);
        return versionData.version;
      }
      return '1.0.0'; // Version par défaut
    } catch (error) {
      console.error('Erreur lecture version:', error);
      return '1.0.0';
    }
  }

  /**
   * Vérifie si une mise à jour est disponible
   */
  async checkForUpdates() {
    try {
      const currentVersion = this.getCurrentVersion();

      // Agent HTTPS pour gerer les certificats SSL
      const httpsAgent = new https.Agent({
        rejectUnauthorized: false
      });

      const response = await axios.get(`${this.updateServerUrl}/update/version.json`, {
        timeout: 10000,
        httpsAgent: httpsAgent
      });

      const latestVersion = response.data.version;
      const isUpdateAvailable = this.compareVersions(latestVersion, currentVersion) > 0;

      return {
        currentVersion,
        latestVersion,
        available: isUpdateAvailable,
        downloadUrl: response.data.downloadUrl,
        changelog: response.data.changelog,
        size: response.data.size,
        hash: response.data.hash
      };
    } catch (error) {
      console.error('Erreur vérification mises à jour:', error.message);
      throw new Error('Impossible de vérifier les mises à jour. Vérifiez votre connexion internet.');
    }
  }

  /**
   * Compare deux versions (format semver)
   * @returns 1 si v1 > v2, -1 si v1 < v2, 0 si égales
   */
  compareVersions(v1, v2) {
    const parts1 = v1.split('.').map(Number);
    const parts2 = v2.split('.').map(Number);

    for (let i = 0; i < 3; i++) {
      if (parts1[i] > parts2[i]) return 1;
      if (parts1[i] < parts2[i]) return -1;
    }
    return 0;
  }

  /**
   * Télécharge une mise à jour
   */
  async downloadUpdate(downloadUrl, expectedHash, onProgress) {
    try {
      await this.ensureDirectories();

      const fileName = path.basename(downloadUrl);
      const filePath = path.join(this.tempDir, fileName);

      // Nettoyer les téléchargements précédents
      if (fs.existsSync(filePath)) {
        await fs.remove(filePath);
      }

      console.log('Téléchargement depuis:', downloadUrl);

      // Agent HTTPS pour gerer les certificats SSL
      const httpsAgent = new https.Agent({
        rejectUnauthorized: false,
        keepAlive: true
      });

      const response = await axios({
        method: 'get',
        url: downloadUrl,
        responseType: 'stream',
        timeout: 600000, // 10 minutes
        httpsAgent: httpsAgent,
        maxContentLength: Infinity,
        maxBodyLength: Infinity
      });

      const totalLength = parseInt(response.headers['content-length'], 10);
      let downloadedLength = 0;
      let lastLoggedProgress = 0;

      console.log('[UPDATE] Taille totale:', totalLength, 'octets');

      const writer = fs.createWriteStream(filePath);

      // Promesse avec gestion complete des erreurs
      await new Promise((resolve, reject) => {
        let hasError = false;

        // Gestion des erreurs sur le stream d'entree
        response.data.on('error', (err) => {
          console.error('[UPDATE] Erreur stream entree:', err.message);
          hasError = true;
          writer.destroy();
          reject(new Error('Erreur de telechargement: ' + err.message));
        });

        // Gestion des erreurs sur le writer
        writer.on('error', (err) => {
          console.error('[UPDATE] Erreur ecriture fichier:', err.message);
          hasError = true;
          reject(new Error('Erreur ecriture: ' + err.message));
        });

        // Progression du telechargement
        response.data.on('data', (chunk) => {
          if (hasError) return;
          downloadedLength += chunk.length;
          if (onProgress && totalLength) {
            const progress = Math.round((downloadedLength / totalLength) * 100);
            onProgress(progress);
            // Log tous les 10%
            if (progress >= lastLoggedProgress + 10) {
              console.log('[UPDATE] Progression:', progress + '%');
              lastLoggedProgress = progress;
            }
          }
        });

        // Fin du telechargement
        writer.on('finish', () => {
          if (!hasError) {
            console.log('[UPDATE] Ecriture fichier terminee');
            resolve();
          }
        });

        // Demarrer le pipe
        response.data.pipe(writer);
      });

      // Vérifier le hash
      const actualHash = await this.calculateFileHash(filePath);
      if (actualHash !== expectedHash) {
        await fs.remove(filePath);
        throw new Error('Le fichier téléchargé est corrompu (hash invalide)');
      }

      console.log('Téléchargement terminé:', filePath);
      return filePath;
    } catch (error) {
      console.error('Erreur téléchargement:', error.message);
      throw new Error(`Échec du téléchargement: ${error.message}`);
    }
  }

  /**
   * Calcule le hash SHA256 d'un fichier
   */
  async calculateFileHash(filePath) {
    return new Promise((resolve, reject) => {
      const hash = crypto.createHash('sha256');
      const stream = fs.createReadStream(filePath);

      stream.on('data', (data) => hash.update(data));
      stream.on('end', () => resolve(hash.digest('hex')));
      stream.on('error', reject);
    });
  }

  /**
   * Installe une mise à jour
   */
  async installUpdate(zipPath) {
    let backupPath = null;
    let manifest = null;

    try {
      console.log('Installation de la mise à jour:', zipPath);

      // 1. Extraire le ZIP
      const extractDir = path.join(this.tempDir, 'extract');

      // Nettoyer le dossier d'extraction (évite les problèmes de fichiers résiduels)
      if (fs.existsSync(extractDir)) {
        console.log('[UPDATE] Nettoyage du dossier d\'extraction précédent...');
        await fs.remove(extractDir);
      }
      await fs.ensureDir(extractDir);

      console.log('[UPDATE] Extraction du ZIP...');
      const zip = new AdmZip(zipPath);

      // Lister le contenu du ZIP pour debug
      const zipEntries = zip.getEntries();
      console.log('[UPDATE] ZIP contient', zipEntries.length, 'entrées');

      // Extraire
      zip.extractAllTo(extractDir, true);

      // Vérifier que l'extraction a réussi
      const extractedItems = await fs.readdir(extractDir);
      console.log('[UPDATE] Extraction terminée, contenu:', extractedItems.join(', '));

      // Vérifier spécifiquement que files/backend existe
      const filesBackendPath = path.join(extractDir, 'files', 'backend');
      if (!fs.existsSync(filesBackendPath)) {
        console.error('[UPDATE] ERREUR: files/backend non trouvé après extraction!');
        console.error('[UPDATE] Contenu de extractDir:', extractedItems);

        // Afficher la structure pour debug
        const filesDir = path.join(extractDir, 'files');
        if (fs.existsSync(filesDir)) {
          const filesContent = await fs.readdir(filesDir);
          console.error('[UPDATE] Contenu de files/:', filesContent);
        } else {
          console.error('[UPDATE] Le dossier files/ n\'existe pas non plus!');
        }

        throw new Error('Extraction incomplète: files/backend manquant');
      }
      console.log('[UPDATE] Vérification OK: files/backend existe');

      // 2. Lire le manifest
      const manifestPath = path.join(extractDir, 'manifest.json');
      if (!fs.existsSync(manifestPath)) {
        throw new Error('Fichier manifest.json introuvable dans le package');
      }

      manifest = await fs.readJson(manifestPath);
      console.log('Manifest chargé:', manifest);

      // 3. Créer une sauvegarde
      if (manifest.type === 'full') {
        // Package complet (backend + frontend)
        console.log('Package complet détecté (backend + frontend)');
        backupPath = await this.createFullBackup();
      } else {
        // Package sélectif (fichiers individuels)
        backupPath = await this.createBackup(manifest.files);
      }
      console.log('Sauvegarde créée:', backupPath);

      // 4. Copier les nouveaux fichiers
      const filesDir = path.join(extractDir, 'files');
      if (!fs.existsSync(filesDir)) {
        throw new Error('Dossier "files" introuvable dans le package');
      }

      if (manifest.type === 'full') {
        // Installation complète
        console.log('Installation complète du backend et frontend...');

        // Copier le backend avec nettoyage complet
        const backendSource = path.join(filesDir, 'backend');
        if (fs.existsSync(backendSource)) {
          console.log('[UPDATE] Installation du backend (mode nettoyage)...');

          // Dossiers et fichiers à préserver (ne pas supprimer)
          // node_modules est préservé — l'auto-install au démarrage gère les mises à jour
          const preserveItems = ['logs', 'public', 'db-config.json', 'uploads', 'node_modules'];

          // 1. Sauvegarder les fichiers de configuration
          const configBackup = {};
          const configFiles = ['db-config.json'];
          for (const configFile of configFiles) {
            const configPath = path.join(this.projectRoot, configFile);
            if (fs.existsSync(configPath)) {
              configBackup[configFile] = await fs.readFile(configPath, 'utf8');
              console.log(`[UPDATE] Config sauvegardée: ${configFile}`);
            }
          }

          // 2. Lister et supprimer tout sauf les éléments à préserver
          console.log('[UPDATE] Nettoyage du backend (préservation: logs, public, uploads, config)...');
          const items = await fs.readdir(this.projectRoot);
          for (const item of items) {
            if (!preserveItems.includes(item)) {
              const itemPath = path.join(this.projectRoot, item);
              try {
                await fs.remove(itemPath);
              } catch (err) {
                // Ignorer les erreurs (fichiers verrouillés, etc.)
                console.warn(`[UPDATE] Impossible de supprimer: ${item} (${err.message})`);
              }
            }
          }
          console.log('[UPDATE] Ancien backend nettoyé');

          // 3. Copier le nouveau backend
          console.log('[UPDATE] Copie du nouveau backend...');
          await fs.copy(backendSource, this.projectRoot, {
            overwrite: true,
            filter: (src) => {
              const relativePath = path.relative(backendSource, src).replace(/\\/g, '/');
              // Ne pas écraser les dossiers préservés
              for (const preserve of preserveItems) {
                if (relativePath === preserve || relativePath.startsWith(preserve + '/')) {
                  return false;
                }
              }
              return true;
            }
          });

          // 4. Restaurer les fichiers de configuration
          for (const [configFile, content] of Object.entries(configBackup)) {
            const configPath = path.join(this.projectRoot, configFile);
            await fs.writeFile(configPath, content, 'utf8');
            console.log(`[UPDATE] Config restaurée: ${configFile}`);
          }

          console.log(`✓ Backend installé proprement (${manifest.backend.fileCount} fichiers)`);
        }

        // Copier le frontend vers le chemin détecté
        const frontendSource = path.join(filesDir, 'frontend');
        if (fs.existsSync(frontendSource)) {
          console.log('[UPDATE] Installation du frontend...');
          console.log('[UPDATE] Source:', frontendSource);
          console.log('[UPDATE] Destination:', this.frontendDir);

          // Supprimer complètement l'ancien dossier frontend
          if (fs.existsSync(this.frontendDir)) {
            console.log('[UPDATE] Suppression de l\'ancien frontend...');
            await fs.remove(this.frontendDir);
            console.log('[UPDATE] Ancien frontend supprimé');
          }

          // Créer le nouveau dossier et copier
          console.log('[UPDATE] Copie du nouveau frontend...');
          await fs.ensureDir(this.frontendDir);
          await fs.copy(frontendSource, this.frontendDir);

          // Vérifier que index.html existe
          const indexPath = path.join(this.frontendDir, 'index.html');
          if (fs.existsSync(indexPath)) {
            console.log(`✓ Frontend installé (${manifest.frontend.fileCount} fichiers)`);
            console.log(`✓ index.html vérifié: ${indexPath}`);
          } else {
            console.error('[UPDATE] ERREUR: index.html non trouvé après copie !');
          }
        } else {
          console.error('[UPDATE] ERREUR: Source frontend non trouvée:', frontendSource);
        }

      } else {
        // Installation sélective (ancien comportement)
        for (const fileInfo of manifest.files) {
          const sourcePath = path.join(filesDir, fileInfo.path);
          const destPath = path.join(this.projectRoot, fileInfo.path);

          if (fs.existsSync(sourcePath)) {
            await fs.ensureDir(path.dirname(destPath));
            await fs.copy(sourcePath, destPath, { overwrite: true });
            console.log('Copié:', fileInfo.path);
          } else {
            console.warn('Fichier introuvable dans le package:', fileInfo.path);
          }
        }
      }

      // 5. Exécuter les scripts SQL si présents
      if (manifest.sqlScripts && manifest.sqlScripts.length > 0) {
        console.log('Exécution des scripts SQL...');
        const sqlDir = path.join(extractDir, 'sql');

        for (const sqlScript of manifest.sqlScripts) {
          const sqlPath = path.join(sqlDir, sqlScript);
          if (fs.existsSync(sqlPath)) {
            console.log('Exécution SQL:', sqlScript);
            // TODO: Exécuter le script SQL via votre connexion DB
            // await this.executeSQLScript(sqlPath);
          }
        }
      }

      // 6. Les dépendances npm sont gérées automatiquement au démarrage (auto-install dans index.js)
      // Supprimer le hash pour forcer la vérification au prochain démarrage
      const hashFile = path.join(this.projectRoot, '.package-hash');
      if (fs.existsSync(hashFile)) {
        await fs.remove(hashFile);
        console.log('[UPDATE] Hash package supprimé — les dépendances seront vérifiées au redémarrage');
      }

      // 7. Mettre à jour version.json
      await fs.writeJson(this.versionFile, {
        version: manifest.version,
        updatedAt: new Date().toISOString()
      }, { spaces: 2 });

      // 8. Nettoyer
      await fs.remove(extractDir);
      await fs.remove(zipPath);

      console.log('Installation terminée avec succès');
      return {
        success: true,
        version: manifest.version,
        backupPath
      };

    } catch (error) {
      console.error('Erreur installation:', error.message);

      // Rollback automatique
      if (backupPath && manifest) {
        console.log('Rollback en cours...');
        try {
          if (manifest.type === 'full') {
            await this.rollbackFull(backupPath);
          } else {
            await this.rollback(backupPath, manifest.files);
          }
          console.log('Rollback terminé');
        } catch (rollbackError) {
          console.error('Erreur rollback:', rollbackError.message);
        }
      }

      throw new Error(`Installation échouée: ${error.message}`);
    }
  }

  /**
   * Crée une sauvegarde complète (backend + frontend)
   */
  async createFullBackup() {
    const timestamp = Date.now();
    const backupPath = path.join(this.backupDir, `backup-full-${timestamp}`);
    await fs.ensureDir(backupPath);

    console.log('Création sauvegarde complète...');

    // Sauvegarder le backend
    const backendBackup = path.join(backupPath, 'backend');
    await fs.ensureDir(backendBackup);

    // Liste des dossiers/fichiers à exclure du backup
    const excludePatterns = [
      'node_modules',
      '.git',
      'maj_web/temp',
      'maj_web/backup',
      'maj_web/releases',
      'logs'
    ];

    await fs.copy(this.projectRoot, backendBackup, {
      filter: (src) => {
        const relativePath = path.relative(this.projectRoot, src);
        if (!relativePath) return true; // Racine
        // Normaliser le chemin pour la comparaison (Windows et Unix)
        const normalizedPath = relativePath.replace(/\\/g, '/');
        // Vérifier les patterns d'exclusion
        for (const pattern of excludePatterns) {
          if (normalizedPath === pattern || normalizedPath.startsWith(pattern + '/')) {
            return false;
          }
        }
        return true;
      },
      // Ignorer les erreurs sur fichiers inaccessibles
      errorOnExist: false
    });

    // Sauvegarder le frontend
    if (fs.existsSync(this.frontendDir)) {
      console.log('Sauvegarde du frontend depuis:', this.frontendDir);
      const frontendBackup = path.join(backupPath, 'frontend');
      await fs.copy(this.frontendDir, frontendBackup);
    }

    return backupPath;
  }

  /**
   * Crée une sauvegarde des fichiers qui vont être modifiés
   */
  async createBackup(files) {
    const timestamp = Date.now();
    const backupPath = path.join(this.backupDir, `backup-${timestamp}`);
    await fs.ensureDir(backupPath);

    for (const fileInfo of files) {
      const sourcePath = path.join(this.projectRoot, fileInfo.path);
      const destPath = path.join(backupPath, fileInfo.path);

      if (fs.existsSync(sourcePath)) {
        await fs.ensureDir(path.dirname(destPath));
        await fs.copy(sourcePath, destPath);
      }
    }

    return backupPath;
  }

  /**
   * Restaure une sauvegarde
   */
  async rollback(backupPath, files) {
    if (!fs.existsSync(backupPath)) {
      throw new Error('Sauvegarde introuvable');
    }

    for (const fileInfo of files) {
      const sourcePath = path.join(backupPath, fileInfo.path);
      const destPath = path.join(this.projectRoot, fileInfo.path);

      if (fs.existsSync(sourcePath)) {
        await fs.ensureDir(path.dirname(destPath));
        await fs.copy(sourcePath, destPath, { overwrite: true });
      }
    }

    console.log('Rollback terminé');
  }

  /**
   * Restaure une sauvegarde complète (backend + frontend)
   */
  async rollbackFull(backupPath) {
    if (!fs.existsSync(backupPath)) {
      throw new Error('Sauvegarde introuvable');
    }

    console.log('Restauration complète depuis la sauvegarde...');

    // Restaurer le backend
    const backendBackup = path.join(backupPath, 'backend');
    if (fs.existsSync(backendBackup)) {
      console.log('Restauration du backend...');
      await fs.copy(backendBackup, this.projectRoot, { overwrite: true });
      console.log('✓ Backend restauré');
    }

    // Restaurer le frontend
    const frontendBackup = path.join(backupPath, 'frontend');
    if (fs.existsSync(frontendBackup)) {
      console.log('Restauration du frontend vers:', this.frontendDir);
      await fs.ensureDir(this.frontendDir);
      await fs.emptyDir(this.frontendDir);
      await fs.copy(frontendBackup, this.frontendDir, { overwrite: true });
      console.log('✓ Frontend restauré');
    }

    console.log('Rollback complet terminé');
  }

  /**
   * Liste les sauvegardes disponibles
   */
  async listBackups() {
    if (!fs.existsSync(this.backupDir)) {
      return [];
    }

    const backups = await fs.readdir(this.backupDir);
    return backups
      .filter(name => name.startsWith('backup-'))
      .map(name => {
        const timestamp = parseInt(name.replace('backup-', ''));
        return {
          name,
          path: path.join(this.backupDir, name),
          timestamp,
          date: new Date(timestamp).toISOString()
        };
      })
      .sort((a, b) => b.timestamp - a.timestamp);
  }

  /**
   * Redémarre le serveur via une tâche planifiée Windows
   * La tâche planifiée s'exécute indépendamment du processus Node.js
   * et survit à l'arrêt du service
   */
  async restartServer() {
    console.log('[UPDATE] Redémarrage du service OMAGAPI...');

    const taskName = 'OMAG_Restart_Service';
    const batchPath = path.join(this.tempDir, 'restart-service.bat');

    // Script batch qui redémarre le service et nettoie
    const batchContent = `@echo off
REM Script de redémarrage automatique - généré par updateManager
REM Log du redémarrage
echo [%date% %time%] Redémarrage du service OMAGAPI... >> "${path.join(this.tempDir, 'restart.log')}"

REM Arrêter le service
net stop OMAGAPI
if %errorlevel% neq 0 (
    echo [%date% %time%] Erreur arret service: %errorlevel% >> "${path.join(this.tempDir, 'restart.log')}"
)

REM Attendre que le service soit bien arrêté
timeout /t 3 /nobreak > nul

REM Démarrer le service
net start OMAGAPI
if %errorlevel% neq 0 (
    echo [%date% %time%] Erreur demarrage service: %errorlevel% >> "${path.join(this.tempDir, 'restart.log')}"
) else (
    echo [%date% %time%] Service redémarre avec succes >> "${path.join(this.tempDir, 'restart.log')}"
)

REM Supprimer la tâche planifiée
schtasks /delete /tn "${taskName}" /f > nul 2>&1

REM Supprimer ce script
del "%~f0"
`;

    try {
      // Écrire le script batch
      await fs.writeFile(batchPath, batchContent, 'utf8');
      console.log('[UPDATE] Script de redémarrage créé:', batchPath);

      // Supprimer l'ancienne tâche si elle existe
      try {
        await execPromise(`schtasks /delete /tn "${taskName}" /f`, { timeout: 5000 });
      } catch (e) {
        // Ignorer si la tâche n'existe pas
      }

      // Créer une tâche planifiée qui s'exécute immédiatement avec privilèges SYSTEM
      // La tâche s'exécute une seule fois et se supprime elle-même
      const scheduleCmd = `schtasks /create /tn "${taskName}" /tr "${batchPath}" /sc once /st 00:00 /ru SYSTEM /rl HIGHEST /f`;

      console.log('[UPDATE] Création de la tâche planifiée...');
      await execPromise(scheduleCmd, { timeout: 10000 });

      // Exécuter la tâche immédiatement
      const runCmd = `schtasks /run /tn "${taskName}"`;
      console.log('[UPDATE] Exécution de la tâche planifiée...');
      await execPromise(runCmd, { timeout: 10000 });

      console.log('[UPDATE] Tâche de redémarrage lancée avec succès');
      return true;
    } catch (error) {
      console.error('[UPDATE] Erreur création tâche planifiée:', error.message);

      // Fallback: essayer avec PowerShell en mode détaché
      try {
        console.log('[UPDATE] Tentative avec PowerShell...');
        const psCommand = `Start-Process -FilePath "cmd.exe" -ArgumentList "/c net stop OMAGAPI && timeout /t 3 /nobreak && net start OMAGAPI" -WindowStyle Hidden -Verb RunAs`;

        const { spawn } = require('child_process');
        const child = spawn('powershell.exe', ['-Command', psCommand], {
          detached: true,
          stdio: 'ignore',
          windowsHide: true
        });
        child.unref();

        console.log('[UPDATE] Fallback PowerShell lancé');
        return true;
      } catch (psError) {
        console.error('[UPDATE] Erreur fallback PowerShell:', psError.message);
        return false;
      }
    }
  }
}

module.exports = new UpdateManager();
