const webpack = require('webpack');
const devServer = require('./web/dev-server.js');
const getConfig = require('../configs/index.js');
const {startServer: startWeexLiveLoad, broadcast} = require('./weex/socket-server.js');
const previewSocket = require('./web/web-socket.js');
const cmlLinter = require('chameleon-linter');
const watch = require('glob-watcher');
const fse = require('fs-extra');
const path = require('path');
const fs = require('fs');
const crypto = require('crypto');
const cmlUtils = require('chameleon-tool-utils');
const {getEntryName} = require('../configs/utils.js');

/**
 * 非web端构建
 * @param {*} media  dev or build ...
 * @param {*} type  wx web weex
 */
exports.getBuildPromise = async function (media, type) {

  let options = exports.getOptions(media, type);
  let webpackConfig = await getConfig(options);

  //  非web和weex 并且非增量
  if (!~['web', 'weex'].indexOf(type) && options.increase !== true) {
    // 异步删除output目录
    var outputpath = webpackConfig.output.path;
    if (outputpath) {
      await new Promise(function(resolve, reject) {
        fse.remove(outputpath, function(err) {
          if (err) {
            reject(err);
          }
          resolve();
        })
      })['catch'](e => {
        let message = `clear file error! please remove direction ${outputpath} by yourself!`
        cml.log.error(message);
        throw new Error(e)
      })
    }

  }
  // 工程配置输出
  let projectConfig = options && options.projectConfig;
  let destDir = path.resolve(cml.projectRoot, `dist/${type}`);
  // 支付宝小程序没有工程配置
  if (['wx', 'qq', 'tt'].includes(type) && projectConfig) {
    fse.outputJsonSync(path.join(destDir, 'project.config.json'), projectConfig, {spaces: 4})
  }
  if (type === 'baidu' && projectConfig) {
    fse.outputJsonSync(path.join(destDir, 'project.swan.json'), projectConfig, {spaces: 4})
  }
  // sitemap配置输出
  let siteMap = options && options.siteMap;
  if (type === 'wx' && siteMap) {
    fse.outputJsonSync(path.join(destDir, 'sitemap.json'), siteMap, {spaces: 4})
  }
  return new Promise(function(resolve, reject) {
    // watch模式
    if (media === 'dev') {
      const compiler = webpack(webpackConfig);
      if (type === 'weex') {
        startWeexLiveLoad(options);
      }
      let optimizeCML = cml.config.get().optimize;
      let watchOptions = {
        aggregateTimeout: 300,
        poll: undefined
      }
      if (optimizeCML && !optimizeCML.watchNodeModules) {
        watchOptions.ignored = /node_modules/;
      }
      compiler.watch(watchOptions, (err, stats) => {

        if (type === 'weex') {
          if (!(stats && stats.compilation && stats.compilation.errors && stats.compilation.errors.length > 0)) {
            broadcast('weex_refresh');
          }
          previewSocket.broadcast('weex_refresh');
        }
        if (err) {
          reject(err);
        }
        resolve();
      });
    } else {
      // build模式
      webpack(webpackConfig, (err, stats) => {
        if (err) {
          reject(err);
        }
        resolve();
      });
    }
  })
}

/**
 * 获取webpack配置
 * @param {*} media  dev or build ...
 * @param {*} type  wx web weex
 */
exports.getOptions = function (media, type) {
  let chameleonConfig = (cml.config.get() && cml.config.get()[type] && cml.config.get()[type][media]) || {};

  if (!chameleonConfig) {
    cml.log.error(`在chameleon的config中未找到 ${media}的配置参数`);
    return;
  }

  let options = cml.utils.merge(
    chameleonConfig,
    {
      type: type,
      media,
      root: cml.projectRoot,
      buildType: 'weex' // 传递给dev-server，判断启动devserver的类型 暂时固定为weex
    }
  )

  return options;
}

/**
 * web端构建
 * @param {*} type   dev or build or 其他
 * @param {*} isCompile   是否要编译web端 不编译web时也要启动web端服务
 */
exports.getWebBuildPromise = async function (media, isCompile) {
  if (media === 'dev') {
    let options = exports.getOptions(media, 'web');
    let webpackConfig = await getConfig(options)
    let compiler;
    if (isCompile) {
      compiler = webpack(webpackConfig);
    }
    return devServer({webpackConfig, options, compiler});
  } else {
    if (isCompile) {
      return exports.getBuildPromise(media, 'web');
    } else {
      return Promise.resolve();
    }
  }
}

