import fs from 'fs';
import path from 'path';
import archiver from 'archiver';
import Logger from '../logger';
import decompress from 'decompress';

/**
 * Classe permettant la gestion des fichiers en local
 */
export default class LocalFS {

  // properties
  private properties_: any;

  // Service de logs
  private logger_: Logger;

  constructor(properties: any, logger: Logger) {
    this.properties_ = properties;
    this.logger_ = logger;
  }

  public zipDir(dirPath: string, zipName: string | null = null): Promise<any> {
    return new Promise((resolve, reject) => {
      let zipPath = `${dirPath}.zip`;

      if (zipName && zipName.indexOf('.zip') > 0) {
        const aPath: Array<string> = dirPath.split('/');
        aPath.pop();
        aPath.push(zipName);
        zipPath = aPath.join('/');
      }

      const output = fs.createWriteStream(zipPath);
      const archive = archiver('zip', {
        zlib: {
          level: 9,
        },
      });

      output.on('close', () => {
        // Supprime le dossier
        this.deleteFolderRecursive_(dirPath);
        setTimeout(() => {

          // Retour
          resolve(zipPath);
        }, 0);
      });

      archive.on('error', (err) => {
        throw err;
      });

      archive.pipe(output);
      archive.directory(dirPath, false);
      archive.finalize();
    });
  }

  /**
 * Dézippe un dossier
 * @param zipPath 
 * @param unzipToDir 
 * @returns 
 */
  public unzip(zipPath: string, unzipToDir: string): Promise<any> {
    return decompress(zipPath, unzipToDir);
  }

  /**
   * Supprime une dossier et son contenu
   * @param {*} folderPath
   */
  public deleteFolder(folderPath: string) {
    this.deleteFolderRecursive_(folderPath);
  }

  /**
   * Donne tous les droits au dossier
   * @param {*} folderPath
   * @param {*} sRights
   */
  public changeFolderRights(folderPath: string, sRights: string) {
    this.changeFolderRightsRecursive_(folderPath, sRights);
  }

  /**
   * Donne tous les droits au dossier
   * @param {*} folderPath
   * @param {*} sOwner
   */
  public changeFolderOwner(folderPath: string, sOwner: string) {

    if (process.platform !== "win32") {
      const userid = require('userid');
      const uid = userid.uid(sOwner);
      const gid = userid.gid(sOwner);

      if (Number.isInteger(uid) && Number.isInteger(gid)) {
        this.changeFolderOwnerRecursive_(folderPath, uid, gid);
      }
    }
  }

  /**
   * Supprime une dossier et son contenu
   * @param {*} folderPath
   */
  private deleteFolderRecursive_(folderPath: string) {
    if (fs.existsSync(folderPath)) {
      fs.readdirSync(folderPath).forEach((file, index) => {
        const curPath = path.join(folderPath, file);
        if (fs.lstatSync(curPath).isDirectory()) {
          // Récursive
          this.deleteFolderRecursive_(curPath);
        } else {
          // Suppression du fichier
          fs.unlinkSync(curPath);
        }
      });
      // Suppression du dossier
      fs.rmdirSync(folderPath);
    }
  }

  /**
   * Donne tous les droits au dossier
   * @param folderPath 
   */
  private changeFolderRightsRecursive_(folderPath: string, sRights: string) {
    if (fs.existsSync(folderPath)) {
      fs.readdirSync(folderPath).forEach((file, index) => {
        const curPath = path.join(folderPath, file);
        if (fs.lstatSync(curPath).isDirectory()) {
          // Récursive
          this.changeFolderRightsRecursive_(curPath, sRights);
        } else {
          // Modification du fichier
          fs.chmodSync(curPath, sRights);
        }
      });
      // Modification du dossier
      fs.chmodSync(folderPath, sRights);
    }
  }

  /**
   * Change le propriétaire du dossier
   * @param folderPath 
   */
  private changeFolderOwnerRecursive_(folderPath: string, uid: number, gid: number) {
    if (fs.existsSync(folderPath)) {
      fs.readdirSync(folderPath).forEach((file, index) => {
        const curPath = path.join(folderPath, file);
        if (fs.lstatSync(curPath).isDirectory()) {
          // Récursive
          this.changeFolderOwnerRecursive_(curPath, uid, gid);
        } else {
          // Modification du fichier
          fs.chownSync(curPath, uid, gid);
        }
      });
      // Modification du dossier
      fs.chownSync(folderPath, uid, gid);
    }
  }

  /**
   * 
   * @param srcPath 
   * @param dstPath 
   * @returns 
   */
  public copyDirRecursiveSync(srcPath: string, dstPath: string) {
    // Vérifier si le dossier source existe
    if (!fs.existsSync(srcPath)) {
      return false;
    }

    // Créer le dossier de destination s'il n'existe pas
    if (!fs.existsSync(dstPath)) {
      fs.mkdirSync(dstPath, { recursive: true });
    }
    // Lister le contenu du dossier source
    const files: string[] = fs.readdirSync(srcPath);

    for (const file of files) {
      const sourcePath: string = srcPath + '/' + file;
      const destinationPath: string = dstPath + '/' + file;

      if (fs.statSync(sourcePath).isDirectory()) {
        // Si c'est un sous-dossier, copier récursivement
        this.copyDirRecursiveSync(sourcePath, destinationPath);
      } else {
        // Si c'est un fichier, le copier
        fs.copyFileSync(sourcePath, destinationPath);
      }
    }

    return true;
  }
}