const path = require('path');
// const debug = require('debug');
const inquirer = require('inquirer');
const EventEmitter = require('events');
const Generator = require('./Generator');
const cloneDeep = require('lodash.clonedeep');
const sortObject = require('./util/sortObject');
// const getVersions = require('./util/getVersions');
const PackageManager = require('./util/ProjectPackageManager');
const { clearConsole } = require('./util/clearConsole');
const PromptModuleAPI = require('./PromptModuleAPI');
const writeFileTree = require('./util/writeFileTree');

// const { formatFeatures } = require('./util/features');
// const loadLocalPreset = require('./util/loadLocalPreset');
// const loadRemotePreset = require('./util/loadRemotePreset');
// const generateReadme = require('./util/generateReadme');
// const { resolvePkg, isOfficialPlugin } = require('@vue/cli-shared-utils');

// const {
//   defaults,
//   saveOptions,
//   loadOptions,
//   savePreset,
//   validatePreset,
//   rcPath
// } = require('./options');

// const {
//   chalk,
//   execa,

//   log,
//   warn,
//   error,

//   hasGit,
//   hasProjectGit,
//   hasYarn,
//   hasPnpm3OrLater,
//   hasPnpmVersionOrLater,

//   exit,
//   loadModule
// } = require('@vue/cli-shared-utils');

const execa = require('execa');
const chalk = require('chalk');
const { log, warn, error } = require('./shared-utils/logger');
const { resolvePkg } = require('./shared-utils/pkg');
const { hasGit, hasProjectGit, hasYarn } = require('./shared-utils/env');

// const isManualMode = answers => answers.preset === '__manual__';