/**
 * 构建所有端
 * @param {*} media  dev or build ...
 * @param {*} type  wx web weex
 */
exports.startReleaseAll = async function (media) {
  if (media === 'build') {
    process.env.NODE_ENV = 'production';
  }
  let cmlConfig = cml.config.get();
  let allPlatform = cmlConfig.platforms;
  let offPlatform = [];
  let activePlatform = []; // 启动编译的platform
  if (media === 'dev') {
    offPlatform = cmlConfig.devOffPlatform;
  } else if (media === 'build') {
    offPlatform = cmlConfig.buildOffPlatform;
  }
  // 获取激活平台
  for (let i = 0, j = allPlatform.length; i < j; i++) {
    let platform = allPlatform[i];
    if (!~offPlatform.indexOf(platform)) {
      activePlatform.push(platform)
    }
  }

  // 是否编译web端
  let isCompile = !!~activePlatform.indexOf('web');
  // 给preview使用
  cml.activePlatform = activePlatform;

  for (let i = 0, j = activePlatform.length; i < j; i++) {
    let platform = activePlatform[i];
    if (platform !== 'web') {
      await exports.getBuildPromise(media, platform);
    }
  }

  await exports.getWebBuildPromise(media, isCompile);
  if (media === 'build') {
    exports.createConfigJson()
  }
  startCmlLinter(media);
}

exports.startReleaseOne = async function(media, type) {
  if (media === 'build') {
    process.env.NODE_ENV = 'production';
  }
  // 给preview使用
  cml.activePlatform = [type];
  if (type === 'web') {
    await exports.getWebBuildPromise(media, true);
  } else {
    let build = exports.getBuildPromise(media, type);
    // 如果dev模式再启动web服务
    if (media === 'dev') {
      await build.then(res => {
        exports.getWebBuildPromise(media, false);
      })
    } else {
      await build;
    }
  }
  if (media === 'build') {
    exports.createConfigJson()
  }
  startCmlLinter(media);

}

