const tmp = require('tmp');
const presets = require('../../config/presets');
// Workers
const branchWorker = require('../branch');
// children
const apis = require('./apis');
const onboarding = require('./onboarding');
const upgrades = require('./upgrades');
const cleanup = require('./cleanup');

module.exports = {
  renovateRepository,
};

async function renovateRepository(repoConfig, token) {
  let config = { ...repoConfig };
  const { logger } = config;
  config.tmpDir = tmp.dirSync({ unsafeCleanup: true });
  config.errors = [];
  config.warnings = [];
  logger.trace({ config }, 'renovateRepository');
  try {
    config = await apis.initApis(config, token);
    config = await apis.mergeRenovateJson(config);
    if (config.enabled === false) {
      logger.debug('repository is disabled');
      await cleanup.pruneStaleBranches(config, []);
      return;
    }
    if (config.isFork && !config.renovateJsonPresent) {
      logger.debug('repository is a fork and not manually configured');
      return;
    }
    if (config.baseBranch) {
      // Renovate should read content and target PRs here
      if (await config.api.branchExists(config.baseBranch)) {
        config.api.setBaseBranch(config.baseBranch);
      } else {
        // Warn and ignore setting (use default branch)
        const message = `The configured baseBranch "${config.baseBranch}" is not present. Ignoring`;
        config.errors.push({
          depName: 'baseBranch',
          message,
        });
        logger.warn(message);
      }
    }
    // Detect package files in default branch if not manually provisioned
    if (config.packageFiles.length === 0) {
      logger.debug('Detecting package files');
      config = await apis.detectPackageFiles(config);
      // If we can't detect any package.json then return
      if (config.packageFiles.length === 0) {
        logger.info('Cannot detect package.json');
        return;
      }
      logger.debug(
        `Detected ${config.packageFiles
          .length} package files: ${config.packageFiles}`
      );
    }
    logger.debug('Resolving package files and content');
    config = await apis.resolvePackageFiles(config);
    logger.trace({ config }, 'post-packageFiles config');
    // TODO: why is this fix needed?!
    config.logger = logger;
    config.repoIsOnboarded = await onboarding.getOnboardingStatus(config);
    if (!config.repoIsOnboarded) {
      config.contentBaseBranch = `${config.branchPrefix}configure`;
      // Remove packageFile list in case they are provisioned in renovate.json
      const packageFiles = config.packageFiles.map(
        packageFile => packageFile.packageFile
      );
      config.packageFiles = [];
      config = await apis.mergeRenovateJson(config, config.contentBaseBranch);
      // Restore previous packageFile list if not provisioned manually
      if (config.packageFiles.length === 0) {
        config.packageFiles = packageFiles;
      }
      if (config.baseBranch) {
        if (await config.api.branchExists(config.baseBranch)) {
          config.contentBaseBranch = config.baseBranch;
        } else {
          const message = `The configured baseBranch "${config.baseBranch}" is not present. Ignoring`;
          config.errors.push({
            depName: 'baseBranch',
            message,
          });
          logger.warn(message);
        }
      }
      config = await apis.resolvePackageFiles(config);
      config = await presets.resolveConfigPresets(config);
      config.logger = logger;
      logger.trace({ config }, 'onboarding config');
    }
    const allUpgrades = await upgrades.determineRepoUpgrades(config);
    const res = await upgrades.branchifyUpgrades(allUpgrades, logger);
    config.errors = config.errors.concat(res.errors);
    config.warnings = config.warnings.concat(res.warnings);
    const branchUpgrades = res.upgrades;
    logger.debug(`Updating ${branchUpgrades.length} branch(es)`);
    logger.trace({ config: branchUpgrades }, 'branchUpgrades');
    let branchList;
    if (config.repoIsOnboarded) {
      for (const branchUpgrade of branchUpgrades) {
        await branchWorker.processBranch(
          branchUpgrade,
          config.errors,
          config.warnings
        );
      }
      branchList = branchUpgrades.map(upgrade => upgrade.branchName);
      logger.debug(`branchList=${branchList}`);
    } else {
      await onboarding.ensurePr(config, branchUpgrades);
      logger.info('"Configure Renovate" PR needs to be closed first');
      branchList = [`${config.branchPrefix}configure`];
    }
    await cleanup.pruneStaleBranches(config, branchList);
  } catch (err) {
    // Swallow this error so that other repositories can be processed
    if (err.message === 'uninitiated') {
      logger.info('Repository is uninitiated - skipping');
    } else {
      logger.error(`Failed to process repository: ${err.message}`);
      logger.debug({ err });
    }
  }
  config.tmpDir.removeCallback();
}