'use strict';

var chalk = require('chalk');
var fs = require('fs-extra');
var handlebars = require('handlebars');
var ora = require('ora');
var recursive = require('recursive-readdir');
var path = require('path');
var fetch = require('node-fetch');
var child_process = require('child_process');
var versions = require('./versions.cjs.js');
var util = require('util');
var os = require('os');

function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }

var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
var handlebars__default = /*#__PURE__*/_interopDefaultCompat(handlebars);
var ora__default = /*#__PURE__*/_interopDefaultCompat(ora);
var recursive__default = /*#__PURE__*/_interopDefaultCompat(recursive);
var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
var os__default = /*#__PURE__*/_interopDefaultCompat(os);

const TASK_NAME_MAX_LENGTH = 14;
const TEN_MINUTES_MS = 1e3 * 60 * 10;
const exec = util.promisify(child_process.exec);
class Task {
  static log(name = "") {
    process.stdout.write(`${chalk__default.default.green(name)}
`);
  }
  static error(message = "") {
    process.stdout.write(`
${chalk__default.default.red(message)}

`);
  }
  static section(name) {
    const title = chalk__default.default.green(`${name}:`);
    process.stdout.write(`
 ${title}
`);
  }
  static exit(code = 0) {
    process.exit(code);
  }
  static async forItem(task, item, taskFunc) {
    const paddedTask = chalk__default.default.green(task.padEnd(TASK_NAME_MAX_LENGTH));
    const spinner = ora__default.default({
      prefixText: chalk__default.default.green(`  ${paddedTask}${chalk__default.default.cyan(item)}`),
      spinner: "arc",
      color: "green"
    }).start();
    try {
      await taskFunc();
      spinner.succeed();
    } catch (error) {
      spinner.fail();
      throw error;
    }
  }
}
async function templatingTask(templateDir, destinationDir, context) {
  const files = await recursive__default.default(templateDir).catch((error) => {
    throw new Error(`Failed to read template directory: ${error.message}`);
  });
  for (const file of files) {
    const destinationFile = path.resolve(
      destinationDir,
      path.relative(templateDir, file)
    );
    await fs__default.default.ensureDir(path.dirname(destinationFile));
    if (file.endsWith(".hbs")) {
      await Task.forItem("templating", path.basename(file), async () => {
        const destination = destinationFile.replace(/\.hbs$/, "");
        const template = await fs__default.default.readFile(file);
        const compiled = handlebars__default.default.compile(template.toString());
        const contents = compiled(
          { name: path.basename(destination), ...context },
          {
            helpers: {
              version(name) {
                if (name in versions.packageVersions) {
                  return versions.packageVersions[name];
                }
                throw new Error(`No version available for package ${name}`);
              }
            }
          }
        );
        await fs__default.default.writeFile(destination, contents).catch((error) => {
          throw new Error(
            `Failed to create file: ${destination}: ${error.message}`
          );
        });
      });
    } else {
      await Task.forItem("copying", path.basename(file), async () => {
        await fs__default.default.copyFile(file, destinationFile).catch((error) => {
          const destination = destinationFile;
          throw new Error(
            `Failed to copy file to ${destination} : ${error.message}`
          );
        });
      });
    }
  }
}
async function checkAppExistsTask(rootDir, name) {
  await Task.forItem("checking", name, async () => {
    const destination = path.resolve(rootDir, name);
    if (await fs__default.default.pathExists(destination)) {
      const existing = chalk__default.default.cyan(destination.replace(`${rootDir}/`, ""));
      throw new Error(
        `A directory with the same name already exists: ${existing}
Please try again with a different app name`
      );
    }
  });
}
async function checkPathExistsTask(path) {
  await Task.forItem("checking", path, async () => {
    try {
      await fs__default.default.mkdirs(path);
    } catch (error) {
      throw new Error(`Failed to create app directory: ${error.message}`);
    }
  });
}
async function buildAppTask(appDir) {
  process.chdir(appDir);
  const runCmd = async (cmd) => {
    await Task.forItem("executing", cmd, async () => {
      await exec(cmd).catch((error) => {
        process.stdout.write(error.stderr);
        process.stdout.write(error.stdout);
        throw new Error(`Could not execute command ${chalk__default.default.cyan(cmd)}`);
      });
    });
  };
  const installTimeout = setTimeout(() => {
    Task.error(
      "\n\u23F1\uFE0F  It's taking a long time to install dependencies, you may want to exit (Ctrl-C) and run 'yarn install' and 'yarn tsc' manually"
    );
  }, TEN_MINUTES_MS);
  await runCmd("yarn install").finally(() => clearTimeout(installTimeout));
  await runCmd("yarn tsc");
}
async function moveAppTask(tempDir, destination, id) {
  await Task.forItem("moving", id, async () => {
    await fs__default.default.move(tempDir, destination).catch((error) => {
      throw new Error(
        `Failed to move app from ${tempDir} to ${destination}: ${error.message}`
      );
    }).finally(() => {
      fs__default.default.removeSync(tempDir);
    });
  });
}
async function readGitConfig() {
  const tempDir = await fs__default.default.mkdtemp(path.resolve(os__default.default.tmpdir(), "git-temp-dir-"));
  try {
    await exec("git init", { cwd: tempDir });
    await exec('git commit --allow-empty -m "Initial commit"', {
      cwd: tempDir
    });
    const getDefaultBranch = await exec(
      'git branch --format="%(refname:short)"',
      { cwd: tempDir }
    );
    return {
      defaultBranch: getDefaultBranch.stdout?.trim() || void 0
    };
  } catch (error) {
    return void 0;
  } finally {
    await fs__default.default.rm(tempDir, { recursive: true });
  }
}
async function tryInitGitRepository(dir) {
  try {
    await exec("git rev-parse --is-inside-work-tree", { cwd: dir });
    return false;
  } catch {
  }
  try {
    await exec("git init", { cwd: dir });
    await exec("git add .", { cwd: dir });
    await exec('git commit -m "Initial commit"', { cwd: dir });
    return true;
  } catch (error) {
    try {
      await fs__default.default.rm(path.resolve(dir, ".git"), { recursive: true, force: true });
    } catch {
      throw new Error("Failed to remove .git folder");
    }
    return false;
  }
}
async function fetchYarnLockSeedTask(dir) {
  try {
    await Task.forItem("fetching", "yarn.lock seed", async () => {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 3e3);
      const res = await fetch__default.default(
        "https://raw.githubusercontent.com/backstage/backstage/master/packages/create-app/seed-yarn.lock",
        {
          signal: controller.signal
        }
      );
      clearTimeout(timeout);
      if (!res.ok) {
        throw new Error(
          `Request failed with status ${res.status} ${res.statusText}`
        );
      }
      const initialYarnLockContent = await res.text();
      await fs__default.default.writeFile(
        path.resolve(dir, "yarn.lock"),
        initialYarnLockContent.split("\n").filter((l) => !l.startsWith("//")).join("\n"),
        "utf8"
      );
    });
    return true;
  } catch {
    return false;
  }
}

exports.Task = Task;
exports.buildAppTask = buildAppTask;
exports.checkAppExistsTask = checkAppExistsTask;
exports.checkPathExistsTask = checkPathExistsTask;
exports.fetchYarnLockSeedTask = fetchYarnLockSeedTask;
exports.moveAppTask = moveAppTask;
exports.readGitConfig = readGitConfig;
exports.templatingTask = templatingTask;
exports.tryInitGitRepository = tryInitGitRepository;
//# sourceMappingURL=tasks.cjs.js.map
