var libChildProcess = require('child_process');
var libUtil = require('util');
var libPath = require('path');
var libOS = require('os');
var libFS = require('fs');
var libYazl = require("yazl");
var recursiveCopy = require("recursive-copy");



fcf.module({
  name:         "fcfBackup:Tools/backup.js",
  dependencies: ["fcf:NSystem/fs.js"],
  lazy:         [],
  module: function(fs){
    var Namespace = fcf.prepareObject(fcf, "packages/fcfBackup/Tools");

    class Backup {
      constructor(){
      }

      getFileSuffix(a_id){
        return `backup-${fcf.dateFormat(new Date(), "Y-m-dTH:i:s")}-${a_id}`;
      }

      async saveApplicationFiles(a_id) {
        if (!a_id) {
          a_id = fcf.id(16, true);
        }
        let fileSuffix = Namespace.backup.getFileSuffix(a_id);
        let backupDirectory = fcf.getPath(fcf.application.getSystemVariable("fcfBackup:config").directory);
        let zip = new libYazl.ZipFile();

        await fs.prepareDirectory(backupDirectory);

        async function addFSStruct(a_path, a_dir){
          let files = await libUtil.promisify(libFS.readdir)(fcf.getPath(libPath.join(a_dir, a_path)));
          for(let i = 0; i < files.length; ++i) {
            let filePath = libPath.join(a_path, files[i]);
            let realFilePath = fcf.getPath(libPath.join(a_dir, filePath));
            let stat = await libUtil.promisify(libFS.lstat)(fcf.getPath(realFilePath));
            if (!stat.isDirectory()){
              zip.addFile(realFilePath, filePath);
            } else {
              let isNotEmpty = await addFSStruct(filePath, a_dir);
              if (!isNotEmpty) {
                zip.addEmptyDirectory(filePath);
              }
            }
          }
          return !fcf.empty(files);
        }

        let tmpDir = libPath.join(libOS.tmpdir(), "fcf-framework-backup", a_id);
        await fs.prepareDirectory(tmpDir);
        let files = await libUtil.promisify(libFS.readdir)(fcf.getPath(""));

        try {
          await fcf.each(files, async (a_key, a_file)=>{
            let originRealFilePath = fcf.getPath(a_file);
            if (a_file == ".hg" ||
                a_file == ".git" ||
                originRealFilePath == backupDirectory) {
              return;
            }
            await libUtil.promisify(recursiveCopy)(fcf.getPath(a_file), libPath.join(tmpDir, a_file));
          });
          await addFSStruct("", tmpDir);
        } catch (e) {
          try {
            await libUtil.promisify(libFS.rmdir)(tmpDir, { recursive: true, force: true });
          } catch(err){
          }
          throw e;
        }

        return fcf.actions()
        .then((a_res, a_act)=>{
          let fsStream = libFS.createWriteStream(fcf.getPath(`${backupDirectory}/fs-${fileSuffix}.zip`));
          fsStream.on("error", function(a_error) {
            a_act.error(a_error);
          });
          zip.outputStream.pipe(fsStream)
          .on("close", function() {
            a_act.complete();
          })
          .on("error", function(a_error) {
            a_act.error(a_error);
          });
          zip.on("error", function(a_error) {
            a_act.error(a_error);
          });
          zip.end();
        })
        .then(()=>{
          return a_id;
        })
        .finally(async ()=>{
          try {
            await libUtil.promisify(libFS.rmdir)(tmpDir, { recursive: true, force: true });
          } catch(err){
          }
        })

      }

      async createDBBackup(a_id) {
        if (!a_id) {
          a_id = fcf.id(16, true);
        }
        let fileSuffix = Namespace.backup.getFileSuffix(a_id);
        let directory  = fcf.application.getSystemVariable("fcfBackup:config").directory;
        await fs.prepareDirectory(directory);
        await fcf.each(fcf.application.getConfiguration().dataClient.connections, async (a_connectionName, a_connectionInfo)=>{
          let sql = (await fcf.actions()
                    .then((a_res, a_act)=>{
                      let stderror = "";
                      let stdout   = "";
                      let proc = libChildProcess.spawn( "mysqldump",
                                            [ `-u${a_connectionInfo.user}`,
                                              `-p${a_connectionInfo.pass}`,
                                              a_connectionInfo.db]
                                          );
                      proc.stdout.on('data', function (a_data) {
                        stdout += a_data.toString();
                      });
                      proc.stderr.on('data', function (a_data) {
                        stderror += a_data.toString();
                      });
                      proc.on('close', function (a_code) {
                        if (a_code){
                          a_act.error(new fcf.Exception("ERROR", {error: "Invalid execution mysqldump: " + stderror}));
                          return;
                        }
                        a_act.complete(stdout);
                      });
                    }));
          fs.writeFile(`${directory}/db-${a_connectionName}-${fileSuffix}.sql`, sql, [{server: fcf.application.getConfiguration().mainServer}]);
        });
        return a_id;
      }
    };

    Namespace.backup = new Backup();

    return Namespace.backup;
  }
});
