"use strict";

/**
 * author:
 * time:
 * describtion: fs适配层
 */
const {
  warn
} = require('../../cli-shared-utils');
const fs = require('fs');
const $path = require('path');

// path <string> | <Buffer> | <URL>
// options <Object>
// recursive <boolean> 如果为 true，则执行递归的目录删除。在递归模式中，如果 path 不存在则不报告错误，并且在失败时重试操作。默认值: false。
// exclude <Regexp> 符合该正则的就不删除
// 在文件（而不是目录）上使用 fs.rmdirSync() 会导致在 Windows 上出现 ENOENT 错误、在 POSIX 上出现 ENOTDIR 错误。
// 稳定性 - 递归的删除是实验性的（因此不在生产环境中使用原来的参数，2而采用更兼容的方式）
const rmdirSync = (path, options) => {
  if (options && options.recursive) {
    let files = [];
    if (fs.existsSync(path)) {
      if (fs.statSync(path).isDirectory()) {
        files = fs.readdirSync(path);
        for (const file of files) {
          const currentPath = $path.join(path, file);
          if (fs.statSync(currentPath).isDirectory()) {
            rmdirSync(currentPath, options);
          } else {
            fs.unlinkSync(currentPath);
          }
        }
      }
    }
  }
  return fs.rmdirSync(path);
};

// 拓展
// 将指定路径上的文件内容解析为json对象
const parse = path => {
  const json = fs.readFileSync(path);
  return JSON.parse(json) || {};
};

// 依赖 rmdirSync
const clearDir = (path, options) => {
  if (fs.existsSync(path)) {
    let files = [];
    if (fs.existsSync(path)) {
      files = fs.readdirSync(path);
      for (const file of files) {
        const currentPath = $path.join(path, file);
        if (options && options.exclude && options.exclude.test(currentPath)) {
          console.log(`不删除${currentPath}`);
          continue;
        }
        if (fs.statSync(currentPath).isDirectory()) {
          clearDir(currentPath, options);
          let currentFiles = [];
          currentFiles = fs.readdirSync(currentPath);
          if (!currentFiles.length) {
            fs.rmdirSync(currentPath);
          }
        } else {
          fs.unlinkSync(currentPath);
        }
      }
    }
  } else {
    if (options && options.build) {
      console.log(`创建目录${path}`);
      fs.mkdirSync(path);
    }
  }
};
const overwrite = (src, dest, options) => {
  if (options) {
    options.basePath = $path.normalize(src);
  }
  overwriteFile(src, dest, options);
};

// 覆盖指定的文件或者文件夹中同名的文件和文件夹(不会删除dest中存在但是src中不存在的文件和文件夹)
const overwriteFile = (src, dest, options) => {
  if (!fs.existsSync(src)) {
    console.log(`不存在${src}`);
    return;
  }
  if (options && options.exclude) {
    const relativePath = $path.relative(options.basePath, $path.normalize(src));
    if (options.basePath !== $path.normalize(src) && options.exclude.test(relativePath)) {
      console.log(`${src}-x->${dest}`);
      return;
    }
  }
  if (fs.statSync(src).isDirectory()) {
    if (!fs.existsSync(dest)) {
      fs.mkdirSync(dest);
    }
    let files = [];
    files = fs.readdirSync(src);
    for (const file of files) {
      if (options && options.exclude && options.exclude.test(file)) {
        continue;
      }
      overwriteFile($path.join(src, file), $path.join(dest, file), options);
    }
  } else {
    const directory = $path.dirname(dest);
    fs.mkdirSync(directory, {
      recursive: true
    });
    // 默认情况下，如果 dest 已经存在，则覆盖它。
    fs.copyFileSync(src, dest);
  }
  return true;
};

/**
 * 遍历目录文件 同步方法
 * @param dir
 * @param files 收集的文件列表
 */
const traverseDirSync = function (dir, files, options) {
  const st = fs.statSync(dir);
  if (st && st.isFile()) {
    files.push(dir);
    return;
  }
  const list = fs.readdirSync(dir);
  list.forEach(function (file) {
    const isExclude = options && options.exclude && options.exclude.test(file);
    if (!isExclude) {
      file = $path.join(dir, file);
      const stat = fs.statSync(file);
      if (stat && stat.isDirectory()) {
        traverseDirSync(file, files, options);
      } else {
        if (options && options.include) {
          if (options.include.test(file)) {
            files.push(file);
          }
          return;
        }
        files.push(file);
      }
    }
  });
};

/**
 * 替换文件夹中文件的字符串
 * @param {*} dir 文件夹
 * @param {*} options {include: 需要替换的文件, exclude: 需要排除的文件或文件夹，map: 需要替换的字符串, to:需要替换成该字符串}
 * options: replaceFrom：map找到字符串，其中需要被替换的字符串，replaceTo：其中需要替换成的字符串
 * @returns
 */
const replaceFiles = (dir, options) => {
  if (!fs.existsSync(dir)) {
    console.log(`不存在${dir}`);
    return;
  }
  if (!options || !options.map) {
    console.log(`options或map、to为空，${options}`);
    return;
  }
  const files = [];
  traverseDirSync(dir, files, options);
  // console.log(files)
  for (const file of files) {
    let content = fs.readFileSync(file, {
      encoding: 'utf-8'
    });
    const regexp = new RegExp(options.map, 'g');
    if (content && options.map.test(content)) {
      if (options.replaceFrom && options.replaceTo) {
        warn(`>>>> ${file} replaceTo ${options.replaceTo}`);
        var match;
        while ((match = regexp.exec(content)) !== null) {
          var replaceStr = match[0].replace(options.replaceFrom, options.replaceTo);
          content = content.replace(match[0], replaceStr);
        }
        fs.writeFileSync(file, content);
      } else if (options.to) {
        warn(`>>>> ${file}  replaceTo ${options.to}`);
        content = content.replace(regexp, options.to);
        fs.writeFileSync(file, content);
      }
    }
  }
  return true;
};
const fsExt = {
  clearDir,
  parse,
  overwrite
};
module.exports = {
  rmdirSync,
  replaceFiles,
  fsExt
};