const fs = require('fs'); // 👈 Usa il modulo promises
const axios = require('axios');
const path = require('path');

/**
 * Scarica un'immagine da una URL e la salva in una directory locale
 * @param {string} url - L'URL dell'immagine da scaricare
 * @param {string} filepath - Il percorso dove salvare l'immagine scaricata
 */
class ImageDownloader {
  /**
   * Scarica un'immagine da una URL e la salva in una directory locale.
   * Tenta di scaricare fino a 3 volte in caso di errore, con un ritardo tra i tentativi.
   *
   * @param {string} url - L'URL dell'immagine da scaricare
   * @param {string} filepath - Il percorso dove salvare l'immagine scaricata
   * @param {number} maxRetries - Numero massimo di tentativi in caso di fallimento (default: 3)
   * @param {number} delay - Ritardo in millisecondi tra i tentativi (default: 1000)
   * @returns {Promise<boolean>}
   */
  async downloadImage(url, filepath, options = {}) {
    const {
      maxRetries = 3, // Aumentato il numero di tentativi predefiniti
      initialDelay = 1000, // Ritardo iniziale
      maxDelay = 10000, // Ritardo massimo
      timeout = 30000, // Timeout della richiesta
      validateContentType = true, // Verifica del tipo di contenuto
      nomefileoriginale = true,
    } = options;

    // Funzione per il backoff esponenziale
    const getDelay = (attempt) => {
      return Math.min(initialDelay * Math.pow(2, attempt - 1), maxDelay);
    };

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        // Verifica se il filepath esiste già
        if (await this.isFileExistsAsync(filepath)) {
          fs.unlinkSync(filepath);
        }

        if (attempt > 1) console.log(`📥 Tentativo ${attempt}/${maxRetries} - Scaricamento: ${url}`);

        const response = await axios({
          url,
          method: 'GET',
          responseType: 'stream',
          timeout: timeout,
          maxRedirects: 5,
          headers: {
            'User-Agent':
              'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            Accept: 'image/jpeg,image/png,image/webp,image/gif,image/*', // Specifico per immagini
            'Cache-Control': 'no-cache', // Evita problemi di caching
            Connection: 'keep-alive',
          },
          validateStatus: (status) => status === 200, // Per immagini ci aspettiamo 200
          maxContentLength: 10 * 1024 * 1024, // Limite di 10MB per immagine
        });

        // Verifica del content-type se richiesto
        if (validateContentType) {
          const contentType = response.headers['content-type'];
          if (!contentType || !contentType.startsWith('image/')) {
            throw new Error(`Content-Type non valido: ${contentType}`);
          }
        }

        // Verifica della dimensione del file
        const contentLength = parseInt(response.headers['content-length']);
        if (contentLength && contentLength > 100 * 1024 * 1024) {
          // 100MB limit
          throw new Error('File troppo grande');
        }

        let downloadedBytes = 0;
        response.data.on('data', (chunk) => {
          downloadedBytes += chunk.length;
        });

        let writer = null;

        if (nomefileoriginale) {
          // Estrai il nome del file dall'URL o da Content-Disposition
          //let fileName = this.extractFileNameFromUrl(url) || this.extractFileNameFromHeaders(response.headers);
          let fileName = path.basename(response.data.responseUrl);

          // Se il nome del file non è specificato, genera un nome predefinito
          if (!fileName) {
            fileName = `image_${Date.now()}.jpg`;
          }

          // Genera il percorso completo del file
          const fullPath = path.join(path.dirname(filepath), fileName);

          filepath = fullPath;
        }

        // Scrivi il file sul disco
        writer = fs.createWriteStream(filepath);

        response.data.pipe(writer);

        await new Promise((resolve, reject) => {
          writer.on('finish', resolve);
          writer.on('error', (error) => {
            fs.unlink(filepath, () => {}); // Pulizia in caso di errore
            reject(error);
          });
          response.data.on('error', (error) => {
            fs.unlink(filepath, () => {});
            reject(error);
          });
        });

        console.info(`✅ Immagine scaricata con successo in ${filepath}`);

        return { ris: true, filepath };
      } catch (error) {
        console.error(`❌ Errore nel tentativo ${attempt}/${maxRetries}:`, error.message);

        // Pulizia del file in caso di errore
        if (await this.isFileExistsAsync(filepath)) {
          fs.unlinkSync(filepath);
        }

        // se in error.message c'è '404' allora esci e ritorna code: 404
        if (error.message.includes('404')) {
          return { ris: false, code: 404 };
        }

        if (attempt === maxRetries) {
          console.error(`Download fallito dopo ${maxRetries} tentativi: ${error.message}`);
          return { ris: false };
        }

        const delay = getDelay(attempt);
        console.info(`🔄 Attendo ${delay}ms prima del prossimo tentativo...`);
        await new Promise((resolve) => setTimeout(resolve, delay));
      }
    }
  }

  // Funzione per estrarre il nome del file dall'URL
  extractFileNameFromUrl(url) {
    const match = url.match(/\/([^/?#]+)(?:[?#]|$)/);
    return match ? decodeURIComponent(match[1]) : null;
  }

  // Funzione per estrarre il nome del file da Content-Disposition
  extractFileNameFromHeaders(headers) {
    const contentDisposition = headers['content-disposition'];
    if (contentDisposition) {
      const match = contentDisposition.match(/filename="([^"]+)"/);
      if (match) {
        return decodeURIComponent(match[1]);
      }
    }
    return null;
  }

  async isFileExistsAsync (filename) {
    try {
      let fileExists = await fs.promises
        .stat(filename)
        .then(() => true)
        .catch(() => false);
      // console.log(filename, 'esiste', fileExists)
      return fileExists;
    } catch (e) {
      // console.log(filename, 'esiste', 'FALSE')
      return false;
    }
  }

}

module.exports = ImageDownloader;