// let lastLintTime = null;
function startCmlLinter(media) {
  if (cml.config.get().enableLinter === true && media === 'dev') {
    cmlLinter(cml.projectRoot);

    const watcher = watch([cml.projectRoot + '/src/**/**.**']);
    watcher.on('change', function(path, stat) {
      cmlLinter(cml.projectRoot);
    });
  }
}
exports.getMD5 = function(weexjs) {
  if (!weexjs) {
    return '';
  }
  const weexjsName = weexjs.split('/').pop();
  const weexjsPath = path.resolve(cml.projectRoot, 'dist/weex/', weexjsName);

  if (cml.utils.isFile(weexjsPath)) {
    const md5sum = crypto.createHash('md5');
    const buffer = fs.readFileSync(weexjsPath);
    md5sum.update(buffer);
    let md5str = md5sum.digest('hex').toUpperCase();
    return md5str
  }
}
exports.createConfigJson = function() {
  let configJsonPath = path.join(cml.projectRoot, 'dist/config.json');
  let configObj = {};
  if (cml.utils.isFile(configJsonPath)) {
    configObj = JSON.parse(fs.readFileSync(configJsonPath, {encoding: 'utf-8'}))
  }
  // 获取weex jsbundle地址

  let config = cml.config.get();
  config.buildInfo = config.buildInfo || {};
  let {wxAppId = '', baiduAppId = '', alipayAppId = '', qqAppId = '', ttAppId = ''} = config.buildInfo;
  let extPlatform = config.extPlatform ;
  let extCommand = (typeof extPlatform === 'object') ? Object.keys(extPlatform)[0] : undefined;
  let extAppId = ''
  if (extCommand) { // extCommand 可能值为 tt  quickApp
    extAppId = config.buildInfo && config.buildInfo[`${extCommand}AppId`]
  }
  let {routerConfig, hasError} = cml.utils.getRouterConfig();
  let entryName = getEntryName();
  let weexjs = (configObj[entryName] && configObj[entryName].js) || '';
  let md5str = exports.getMD5(weexjs);
  let mpa = routerConfig.mpa;


  if (hasError) {
    throw new Error('router.config.json格式不正确')
  }

  let result = [];
  if (routerConfig) {
    if (~cml.activePlatform.indexOf('web') && !routerConfig.domain) {
      throw new Error('router.config.json 中未设置web端需要的domain字段');
    }
    let {domain, mode} = routerConfig;
    routerConfig.routes.forEach(item => {
      // 如果配置了weex多页面，那么每个路由都要重新计算对应的weexjs
      if (mpa && mpa.weexMpa && Array.isArray(mpa.weexMpa)) { // 配置了weex多页面
        let weexMpa = mpa.weexMpa;
        for (let i = 0; i < weexMpa.length ; i++) {
          if (Array.isArray(weexMpa[i].paths) && weexMpa[i].paths.includes(item.path)) {
            if (typeof weexMpa[i].name === 'string') {
              weexjs = configObj[`${weexMpa[i].name}`] && configObj[`${weexMpa[i].name}`].js;
            } else {
              weexjs = configObj[`${entryName}${i}`] && configObj[`${entryName}${i}`].js;
            }
            // weexjs = configObj[`${entryName}${i}`] && configObj[`${entryName}${i}`].js;
            md5str = exports.getMD5(weexjs);
          }
        }
      }
      let webUrl = domain;
      if (mode === 'history') {
        webUrl += item.url;
      } else if (mode === 'hash') {
        webUrl += ('#' + item.url);
      }
      let route = {
        wx: {
          appId: wxAppId,
          path: item.path
        },
        baidu: {
          appId: baiduAppId,
          path: item.path
        },
        alipay: {
          appId: alipayAppId,
          path: item.path
        },
        qq: {
          appId: qqAppId,
          path: item.path
        },
        tt: {
          appId: ttAppId,
          path: item.path
        },
        web: {
          url: webUrl
        },
        weex: {
          url: weexjs,
          md5: md5str,
          query: {
            path: item.path
          }
        }
      }
      if (item.extra) {
        route.extra = item.extra;
      }
      if (extCommand) {
        route[extCommand] = {
          appId: extAppId,
          path: item.path
        }
      }
      result.push(route);
    })
    // 处理subProject配置的npm包中cml项目的页面
    let subProject = cml.config.get().subProject;
    if (subProject && subProject.length > 0) {
      subProject.forEach(function(item) {
        let npmName = cmlUtils.isString(item) ? item : item.npmName;
        let npmRouterConfig = cml.utils.readsubProjectRouterConfig(cml.projectRoot, npmName);
        npmRouterConfig.routes && npmRouterConfig.routes.forEach(item => {
          let cmlFilePath = path.join(cml.projectRoot, 'node_modules', npmName, 'src', item.path + '.cml');
          let routePath = cml.utils.getPureEntryName(cmlFilePath, '', cml.projectRoot);
          routePath = cml.utils.handleSpecialChar(routePath);
          let webUrl = domain;
          if (mode === 'history') {
            webUrl += item.url;
          } else if (mode === 'hash') {
            webUrl += ('#' + item.url);
          }
          if (routePath[0] !== '/') {
            routePath = '/' + routePath;
          }
          let route = {
            wx: {
              appId: wxAppId,
              path: routePath
            },
            baidu: {
              appId: baiduAppId,
              path: routePath
            },
            alipay: {
              appId: alipayAppId,
              path: routePath
            },
            qq: {
              appId: qqAppId,
              path: routePath
            },
            tt: {
              appId: ttAppId,
              path: item.path
            },
            web: {
              url: webUrl
            },
            weex: {
              url: weexjs,
              query: {
                path: routePath
              }
            }
          }
          if (item.extra) {
            route.extra = item.extra;
          }
          if (extCommand) {
            route[extCommand] = {
              appId: extAppId,
              path: item.path
            }
          }
          result.push(route);
        })
      })
    }
  }

  result.forEach(item => {
    Object.keys(item).forEach(key => {
      if (!~cml.activePlatform.indexOf(key) && key !== 'extra') {
        delete item[key]
      }
    })
  })

  cml.event.emit('config-json', result);
  /* eslint-disable-next-line */
  fse.outputFileSync(configJsonPath, JSON.stringify(result, '', 4))
}
