'use strict';

// GLOBAL
const path = require('path');

// UTILS
const utils = require('../../../utils');

// CSDR RELATED
const installUtils = require('../../install/install-utils');
const metadataUtils = require('../../metadata/metadata-utils');
const versionUtils = require('../../version/version-utils');
const configUtils = require('../../config/config-utils');

module.exports.run = () => {
  // current projects
  const project = configUtils.projects.getProject();

  // fetching command parameters
  let { branch, envTarget, configEnvTarget } = utils.tools.getArgs();

  // checking branch and envTarget types
  if (branch && typeof branch === 'boolean') {
    branch = null;
  }
  if (envTarget && typeof envTarget === 'boolean') {
    envTarget = null;
  }
  if (configEnvTarget && typeof configEnvTarget === 'boolean') {
    configEnvTarget = null;
  }

  // globals
  if (!branch && !envTarget) {
    utils.tools.logError('ERRROR!!! no branch OR envTarget provided');
    return;
  }

  // Getting branch flags
  let isSupportSnapshotBranch;
  let isSupportBranch;
  let isSnapshot;

  if (branch) {
    isSupportSnapshotBranch = branch !== 'master' && branch.indexOf('support/develop') > -1;
    isSupportBranch = branch !== 'master' && !isSupportSnapshotBranch && branch.indexOf('support/') > -1;
    isSnapshot = branch !== 'master' && !isSupportBranch;
  }

  // Backward compatibiity with Release process v1
  // Classical app repo develop/master branches can still be used
  // the envTarget generated is deducted in that case

  let envTargetGen;
  // when envTarget is provided in process v2 for MWP, this take the lead
  if (envTarget) {
    envTargetGen = envTarget;

    // for traditional app build using develop / master branch
  } else {
    if (isSnapshot || isSupportSnapshotBranch) {
      envTargetGen = 'DEV';
    } else {
      envTargetGen = 'TST';
    }
  }

  // Modules global declarations
  let newVersion, packagesDeps;

  // Starting the release process
  return (
    Promise.resolve()
      .then(() => {
        utils.tools.logVersion();
      })

      // *****************************************************************
      // RELEASE PACKAGE START
      // *****************************************************************
      .then(() => {
        // Starting the release flow
        console.log('\n\n');
        utils.tools.logTitle('-------------------------------------------------------------');
        utils.tools.logTitle(`Releasing new version of application "${project.name}"`);
        utils.tools.logTitle('-------------------------------------------------------------');
        console.log(project);

        // start mailStack
        utils.notification.messageStack.startStack();

        return utils.notification.project.sendProjectMessage({
          project: project,
          text: `:arrow_forward: :arrow_forward: :arrow_forward: :arrow_forward: :arrow_forward: Launching *${project.name}* release for *${envTargetGen}* environment`,
        });
      })

      // *****************************************************************
      // CLONE METADATA REPO
      // *****************************************************************
      .then(() => {
        return metadataUtils.common.cloneMetadataRepo();
      })

      // *****************************************************************
      // GET AND CHECK DEVOPS CONFIG
      // *****************************************************************
      .then(() => {
        // check if global flag is enabled, if not enabled, this will throw a GLOBAL_PIPELINE_DISABLED exception
        return configUtils.global.isPipelineEnabled();
      })

      // *****************************************************************
      // FETCHING CURRENT PACKAGES & INSTALL
      // *****************************************************************
      .then(() => {
        // installing the dependencies by composite definition, it's outputting the app package dependencies declared
        // in the dependencies-composite.json file of the project, remapped with the composite declaration per env.
        return installUtils.buildApp.install(project, envTargetGen);
      })
      // saving current generated deps metadata for build version
      .then((deps) => {
        packagesDeps = deps;
      })

      // *****************************************************************
      // GETTING NEW VERSION
      // develop|DEV => patch release / master|TST => minor release
      // *****************************************************************
      .then(() => {
        utils.tools.logTitle('Getting app new version...');

        return versionUtils.app.getNewVersion(project, isSnapshot || isSupportSnapshotBranch, isSupportBranch, envTarget);
      })
      .then((version) => {
        // set the new version found for later use
        newVersion = version;
        utils.tools.logSuccess('New version : ' + newVersion);
      })

      // *****************************************************************
      // UPDATE APP PACKAGE VERSION
      // *****************************************************************
      .then(() => {
        // only if a envTarget has not been set (process v2)
        // if envTarget based build, the base repo of app in that case is never updated
        if (!envTarget) {
          return versionUtils.app.updatePackageVersion(project, newVersion);
        }
      })

      // *****************************************************************
      // STORE APP METADATA - PACKAGE DEPS / BUILD INFOS
      // *****************************************************************
      .then(() => {
        // storing project build metadata
        return metadataUtils.app.storeMetadata(project, newVersion, packagesDeps, envTargetGen);
      })

      // *****************************************************************
      // STORE APP METADATA - PACKAGE DEPS / ENVIRONMENT RELATED
      // *****************************************************************
      .then(() => {
        return metadataUtils.appEnvs.storeMetadata(project, newVersion, packagesDeps, envTargetGen);
      })

      // *****************************************************************
      // STORE APP METADATA - VERSIONS
      // *****************************************************************
      .then(() => {
        return metadataUtils.appVersions.storeMetadata(project, newVersion, envTargetGen);
      })

      // *****************************************************************
      // GENERATE MAJOR DIFF REPORT
      // *****************************************************************
      .then(() => {
        // process v1 - diff report is generated on MASTER / TST release
        if (!isSnapshot && !isSupportSnapshotBranch && !envTarget) {
          return metadataUtils.appHistory.generateDiffReport(project, newVersion, isSupportBranch);
        }

        // process v2 - diff report is generated on TST and up
        if (envTarget && envTarget !== 'DEV') {
          return metadataUtils.appHistory.generateDiffReport(project, newVersion, isSupportBranch, envTarget);
        }
      })

      // *****************************************************************
      // STORE APP HISTORY METADATA
      // *****************************************************************
      .then((diffMetadata) => {
        if (diffMetadata) {
          return Promise.resolve()
            .then(() => {
              return metadataUtils.appHistory.flattenDiffMetadata(diffMetadata);
            })

            .then((flatDiffMetadata) => {
              return utils.notification.project.sendProjectDiffReportMessage(project, flatDiffMetadata);
            })
        }
      })

      // *****************************************************************
      // STORE APP ASSETS METADATA
      // *****************************************************************
      .then((historyMetadata) => {
        return metadataUtils.app.storeMetadataAssets(project, newVersion, packagesDeps, historyMetadata, envTargetGen);
      })

      // *****************************************************************
      // BUILD ANGULAR APPLICATION
      // *****************************************************************
      .then(() => {
        return utils.buildApp.angular({
          envTarget: envTarget,
          isSnapshot: isSnapshot || isSupportSnapshotBranch,
          version: newVersion,
          configEnvTargetin: configEnvTarget,
        });
      })

      // *****************************************************************
      // BUILD MAVEN ARTIFACT - OPTIONAL
      // *****************************************************************
      .then(() => {
        if (project.build && project.build.maven) {
          return utils.buildApp.maven();
        }
      })

      // *****************************************************************
      // POST BUILD
      // *****************************************************************
      .then(() => {
        return utils.buildApp.postBuild(project);
      })

      // *****************************************************************
      // DISTRIBUTION OPTIONAL STEP to dynamic S3 buckets
      // *****************************************************************
      .then(() => {
        if (project.build && project.build.distribution) {
          return Promise.resolve()
            .then(() => {
              return utils.buildApp.generateProjectDistributionFile(project, isSnapshot || isSupportSnapshotBranch, envTarget);
            })
            .catch((e) => {
              throw e;
            });
        }
      })

      // *****************************************************************
      // COMMIT AND PUSH UPDATED FILES
      // *****************************************************************
      .then(() => {
        // for env-target based build, the app repo is always untouched, as all is metadata driven outside of the app sources
        if (!envTarget) {
          let message;
          if (isSnapshot) {
            message = `chore(pre-release): pre-release v${newVersion} - from CI server`;
          } else {
            message = `chore(release): release v${newVersion} - from CI server`;
          }
          const projectFolder = path.join(process.cwd(), project.folder);
          return utils.git.commitAndPush(branch, message, projectFolder);
        }
      })

      // *****************************************************************
      // RELEASE TAG ON MASTER
      // *****************************************************************
      .then(() => {
        // for env-target based builds, release is not tagged as app repo is not containing sources, neither version info
        if (!envTarget) {
          if (!isSnapshot) {
            return utils.git.tagVersion(newVersion, branch, `chore(release): release v${newVersion} - from CI server`, project.folder);
          }
        }
      })

      // *****************************************************************
      // MERGE BACK ON DEVELOP for MASTER and SUPPORT
      // *****************************************************************
      .then(() => {
        // for env-target based builds, no need to merge anything back as it only contains a master branch for sources
        if (!envTarget) {
          if (!isSnapshot && !isSupportBranch && !isSupportSnapshotBranch) {
            if (project.build && !project.build.masterBranchOnly) {
              return utils.git.mergeMasterToDevelop(project, project.folder);
            }
          }

          if (isSupportBranch && project.build && project.build.supportSnapshotBranch) {
            utils.tools.logInfo('Branch is support / supportSnapshotBranch config detected');
            return utils.git.mergeSupportToSupportDevelop(project, project.folder, branch, project.build.supportSnapshotBranch);
          }
        }
      })

      // *****************************************************************
      // COMMIT AND PUSH METADATA
      // *****************************************************************
      .then(() => {
        const DEVOPS_METADATA_PATH = configUtils.global.getConfigOptions().DEVOPS_METADATA_PATH;
        const message = `chore: update metadata for ${project.name}-${newVersion}`;

        return utils.git.commitAndPush('master', message, DEVOPS_METADATA_PATH);
      })

      // *****************************************************************
      // SEND SLACK SUCCESS
      // *****************************************************************
      .then(() => {
        return (
          utils.notification.project
            .sendProjectMessage({
              project: project,
              version: newVersion,
              title: '[APPLICATION BUILD SUCCESS]',
              subtitle: 'Successful build of the application! :)',
            })

            // store package config in the root folder to send config to gitlab
            .then(() => {
              return utils.notification.config.storeProjectConfig(project);
            })

            .then(() => {
              utils.notification.messageStack.endStack();
            })
        );
      })

      // *****************************************************************
      // SEND SLACK ERROR
      // *****************************************************************
      .catch((e) => {
        utils.tools.logError(`ERROR!!!! - ${e.message}`);
        console.log(e);

        return (
          utils.notification.project
            .sendProjectMessage({
              project: project,
              exception: e,
              title: '[APPLICATION BUILD FAILED]',
              subtitle: 'Error building the App container application! :(',
            })

            // store package config in the root folder to send config to gitlab
            .then(() => {
              return utils.notification.config.storeProjectConfig(project);
            })
            .then(() => {
              utils.notification.messageStack.endStack();
              return utils.notification.messageStack.sendProjectMessage(project);
            })

            .then(() => {
              process.exit(1);
            })
        );
      })
  );
};