module.exports = class Creator extends EventEmitter {
  constructor(name, context, promptModules) {
    super();

    this.name = name;
    this.context = process.env.MY_CONTEXT = context;

    // this.context = context;
    // console.log('process.env :>> ', process.env);
    // console.log(' this.context :>> ', this.context);

    // const { presetPrompt, featurePrompt } = this.resolveIntroPrompts();
    // this.presetPrompt = presetPrompt;
    // this.featurePrompt = featurePrompt;

    // this.outroPrompts = this.resolveOutroPrompts();
    this.injectedPrompts = [];
    this.promptCompleteCbs = [];
    // this.afterInvokeCbs = [];
    // this.afterAnyInvokeCbs = [];

    // this.run = this.run.bind(this);

    // 注册弹框提示 prompt
    const promptAPI = new PromptModuleAPI(this);
    promptModules.forEach(m => m(promptAPI));
  }

  async create(cliOptions = {}, preset = null) {
    // const { run, name, context, afterInvokeCbs, afterAnyInvokeCbs } = this;
    const { name, context } = this;

    preset = await this.promptAndResolvePreset();

    // const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG;
    // const { run, name, context, afterInvokeCbs, afterAnyInvokeCbs } = this;

    // if (!preset) {
    //   if (cliOptions.preset) {
    //     // vue create foo --preset bar
    //     preset = await this.resolvePreset(cliOptions.preset, cliOptions.clone);
    //   } else if (cliOptions.default) {
    //     // vue create foo --default
    //     preset = defaults.presets['Default (Vue 3)'];
    //   } else if (cliOptions.inlinePreset) {
    //     // vue create foo --inlinePreset {...}
    //     try {
    //       preset = JSON.parse(cliOptions.inlinePreset);
    //     } catch (e) {
    //       error(
    //         `CLI inline preset is not valid JSON: ${cliOptions.inlinePreset}`
    //       );
    //       exit(1);
    //     }
    //   } else {
    //     preset = await this.promptAndResolvePreset();
    //   }
    // }

    // clone before mutating
    preset = cloneDeep(preset);

    // inject core service
    preset.plugins['@kt/unity-pc-cli-service'] = Object.assign(
      {
        projectName: name
      },
      preset
    );
    // preset.plugins.dayjs = {};

    // if (cliOptions.bare) {
    //   preset.plugins['@vue/cli-service'].bare = true;
    // }

    // // legacy support for router
    // if (preset.router) {
    //   preset.plugins['@vue/cli-plugin-router'] = {};

    //   if (preset.routerHistoryMode) {
    //     preset.plugins['@vue/cli-plugin-router'].historyMode = true;
    //   }
    // }

    // // legacy support for vuex
    // if (preset.vuex) {
    //   preset.plugins['@vue/cli-plugin-vuex'] = {};
    // }

    /**
     * 确定用什么包管理来管理项目，这里不让用户做选择，不保存用户配置，每次都会进行一次判断，优先使用 yarn
     */
    // const packageManager =
    //   cliOptions.packageManager ||
    //   loadOptions().packageManager ||
    //   (hasYarn() ? 'yarn' : null) ||
    //   (hasPnpm3OrLater() ? 'pnpm' : 'npm');

    const packageManager = hasYarn() ? 'yarn' : 'npm';

    await clearConsole();

    const pm = new PackageManager({
      context,
      forcePackageManager: packageManager
    });

    log(`✨  Creating project in ${chalk.yellow(context)}.`);
    this.emit('creation', { event: 'creating' });

    // get latest CLI plugin version
    // const { latestMinor } = await getVersions();

    // 生成 package.json 用来安装对应的依赖
    const pkg = {
      name,
      version: '1.0.0',
      private: true,
      // devDependencies: {},
      ...resolvePkg(context)
    };
    const deps = Object.keys(preset.plugins);

    const local = require(`../package.json`).version;
    const latestMinor = local;
    // if (process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG) {
    //   return (sessionCached = {
    //     current: local,
    //     latest: local,
    //     latestMinor: local
    //   });
    // }

    // deps.forEach(dep => {
    //   // if (preset.plugins[dep]._isPreset) {
    //   //   return;
    //   // }
    //   let { version } = preset.plugins[dep];
    //   if (!version) {
    //     version = `~${latestMinor}`;
    //   }
    //   pkg.devDependencies[dep] = version;
    // });

    // write package.json
    await writeFileTree(context, {
      'package.json': JSON.stringify(pkg, null, 2)
    });

    // // generate a .npmrc file for pnpm, to persist the `shamefully-flatten` flag
    // if (packageManager === 'pnpm') {
    //   const pnpmConfig = hasPnpmVersionOrLater('4.0.0')
    //     ? 'shamefully-hoist=true\n'
    //     : 'shamefully-flatten=true\n';

    //   await writeFileTree(context, {
    //     '.npmrc': pnpmConfig
    //   });
    // }

    // intilaize git repository before installing deps
    // so that vue-cli-service can setup git hooks.
    const shouldInitGit = this.shouldInitGit(cliOptions);
    if (shouldInitGit) {
      log(`🗃  Initializing git repository...`);
      this.emit('creation', { event: 'git-init' });
      await this.run('git init');
    }

    // install plugins
    log(`⚙\u{fe0f}  Installing CLI plugins. This might take a while...`);
    log();
    this.emit('creation', { event: 'plugins-install' });

    // TODO:需要判断如果是本地在测试的场景下，这里需要特殊处理，不执行 install
    const falsy = false;
    if (falsy) {
      await pm.install();
    }

    // if (isTestOrDebug && !process.env.VUE_CLI_TEST_DO_INSTALL_PLUGIN) {
    //   // in development, avoid installation process
    //   await require('./util/setupDevProject')(context);
    // } else {
    //   await pm.install();
    // }

    // run generator，开始生成项目
    log(`🚀  Invoking generators...`);
    this.emit('creation', { event: 'invoking-generators' });
    const plugins = await this.resolvePlugins(preset.plugins, pkg);

    const generator = new Generator(context, {
      pkg,
      plugins
      // afterInvokeCbs,
      // afterAnyInvokeCbs
    });
    // await generator.generate({
    //   extractConfigFiles: preset.useConfigFiles
    // });
    await generator.generate();

    // install additional deps (injected by generators)，来自各个插件 generator/index.js 内部的 extendPackage，如 @vue/cli-plugin-router 等
    log(`📦  Installing additional dependencies...`);
    this.emit('creation', { event: 'deps-install' });
    log();

    // if (!isTestOrDebug || process.env.VUE_CLI_TEST_DO_INSTALL_PLUGIN) {
    await pm.install();
    // }

    // run complete cbs if any (injected by generators)
    // log(`⚓  Running completion hooks...`);
    // this.emit('creation', { event: 'completion-hooks' });
    // for (const cb of afterInvokeCbs) {
    //   await cb();
    // }
    // for (const cb of afterAnyInvokeCbs) {
    //   await cb();
    // }

    // if (!generator.files['README.md']) {
    //   // generate README.md
    //   log();
    //   log('📄  Generating README.md...');
    //   await writeFileTree(context, {
    //     'README.md': generateReadme(generator.pkg, packageManager)
    //   });
    // }

    // // commit initial state
    // let gitCommitFailed = false;
    // if (shouldInitGit) {
    //   await run('git add -A');
    //   if (isTestOrDebug) {
    //     await run('git', ['config', 'user.name', 'test']);
    //     await run('git', ['config', 'user.email', 'test@test.com']);
    //     await run('git', ['config', 'commit.gpgSign', 'false']);
    //   }
    //   const msg = typeof cliOptions.git === 'string' ? cliOptions.git : 'init';
    //   try {
    //     await run('git', ['commit', '-m', msg, '--no-verify']);
    //   } catch (e) {
    //     gitCommitFailed = true;
    //   }
    // }

    // log instructions
    log();
    log(`🎉  Successfully created project ${chalk.yellow(name)}.`);
    // if (!cliOptions.skipGetStarted) {
    //   log(
    //     `👉  Get started with the following commands:\n\n` +
    //       (this.context === process.cwd()
    //         ? ``
    //         : chalk.cyan(` ${chalk.gray('$')} cd ${name}\n`)) +
    //       chalk.cyan(
    //         ` ${chalk.gray('$')} ${
    //           packageManager === 'yarn'
    //             ? 'yarn serve'
    //             : packageManager === 'pnpm'
    //             ? 'pnpm run serve'
    //             : 'npm run serve'
    //         }`
    //       )
    //   );
    // }
    log();
    this.emit('creation', { event: 'done' });

    // if (gitCommitFailed) {
    //   warn(
    //     `Skipped git commit due to missing username and email in git config, or failed to sign commit.\n` +
    //       `You will need to perform the initial commit yourself.\n`
    //   );
    // }

    // generator.printExitLogs();
  }

  run(command, args) {
    if (!args) {
      [command, ...args] = command.split(/\s+/);
    }
    return execa(command, args, { cwd: this.context });
  }

  /**
   * 提示选择且解析预设配置
   */
  async promptAndResolvePreset(answers = null) {
    // prompts
    if (!answers) {
      await clearConsole(true);
      log();
      answers = await inquirer.prompt(this.resolveFinalPrompts());
    }

    // if (answers.packageManager) {
    //   saveOptions({
    //     packageManager: answers.packageManager
    //   });
    // }

    // let preset;
    // if (answers.preset && answers.preset !== '__manual__') {
    //   preset = await this.resolvePreset(answers.preset);
    // } else {
    //   // manual
    //   preset = {
    //     useConfigFiles: answers.useConfigFiles === 'files',
    //     plugins: {}
    //   };
    //   answers.features = answers.features || [];
    //   // run cb registered by prompt modules to finalize the preset
    //   this.promptCompleteCbs.forEach(cb => cb(answers, preset));
    // }

    const preset = {};
    // 用来放置相关插件
    preset.plugins = {};

    this.promptCompleteCbs.forEach(cb => cb(answers, preset));

    return preset;

    // // prompt
    // if (!answers) {
    //   await clearConsole(true);
    //   answers = await inquirer.prompt(this.resolveFinalPrompts());
    // }
    // debug('vue-cli:answers')(answers);

    // if (answers.packageManager) {
    //   saveOptions({
    //     packageManager: answers.packageManager
    //   });
    // }

    // let preset;
    // if (answers.preset && answers.preset !== '__manual__') {
    //   preset = await this.resolvePreset(answers.preset);
    // } else {
    //   // manual
    //   preset = {
    //     useConfigFiles: answers.useConfigFiles === 'files',
    //     plugins: {}
    //   };
    //   answers.features = answers.features || [];
    //   // run cb registered by prompt modules to finalize the preset
    //   this.promptCompleteCbs.forEach(cb => cb(answers, preset));
    // }

    // // validate
    // validatePreset(preset);

    // // save preset
    // if (
    //   answers.save &&
    //   answers.saveName &&
    //   savePreset(answers.saveName, preset)
    // ) {
    //   log();
    //   log(
    //     `🎉  Preset ${chalk.yellow(answers.saveName)} saved in ${chalk.yellow(
    //       rcPath
    //     )}`
    //   );
    // }

    // debug('vue-cli:preset')(preset);
    // return preset;
  }

  // async resolvePreset(name, clone) {
  //   let preset;
  //   const savedPresets = this.getPresets();

  //   if (name in savedPresets) {
  //     preset = savedPresets[name];
  //   } else if (name === 'default') {
  //     preset = savedPresets['Default (Vue 3)'];
  //   } else if (
  //     name.endsWith('.json') ||
  //     /^\./.test(name) ||
  //     path.isAbsolute(name)
  //   ) {
  //     preset = await loadLocalPreset(path.resolve(name));
  //   } else if (name.includes('/')) {
  //     log(`Fetching remote preset ${chalk.cyan(name)}...`);
  //     this.emit('creation', { event: 'fetch-remote-preset' });
  //     try {
  //       preset = await loadRemotePreset(name, clone);
  //     } catch (e) {
  //       error(`Failed fetching remote preset ${chalk.cyan(name)}:`);
  //       throw e;
  //     }
  //   }

  //   if (!preset) {
  //     error(`preset "${name}" not found.`);
  //     const presets = Object.keys(savedPresets);
  //     if (presets.length) {
  //       log();
  //       log(`available presets:\n${presets.join(`\n`)}`);
  //     } else {
  //       log(`you don't seem to have any saved preset.`);
  //       log(`run vue-cli in manual mode to create a preset.`);
  //     }
  //     exit(1);
  //   }
  //   return preset;
  // }

  // { id: options } => [{ id, apply, options }]
  async resolvePlugins(rawPlugins, pkg) {
    // 模拟
    // rawPlugins['@kt/foo'] = {};

    // ensure cli-service is invoked first，确保 @kt/unity-pc-cli-service 在第一位，因为这个插件是一个基础
    rawPlugins = sortObject(rawPlugins, ['@kt/unity-pc-cli-service'], true);

    const plugins = [];
    for (const id of Object.keys(rawPlugins)) {
      // const apply = loadModule(`${id}/generator`, this.context) || (() => {});
      let apply;
      try {
        apply = require(`${id}/generator`);
      } catch (error) {
        apply = () => {};
      }

      const options = rawPlugins[id] || {};
      // if (options.prompts) {
      //   let pluginPrompts = loadModule(`${id}/prompts`, this.context);

      //   if (pluginPrompts) {
      //     const prompt = inquirer.createPromptModule();

      //     if (typeof pluginPrompts === 'function') {
      //       pluginPrompts = pluginPrompts(pkg, prompt);
      //     }
      //     if (typeof pluginPrompts.getPrompts === 'function') {
      //       pluginPrompts = pluginPrompts.getPrompts(pkg, prompt);
      //     }

      //     log();
      //     log(`${chalk.cyan(options._isPreset ? `Preset options:` : id)}`);
      //     options = await prompt(pluginPrompts);
      //   }
      // }
      plugins.push({ id, apply, options });
    }
    return plugins;
  }

  // getPresets() {
  //   const savedOptions = loadOptions();
  //   return Object.assign({}, savedOptions.presets, defaults.presets);
  // }

  // resolveIntroPrompts() {
  //   const presets = this.getPresets();
  //   const presetChoices = Object.entries(presets).map(([name, preset]) => {
  //     let displayName = name;
  //     // Vue version will be showed as features anyway,
  //     // so we shouldn't display it twice.
  //     if (name === 'Default (Vue 2)' || name === 'Default (Vue 3)') {
  //       displayName = 'Default';
  //     }

  //     return {
  //       name: `${displayName} (${formatFeatures(preset)})`,
  //       value: name
  //     };
  //   });
  //   const presetPrompt = {
  //     name: 'preset',
  //     type: 'list',
  //     message: `Please pick a preset:`,
  //     choices: [
  //       ...presetChoices,
  //       {
  //         name: 'Manually select features',
  //         value: '__manual__'
  //       }
  //     ]
  //   };
  //   const featurePrompt = {
  //     name: 'features',
  //     when: isManualMode,
  //     type: 'checkbox',
  //     message: 'Check the features needed for your project:',
  //     choices: [],
  //     pageSize: 10
  //   };
  //   return {
  //     presetPrompt,
  //     featurePrompt
  //   };
  // }

  /**
   * 结尾 inquire
   */
  // resolveOutroPrompts() {
  //   const outroPrompts = [
  //     {
  //       name: 'useConfigFiles',
  //       when: isManualMode,
  //       type: 'list',
  //       message: 'Where do you prefer placing config for Babel, ESLint, etc.?',
  //       choices: [
  //         {
  //           name: 'In dedicated config files',
  //           value: 'files'
  //         },
  //         {
  //           name: 'In package.json',
  //           value: 'pkg'
  //         }
  //       ]
  //     },
  //     {
  //       name: 'save',
  //       when: isManualMode,
  //       type: 'confirm',
  //       message: 'Save this as a preset for future projects?',
  //       default: false
  //     },
  //     {
  //       name: 'saveName',
  //       when: answers => answers.save,
  //       type: 'input',
  //       message: 'Save preset as:'
  //     }
  //   ];

  //   // ask for packageManager once
  //   const savedOptions = loadOptions();
  //   if (!savedOptions.packageManager && (hasYarn() || hasPnpm3OrLater())) {
  //     const packageManagerChoices = [];

  //     if (hasYarn()) {
  //       packageManagerChoices.push({
  //         name: 'Use Yarn',
  //         value: 'yarn',
  //         short: 'Yarn'
  //       });
  //     }

  //     if (hasPnpm3OrLater()) {
  //       packageManagerChoices.push({
  //         name: 'Use PNPM',
  //         value: 'pnpm',
  //         short: 'PNPM'
  //       });
  //     }

  //     packageManagerChoices.push({
  //       name: 'Use NPM',
  //       value: 'npm',
  //       short: 'NPM'
  //     });

  //     outroPrompts.push({
  //       name: 'packageManager',
  //       type: 'list',
  //       message:
  //         'Pick the package manager to use when installing dependencies:',
  //       choices: packageManagerChoices
  //     });
  //   }

  //   return outroPrompts;
  // }

  resolveFinalPrompts() {
    // patch generator-injected prompts to only show in manual mode
    this.injectedPrompts.forEach(prompt => {
      const originalWhen = prompt.when || (() => true);
      prompt.when = answers => {
        return originalWhen(answers);
      };
    });

    const prompts = [
      // this.presetPrompt,
      // this.featurePrompt,
      ...this.injectedPrompts
      // ...this.outroPrompts
    ];
    // debug('vue-cli:prompts')(prompts);
    return prompts;
  }

  shouldInitGit(cliOptions) {
    // 先查看有没有安装 git
    if (!hasGit()) {
      return false;
    }
    // // --git
    // if (cliOptions.forceGit) {
    //   return true;
    // }
    // // --no-git
    // if (cliOptions.git === false || cliOptions.git === 'false') {
    //   return false;
    // }

    // default: true unless already in a git repo
    return !hasProjectGit(this.context);
  }
};
