import path from 'path';
import fs from '../fs';
import Linker from '../linker';
import * as git from '../utils/git';
import { PluginInfo } from '../plugin/plugin';
import config from '../config';
import { logger } from '../utils/logger';

// 基于 GIT 的安装实现
export class Git {
  private installPlugins: any = {};

  public async install(plugin: string): Promise<PluginInfo> {
    const pi = new PluginInfo(plugin);
    if (!this.installPlugins[pi.path]) {
      this.installPlugins[pi.path] = this.installPlugin(pi);
    }
    return this.installPlugins[pi.path];
  }

  public async uninstall(plugin: string): Promise<PluginInfo> {
    const pi = new PluginInfo(plugin);
    if (!await pi.isInstalled()) {
      return pi;
    }
    const currentVersion = await pi.getInstalledVersion();
    await fs.deleteDir(pi.getRealPath(currentVersion));
    if (pi.isRangeVersion()) {
      await fs.unlink(pi.path);
    }
    delete this.installPlugins[pi.path];
    return pi;
  }

  public async installPlugin(pi: PluginInfo): Promise<PluginInfo> {
    // 具体版本只要安装过都不需要重新走安装的流程，但一个范围版本可能已经有更新的满足条件的版本了，所以还是会走安装过程，等同于升级
    if (!pi.isRangeVersion() && await pi.isInstalled()) {
      logger.debug(`[${pi.name}] already exist, plugin dir is ${pi.path}`);
      return pi;
    }
    const url = await pi.getRepoUrl();
    if (!url) {
      return Promise.reject(`unknown plugin: ${pi.fullName}`);
    }
    try {
      // step1. 安装插件本身
      await this.enableRepo(pi, url);
      // step2. 链式安装依赖
      await this.enableDeps(pi);
      // step3. 写入安装完成标志
      await pi.done();
    } catch (e) {
      throw `[${pi.name}] ${e}`;
    }
    // TODO 清理掉其它失效的插件
    return pi;
  }

  // 下载某个版本到指定目录
  public static async downloadRepo(url: string, tag: string, toPath: string) {
    console.log(`download to ${toPath} from ${url}`);
    try {
      await fs.deleteDir(toPath);
    } catch {}
    await git.clone(url, tag, toPath);
    const protocolFile = path.join(toPath, config.protocolFileName);
    try {
      await fs.access(protocolFile);
    } catch {
      throw `${config.protocolFileName} not found`;
    }
  }

  // 下载、升级、修复插件
  private async enableRepo(pi: PluginInfo, url: string) {
    const [currentVersion, installVersion] = await Promise.all([
      pi.getInstalledVersion(),
      git.getVersion(url, pi.installVersion),
    ]);
    if (!installVersion) {
      return Promise.reject('no valid version found');
    }
    pi.setRealVersion(installVersion);
    // 版本一致，尝试修正即可
    if (currentVersion === installVersion) {
      return this.fixPlugin(pi);
    }
    return Git.downloadRepo(url, installVersion, pi.getRealPath(installVersion));
  }

  // 指向的具体版本可能并不存在，需要将latest重命名为具体版本
  private async fixPlugin(pi: PluginInfo) {
    if (!pi.realVersion) {
      return;
    }
    const currentPluginPath = pi.getRealPath();
    try {
      const protocolFile = path.join(currentPluginPath, config.protocolFileName);
      await fs.access(protocolFile);
    } catch {
      await fs.rename(pi.path, currentPluginPath);
      await fs.link(currentPluginPath, pi.path);
    }
    return;
  }

  private async enableDeps(pi: PluginInfo) {
    const pluginPath = pi.getRealPath();
    // 链接到插件目录下
    const binPath = path.join(pluginPath, '.bin');
    const protocol = await pi.getProtocol();
    const tasks = protocol.dep.plugin.map(plugin => {
      return this.install(plugin).then(dpi => this.linkPlugin(dpi, binPath));
    });
    return Promise.all(tasks);
  }

  private async linkPlugin(pi: PluginInfo, binPath: string) {
    const linker = new Linker(`${config.execPrefix} ${config.execSubCommand}`);
    const protocol = await pi.getProtocol();
    if (protocol.langRuntime) {
      const commands = protocol.command.getCommands();
      await linker.registerCustom(binPath, commands, pi.name);
    } else {
      await linker.register(binPath, pi.path, pi.name);
    }
  }

}
