'use strict';

const { resolve, join, basename } = require('path');
const os = require('os');
const fse = require('fs-extra');

const ora = require('ora');
const ciEnv = require('ci-info');
const chalk = require('chalk');

const generateNewApp = require('strapi-generate-new');

const hasYarn = require('./has-yarn');
const { runInstall, runApp, initGit } = require('./child-process');
const { getRepoInfo, downloadGithubRepo } = require('./fetch-github');
const logger = require('./logger');
const stopProcess = require('./stop-process');

/**
 * @param  {string} filePath Path to starter.json file
 */
function readStarterJson(filePath, starterUrl) {
  try {
    const data = fse.readFileSync(filePath);
    return JSON.parse(data);
  } catch (err) {
    stopProcess(`Could not find ${chalk.yellow('starter.json')} in ${chalk.yellow(starterUrl)}`);
  }
}

/**
 * @param  {string} rootPath Path to the project directory
 * @param  {string} projectName Name of the project
 */
async function initPackageJson(rootPath, projectName) {
  const packageManager = hasYarn ? 'yarn --cwd' : 'npm run --prefix';

  try {
    await fse.writeJson(
      join(rootPath, 'package.json'),
      {
        name: projectName,
        private: true,
        version: '0.0.0',
        scripts: {
          'develop:backend': `${packageManager} backend develop`,
          'develop:frontend': `wait-on http://localhost:1337/admin && ${packageManager} frontend develop`,
          develop: 'FORCE_COLOR=1 npm-run-all -l -p develop:*',
        },
        devDependencies: {
          'npm-run-all': '4.1.5',
          'wait-on': '5.2.1',
        },
      },
      {
        spaces: 2,
      }
    );
  } catch (err) {
    stopProcess(`Failed to create ${chalk.yellow(`package.json`)} in ${chalk.yellow(rootPath)}`);
  }
}

/**
 * @param  {string} path The directory path for install
 */
async function installWithLogs(path) {
  const installPrefix = chalk.yellow('Installing dependencies:');
  const loader = ora(installPrefix).start();
  const logInstall = (chunk = '') => {
    loader.text = `${installPrefix} ${chunk
      .toString()
      .split('\n')
      .join(' ')}`;
  };

  const runner = runInstall(path);
  runner.stdout.on('data', logInstall);
  runner.stderr.on('data', logInstall);

  await runner;

  loader.stop();
  console.log(`Dependencies installed ${chalk.green('successfully')}.`);
}

/**
 * @param  {object} projectArgs projectName and starterUrl for the project
 * @param  {object} program Commands for generating new application
 */
module.exports = async function buildStarter(projectArgs, program) {
  const { projectName, starterUrl } = projectArgs;

  // Create temporary directory for starter
  const tmpDir = await fse.mkdtemp(join(os.tmpdir(), 'strapi-'));

  // Fetch repo info
  const { full_name } = await getRepoInfo(starterUrl);

  // Download repo inside tmp dir
  await downloadGithubRepo(starterUrl, tmpDir);

  const starterJson = readStarterJson(join(tmpDir, 'starter.json'), starterUrl);

  // Project directory
  const rootPath = resolve(projectName);
  const projectBasename = basename(rootPath);

  try {
    await fse.ensureDir(rootPath);
  } catch (error) {
    stopProcess(`Failed to create ${chalk.yellow(rootPath)}: ${error.message}`);
  }

  // Copy the downloaded frontend folder to the project folder
  const frontendPath = join(rootPath, 'frontend');

  const starterDir = (await fse.pathExists(join(tmpDir, 'starter'))) ? 'starter' : 'frontend';

  try {
    await fse.copy(join(tmpDir, starterDir), frontendPath, {
      overwrite: true,
      recursive: true,
    });
  } catch (error) {
    stopProcess(`Failed to create ${chalk.yellow(frontendPath)}: ${error.message}`);
  }

  // Delete temporary directory
  await fse.remove(tmpDir);

  console.log(`Creating Strapi starter frontend at ${chalk.yellow(frontendPath)}.`);

  // Install frontend dependencies
  console.log(`Installing ${chalk.yellow(full_name)} starter`);

  await installWithLogs(frontendPath);

  const fullUrl = `https://github.com/${full_name}`;
  // Set command options for Strapi app
  const generateStrapiAppOptions = {
    ...program,
    starter: fullUrl,
    template: starterJson.template,
    run: false,
  };

  // Create strapi app using the template
  await generateNewApp(join(rootPath, 'backend'), generateStrapiAppOptions);

  // Setup monorepo
  initPackageJson(rootPath, projectBasename);

  // Add gitignore
  try {
    const gitignore = join(__dirname, '..', 'resources', 'gitignore');
    await fse.copy(gitignore, join(rootPath, '.gitignore'));
  } catch (err) {
    logger.warn(`Failed to create file: ${chalk.yellow('.gitignore')}`);
  }

  await installWithLogs(rootPath);

  if (!ciEnv.isCI) {
    await initGit(rootPath);
  }

  console.log(chalk.green('Starting the app'));
  await runApp(rootPath);
};
