import fs from 'fs';

/**
 * Récupère les properties de conf/properties.json
 */
function getProperties(): Promise<any> {
  return new Promise((resolve, reject) => {

    let oProperties = {};

    readProperties('conf/properties.json').then((oEngineProperties: any) => {

      // Remplace les balises @prop()
      oEngineProperties = replacePropertiesProps(oEngineProperties);

      // Merge properties du moteur (qui contiennent les chemins vers les autres properties)
      oProperties = { ...oProperties, ...oEngineProperties };

      if (oEngineProperties &&
        oEngineProperties.vas_dir &&
        oEngineProperties.vitis_properties_path) {

        // Ajoute les properties de vitis
        readProperties(oEngineProperties.vitis_properties_path).then((oVitisProperties: any) => {

          // Merge properties vitis
          oProperties = { ...oVitisProperties, ...oProperties };

          // Remplace les balises @prop()
          oProperties = replacePropertiesProps(oProperties);

          // Properties GTF
          if (oEngineProperties.gtf_properties_path) {

            // Ajoute les properties de gtf
            readProperties(oEngineProperties.gtf_properties_path).then((oGTFProperties: any) => {

              // Merge properties gtf
              oProperties = { ...oGTFProperties, ...oProperties };

              // Remplace les balises @prop()
              oProperties = replacePropertiesProps(oProperties);

              // Retour
              resolve(oProperties);
            });
          } else {

            // Retour
            resolve(oProperties);
          }

        });

      } else {
        console.error('Engine properties not valid');
        reject();
      }
    });
  });
}


function getAllPropertiesSync(): any {

  let oEngineProperties = readPropertiesSync('conf/properties.json');
  oEngineProperties = replacePropertiesProps(oEngineProperties);

  let oProperties: any = { ...oEngineProperties };

  if (oEngineProperties && oEngineProperties.vas_dir) {
    const sPath: string = oEngineProperties.vas_dir + '/src/Module';
    const aModules: string[] = fs.readdirSync(sPath);

    for (const sModule of aModules) {
      const sConfPath: string = sPath + '/' + sModule + '/conf/properties.json';

      if (!fs.existsSync(sConfPath)) {
        continue;
      }

      let oModuleProperties = readPropertiesSync(sConfPath);

      // on fait une save de server_domain dans une autre properties pour remplacer si besoin
      if (oModuleProperties._server_domain) {
        oModuleProperties._hostname = oModuleProperties._server_domain;
      }

      oProperties = {
        ...oModuleProperties,
        ...oProperties,
      }
    }
  }

  return replacePropertiesProps(oProperties);
}

/**
 * Effectue les remplacements pour les balises @prop()
 * @param oProperties
 */
function replacePropertiesProps(oProperties: any) {
  for (const key1 in oProperties) {
    if (oProperties.hasOwnProperty(key1)) {
      if (typeof oProperties[key1] == 'string') {
        oProperties[key1] = replacePropertiesInString(oProperties[key1], oProperties);
      }
    }
  }

  return oProperties;
}

/**
 * 
 * @param sEval 
 * @param oProperties 
 * @returns 
 */
function replacePropertiesInString(sEval: string, oProperties: any, aProcessedKeys : string[] = []): string {

  const regex: RegExp = /@prop\(([a-zA-Z0-9_-]+)\)/gm;
  const result = Array.from(sEval.matchAll(regex));
  let sResult: string = sEval;
  // sinon on remplace
  for (const aMatch of result) {
    const sKey = aMatch[1];
    // si on est en train de tourner en boucle on retourne dans l'état on verra plus tard
    if (aProcessedKeys.indexOf(sKey) > -1) {
      return sResult;
    }
    aProcessedKeys.push(sKey)
    if (oProperties[sKey]) {
      const tmpreg = new RegExp('@prop\\(' + sKey + '\\)', 'g')
      sResult = sResult.replace(tmpreg, oProperties[sKey]);
    }
  }

  if (regex.test(sResult)) {
    // s'il y a encore des remplacement à faire on relance avec une liste de controle pour les boucles infinies
    return replacePropertiesInString(sResult, oProperties, aProcessedKeys)
  }

  return sResult;
}

/**
 * Récupère le contenu d'un fichier de properties
 * @param sPath
 */
function readProperties(sPath: string): Promise<any> {
  return new Promise((resolve, reject) => {
    fs.readFile(sPath, 'utf8', (err, data) => {
      if (err) { throw err; }
      let properties;

      try {
        properties = JSON.parse(data);
      } catch (error) { }

      if (properties) {
        resolve(properties);
      } else {
        console.log('cannot get engine properties');
      }
    });
  });
}

function readPropertiesSync(sPath: string): any {
  const data = fs.readFileSync(sPath, 'utf8');
  try {
    return JSON.parse(data);
  } catch (err) {
    console.log('cannot get engine properties')
    return {};
  }
}

/**
 * Retourne la valeur de la vairable d'environnement HOME pour un utilisateur
 * @param sUserName username à utiliser pour calculer le dossier home
 */
function processHomeEnvForUser(sUserName: string): string | null {
  if (process.platform !== "win32") {
    switch (sUserName) {
      case 'root':
        return '/root';
      case 'www-data':
        return '/var/www';
      default:
        return '/home/' + sUserName;
    }
  } else {
    return null;
  }
}

export default {
  getProperties,
  getAllPropertiesSync,
  processHomeEnvForUser,
  replacePropertiesInString
};