'use strict';

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

// 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 = () => {
  // getting projects from group

  let { root, projectsGroup, euiVersion, branch } = utils.tools.getArgs();

  if (!projectsGroup) {
    projectsGroup = root;
  }

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

  utils.tools.logTitle(`Building project group: ${projectsGroup} - eUI version: ${euiVersion} - branch: ${branch}`);

  if (!projectsGroup || !euiVersion || !branch) {
    throw new Error('ERROR!!!! either projectsGroup or euiVersion or branch have not been provided');
  }

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

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

  // 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 "${projectsGroup}"`);
        utils.tools.logTitle('-------------------------------------------------------------');
      })

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

      // *****************************************************************
      // 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();
      })

      // *****************************************************************
      // GET ALL DEPENDENCIES AND INSTALL ONCE FOR PROJECTS GROUP
      // *****************************************************************
      .then(() => {
        let resolvedDeps = {};

        return (
          Promise.resolve()
            // Getting internal packages deps - project cloned locally
            .then(() => {
              return installUtils.projects.getLocalProjectsDeps();
            })
            .then((deps) => {
              resolvedDeps = { ...resolvedDeps, ...deps };
            })
            // getting internal projects fixed deps if any
            .then(() => {
              return installUtils.projects.getLocalProjectsFixedDeps();
            })
            .then((deps) => {
              resolvedDeps = { ...resolvedDeps, ...deps };
            })

            // Parse dependencies against carrets ones for projects
            .then(() => {
              const fixedDeps = configUtils.global.getConfigOptions().NPM_FIXED_DEPENDENCIES;
              const configDeps = { ...resolvedDeps, ...fixedDeps };
              return installUtils.common.getResolvedCarretDeps(configDeps, isMaster || isSupportBranch, !isMaster && !isSupportBranch);
            })

            // Install dependencies
            .then((deps) => {
              return installUtils.common.installDeps(deps);
            })
        );
      })

      // *****************************************************************
      // RELEASE EACH PROJECTS IN THE GROUP INDIVIDUALLY
      // *****************************************************************
      .then(() => {
        const projects = configUtils.projects.getCsdrProjectsGroupForEuiVersion(projectsGroup, euiVersion);

        return projects.reduce((promise, project) => {
          return promise.then(() => releaseProject(project, isSnapshot, isSupportBranch, isSupportSnapshotBranch, branch));
        }, Promise.resolve());
      })

      .catch((e) => {
        console.log(e);
        process.exit(1);
      })
  );
};



const releaseProject = (projectName, isSnapshot, isSupportBranch, isSupportSnapshotBranch, branch) => {
  const { dryRun } = utils.tools.getArgs();

  // fetch the project config based on group name
  const project = configUtils.projects.getProject(projectName);

  utils.tools.logTitle(`Release group project: ${project.name}`);

  let newVersion;

  return (
    Promise.resolve()
      // sending slack release start msg
      .then(() => {
        // 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 projects group for *${branch}* branch`,
        });
      })

      // get release version
      .then(() => {
        utils.tools.logTitle('Getting app new version...');

        return versionUtils.app.getNewVersion(project, isSnapshot, isSupportBranch);
      })

      // set the new version found for later use
      .then((version) => {
        newVersion = version;
        utils.tools.logSuccess('New version : ' + newVersion);
      })

      // update project version
      .then(() => {
        return versionUtils.app.updatePackageVersion(project, newVersion);
      })

      // update project metadata assets
      .then(() => {
        return metadataUtils.app.storeMetadataAssets(project, newVersion);
      })

      // build angular project
      .then(() => {
        return utils.buildApp.angular({
          project: project,
          isSnapshot: isSnapshot,
          version: newVersion,
        });
      })

      // post-build
      .then(() => {
        return utils.buildApp.postBuild(project);
      })

      // generate distribution for s3
      .then(() => {
        utils.tools.logTitle('Generate distribution list');
        return utils.buildApp.generateProjectDistributionList(project, isSnapshot || isSupportSnapshotBranch);
      })
      .then((distJson) => {
        utils.tools.logInfo('Distribution list for project :');
        console.log(distJson);

        let bucketsDelivered = 0;

        // distributing to s3 buckets
        utils.tools.logTitle('Distributing to S3 buckets');

        let sourcePath, s3Path;

        // checking app main dist folder
        if (distJson.folder) {
          utils.tools.logTitle(`Distributing main dist folder: `);

          sourcePath = project.paths.distPath;
          s3Path = `s3://${distJson.folder}`;
          console.log(`FROM ===> ${sourcePath}`);
          console.log(`  TO ===> ${s3Path}`);

          if (!dryRun) {
            execa.sync('aws', ['s3', 'cp', '--recursive', sourcePath, s3Path]);
          }

          bucketsDelivered++;
        }

        // checking docFolder 1
        if (distJson.docFolder1 && distJson.docS3Folder1) {
          utils.tools.logTitle(`Distributing docFolder 1: `);

          sourcePath = path.join(project.paths.distPath, distJson.docFolder1);
          s3Path = `s3://${distJson.docS3Folder1}`;
          console.log(`FROM ===> ${sourcePath}`);
          console.log(`  TO ===> ${s3Path}`);

          if (!dryRun) {
            execa.sync('aws', ['s3', 'cp', '--recursive', sourcePath, s3Path]);
          }

          bucketsDelivered++;
        }

        // checking docFolder 2
        if (distJson.docFolder2 && distJson.docS3Folder2) {
          utils.tools.logTitle(`Distributing docFolder 2: `);

          sourcePath = path.join(project.paths.distPath, distJson.docFolder2);
          s3Path = `s3://${distJson.docS3Folder2}`;
          console.log(`FROM ===> ${sourcePath}`);
          console.log(`  TO ===> ${s3Path}`);

          if (!dryRun) {
            execa.sync('aws', ['s3', 'cp', '--recursive', sourcePath, s3Path]);
          }

          bucketsDelivered++;
        }

        // checking docFolder 3
        if (distJson.docFolder3 && distJson.docS3Folder3) {
          utils.tools.logTitle(`Distributing docFolder 3: `);

          sourcePath = path.join(project.paths.distPath, distJson.docFolder3);
          s3Path = `s3://${distJson.docS3Folder3}`;
          console.log(`FROM ===> ${sourcePath}`);
          console.log(`  TO ===> ${s3Path}`);

          if (!dryRun) {
            execa.sync('aws', ['s3', 'cp', '--recursive', sourcePath, s3Path]);
          }

          bucketsDelivered++;
        }

        // sending s3 delivery message
        return utils.notification.project.sendProjectMessage({
          project: project,
          text: `:dart: *${project.name}* - *${bucketsDelivered}* S3 buckets published`,
        });
      })

      // commit release message
      .then(() => {
        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);
      })

      // tag release
      .then(() => {
        if (!isSnapshot) {
          return utils.git.tagVersion(newVersion, branch, `chore(release): release v${newVersion} - from CI server`, project.folder);
        }
      })

      // commit back to develop or support/develop
      .then(() => {
        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);
        }
      })

      // send notification 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();
            })
        );
      })

      // error for the project in group
      .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);
            })
        );
      })
  );
};
