'use strict';

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

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

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

// GLOBAL ARGS
const { dryRun, pipelineId, skipCommitsCheck } = utils.tools.getArgs();

const getBranchDefs = (branch) => {
  if (!branch) {
    branch = 'master';
  }

  const isSnapshot =  (branch === 'develop');
  const isMaster =  (branch === 'master');
  const isSupportSnapshot = (!isMaster && !isSnapshot && branch.indexOf('support/develop') > -1);
  const isSupport = (!isMaster && !isSnapshot && !isSupportSnapshot && branch.indexOf('support/') > -1);
  const isHotfix = (!isMaster && !isSnapshot && !isSupportSnapshot && !isSupport && branch.indexOf('hotfix/') > -1);
  const isNext = (!isMaster && !isSnapshot && !isSupport && !isSupportSnapshot && !isHotfix && branch.indexOf('next/') > -1);

  let valid = true;

  if (!isSnapshot && !isMaster && !isSupportSnapshot && !isSupport && !isHotfix && !isNext) {
    valid = false;
  }

  return {
    isSupport: isSupport,
    isSupportSnapshot: isSupportSnapshot,
    isNext: isNext,
    isSnapshot: isSnapshot,
    isMaster: isMaster,
    isHotfix: isHotfix,
    branch: branch,
    valid: valid
  }
}

const getBranches = module.exports.getBranches = () => {
  let { branch } = utils.tools.getArgs();

  return getBranchDefs(branch);
}



module.exports.getEnvTarget = () => {
  let { envTarget } = utils.tools.getArgs();

  // Initialize composite type
  if (envTarget && typeof (envTarget) === 'boolean') {
    envTarget = null;
  }

  let envTargetGen;

  // when envTarget is provided this take the lead
  if (envTarget) {
    envTargetGen = envTarget;

  // otherwise the envTarget is derived from branches develop = DEV/ master = TST
  } else {
    const branches = getBranches();
    if (branches.isSnapshot || branches.isSupportSnapshotBranch) {
      envTargetGen = 'DEV';
    } else {
      envTargetGen = 'TST';
    }
  }

  return envTargetGen;
}


module.exports.init = (pkg, envTarget) => {
  utils.tools.logBanner('INIT');

  const branches = this.getBranches();

  // checking if at least the branch has been given as a parameter
  if (!branches.branch) {
    utils.tools.logError('ERROR : "branch" NOT PROVIDED as argument !!!');
    throw new Error('BRANCH_NOT_PROVIDED');

  // for envTarget / à la carte remote delivery, only master branch is allowed
  } else {
    if (pkg.remote && pkg.build && pkg.build.envTargetActive && !branches.isMaster) {
      throw new Error('REMOTE_ENVTARGET_NOT_MASTER_BRANCH');
    }
  }

  return Promise.resolve()
    .then(() => {
      return initMessage(pkg, branches, envTarget);
    })

    .catch((e) => {
      throw e;
    })
}


const initMessage = module.exports.initMessage = (pkg, branches, envTarget) => {
  return Promise.resolve()

  // Starting the release flow
  .then(() => {
    // initiating mail buffer
    utils.notification.messageStack.startStack();

    // run start
    console.log('\n\n');
    utils.tools.logTitle('-------------------------------------------------------------------------------');
    utils.tools.logTitle(`Releasing new version of package "${pkg.name}" on branch "${branches.branch}"`);
    utils.tools.logTitle('-------------------------------------------------------------------------------');

    console.log(pkg);

    let launchMessage = ':arrow_forward: :arrow_forward: :arrow_forward: :arrow_forward: :arrow_forward:';

    if (pkg.remote) {
      launchMessage += ` Launching remote *${pkg.name}* release for ENV: *${envTarget}*`;
    } else {
      launchMessage += ` Launching *${pkg.name}* release for branch *${branches.branch}*`;
    }

    return utils.notification.package.sendPackageMessage({
      package: pkg,
      text: launchMessage
    });
  })

  .then(() => {
    if (pipelineId) {
      const launchMessage = `:arrow_forward: :arrow_forward: Pipeline ID : https://gitlab.ecdevops.eu/eui/cc-csdr-root/-/pipelines/${pipelineId}`;

      return utils.notification.package.sendPackageMessage({
        package: pkg,
        text: launchMessage
      });
    }
  })

  .catch((e) => {
    throw e;
  })
}


module.exports.cloneMetadataRepos = (pkg) => {
  utils.tools.logBanner('CLONE METADATA REPOS');

  return Promise.resolve()
  .then(() => {
    // cloning metadata repo as further installation is based on historical builds from the past
    return metadataUtils.common.cloneMetadataRepo();
  })
  // CLONE METADATA LOCKS REPO
  .then(() => {
    if (pkg.build && pkg.build.skipLock) {
      utils.tools.logInfo('Package has lock de-activated...skipping cloning of devops-metadata-locks repository');

    } else {
      return metadataUtils.common.cloneMetadataLocksRepo();
    }
  })
  .catch((e) => {
    throw e;
  })
}


module.exports.cloneMetadataLocksRepos = (pkg) => {
  utils.tools.logBanner('CLONE METADATA LOCKS REPOS');

  return Promise.resolve()
  // CLONE METADATA LOCKS REPO
  .then(() => {
    if (pkg.build && pkg.build.skipLock) {
      utils.tools.logInfo('Package has lock de-activated...skipping cloning of devops-metadata-locks repository');

    } else {
      return metadataUtils.common.cloneMetadataLocksRepo();
    }
  })
  .catch((e) => {
    throw e;
  })
}


const checkBranchValidity = module.exports.checkBranchValidity = (branches) => {
  utils.tools.logTitle('Checking branch validity');

  if (!branches.valid) {
    utils.tools.logInfo('Branch not supported ... stopping : allowed branches are develop-master-next/-support/-hotfix/');
    throw new Error('BRANCH_NOT_SUPPORTED');

  } else {
    utils.tools.logInfo('Branch is valid... processing');
  }
}



module.exports.preReleaseChecks = (pkg) => {
  if (dryRun) {
    return;
  }

  utils.tools.logBanner('PRE-RELEASE checks');

  return Promise.resolve()

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

  // CHECK BRANCH VALIDITY
  .then(() => {
    const branches = this.getBranches();

    checkBranchValidity(branches);
  })

  // CHECK eUI VERSION OF PKG VALIDITY (no v10 allowed after 5/04)
  // .then(() => {
  //   utils.tools.logTitle('Checking supported eUI version');

  //   const euiVersion = configUtils.packages.getPackageEuiVersion(pkg);

  //   if (euiVersion && euiVersion === '10.x') {
  //     throw new Error('VERSION_10_NOT_SUPPORTED');
  //   }
  // })

  // CHECKING PACKAGE LOCK STATE - AVOID SAME PACKAGE RUN issuing race conditions
  .then(() => {
    if (!pkg.backend) {
      utils.tools.logTitle('Checking package lock state');


      if (pkg.build && pkg.build.skipLock) {
        utils.tools.logInfo('lock package de-activated...skipping');

      } else {
        // if package is locked, exception is thrown, check this specific exception handling in the global catch
        if (metadataUtils.packageUtils.isPackageLocked(pkg)) {
          utils.tools.logInfo('Package is locked ==> throwing exception');
          throw 'PACKAGE_LOCKED';

          // if package is not locked, we lock it
        } else {
          return metadataUtils.packageUtils.lockPackage(pkg);
        }
      }
    }
  })

  .catch((e) => {
    throw e;
  })
}


module.exports.commitMetadataChecks = (pkg) => {
  utils.tools.logBanner('COMMIT METADATA CHECKS');

  utils.tools.logTitle('Checking package commit metadata');
  let pkgMetadata;

  if (skipCommitsCheck) {
    utils.tools.logInfo('Commits checks skip active...skippping');
    return {};
  }

  if (pkg.remote) {
    utils.tools.logWarning('Remote package...skipping');
    return {};
  }

  return Promise.resolve()
  // GENERATE COMMIT METADATA
  .then(() => {
    return metadataUtils.commit.getMetadata(pkg);
  })

  // CHECK COMMIT GATES
  .then((metadata) => {
    // storing for later exports
    pkgMetadata = metadata;

    // check commit gates, exception is thrown if invalid
    return metadataUtils.commit.checkCommitsMetadata(metadata);
  })

  .then(() => {
    return pkgMetadata;
  })

  .catch((e) => {
    throw e;
  })
}


module.exports.preBuild = (pkg) => {
  utils.tools.logBanner('PRE-BUILD');

  return Promise.resolve()
  .then(() => {
    return utils.buildPackage.preBuild(pkg);
  })

  .catch((e) => {
    throw e;
  })
}




module.exports.updateVersion = (pkg, pkgMetadata, envTarget) => {
  utils.tools.logBanner('UPDATE VERSION');

  // getting branch flags
  const branches = this.getBranches();

  // local vars
  let newVersion;

  let metadataCommits;

  if (pkgMetadata) {
    metadataCommits = pkgMetadata.commits;
  }

  return Promise.resolve()
    // GET NEW VERSION from VALID commits found in metadata
    .then(() => {
      return versionUtils.package.getNewVersion(
        pkg,
        metadataCommits,
        (branches.isSnapshot || branches.isSupportSnapshot),
        branches.isNext,
        branches.isSupport,
        branches.isHotfix,
        envTarget
      );
    })


    // UPDATE PACKAGE VERSION with the new generated one
    .then((version) => {
      newVersion = version;

      return versionUtils.package.updateVersion(
        pkg,
        version,
        (branches.isSnapshot || branches.isSupportSnapshot),
        branches.isNext,
        branches.isSupport,
        branches.isHotfix,
        envTarget
      );
    })

    // returning new version generated
    .then(() => {
      return newVersion;
    })

    .catch((e) => {
      throw e;
    })

}



module.exports.generateChangelog = (pkg, version, pkgMetadata) => {
  utils.tools.logBanner('GENERATE CHANGELOG');

  return Promise.resolve()

  // GENERATE CHANGELOG
  .then(() => {
    utils.tools.logTitle('generating changelog...');

    return utils.changelog.generate(pkg, version, pkgMetadata.commits);
  })


  // WRITE CHANGELOG for MASTER and SUPPORT
  .then((changelogContent) => {
    utils.tools.logTitle('writing changelog...');

    // getting branch flags
    const branches = this.getBranches();

    if ((branches.isSupport || branches.isNext || branches.isMaster) && !dryRun) {
      return utils.changelog.writeChangelog(changelogContent, pkg.paths.root);

    } else {
      utils.tools.logInfo('WRITING SKIPPED - (either DRY-RUN or snapshot release) - Actual changelog :');
      console.log(changelogContent);
    }
  })

  .catch((e) => {
    throw e;
  })
}



module.exports.postBuild = (pkg, version) => {
  utils.tools.logBanner('POST BUILD');

  return Promise.resolve()
  // all packages
  .then(() => {
    return utils.buildPackage.postBuild(pkg);
  })


  // Element specific
  .then(() => {
    if (pkg.element) {
      return utils.buildPackage.element.postBuild(pkg, version);
    }
  })

  .catch((e) => {
    throw e;
  })
}


module.exports.storeMetadataAssets = (pkg, pkgCompositeDeps) => {
  utils.tools.logBanner('STORE METADATA ASSETS');

  return Promise.resolve()
    .then(() => {
      return metadataUtils.remotePackage.storeMetadataAssets(pkg, pkgCompositeDeps);
    })

    .catch((e) => {
      throw e;
    })
}


const commitPackage = (pkg, version, branches) => {
  // COMMIT and PUSH UPDATED FILES ON PACKAGE REPOSITORY
  return Promise.resolve()
  .then(() => {
    return utils.git.commitAndPush(
      branches.branch,
      `chore(release): update version: ${version} - from CI server [skip ci]`,
      pkg.paths.root,
      pkg.standalone
    );
  })
  .catch((e) => {
    throw e;
  })
}


const tagAndMerge = (pkg, version, branches) => {
  return Promise.resolve()
  // TAG THE NEW RELEASE (MASTER) for MASTER and SUPPORT
  .then(() => {
    if (!branches.isSnapshot && !branches.isSupportSnapshot) {
      return utils.git.tagVersion(
        version, branches.branch,
        `chore(release): tagging ${version} - from CI server [skip ci]`,
        pkg.paths.root
      );
    }
  })

  // MERGE BACK ON DEVELOP for MASTER / LATEST RELEASE
  .then(() => {
    if (branches.isMaster) {
      utils.tools.logInfo('Branch is master merge back to develop');
      return utils.git.mergeMasterToDevelop(pkg, pkg.paths.root, version);
    }
  })

  // MERGE BACK ON support/develop for support release for package that has a support/develop branch active
  .then(() => {
    if (branches.isSupport && pkg.build && pkg.build.supportSnapshotBranch) {
      utils.tools.logInfo('Branch is support / supportSnapshotBranch config detected');
      return utils.git.mergeSupportToSupportDevelop(pkg, pkg.paths.root, branches.branch, pkg.build.supportSnapshotBranch);
    }
  })

  .catch((e) => {
    throw e;
  })
}



module.exports.runGitOperations = (pkg, version) => {
  utils.tools.logBanner('RUN GIT OPERATIONS');

  const branches = this.getBranches();

  console.log(branches);

  // authenticate for standalone pkg
  if (pkg.standalone) {
    utils.git.setRepoUrlStandalone(pkg, pkg.paths.root);
  }

  if (pkg.remote && pkg.build && pkg.build.envTargetActive) {
    utils.tools.logInfo('Remote - with envTargetActive set - commit package only');

    return Promise.resolve()
    .then(() => {
      return commitPackage(pkg, version, branches);
    })
    .catch((e) => {
      throw e;
    })

  } else {
    utils.tools.logInfo('Normal UI package - commit package, tagging and do merge operations if needed');

    return Promise.resolve()
    .then(() => {
      return commitPackage(pkg, version, branches);
    })
    .then(() => {
      return tagAndMerge(pkg, version, branches);
    })
    .catch((e) => {
      throw e;
    })
  }
}



module.exports.commitMetadata = (pkg, version) => {

  let configOptions;

  if (pkg.standalone) {
    configOptions = configUtils.global.getConfigOptionsStandalone();
  } else{
    configOptions = configUtils.global.getConfigOptions();
  }


  return Promise.resolve()
    .then(() => {
      utils.tools.logTitle('Commiting metadata info');

      return utils.git.commitAndPush(
        'master',
        `chore: update metadata for ${pkg.name}-${version}`,
        configOptions.DEVOPS_METADATA_PATH
      );
    })

    .catch((e) => {
      throw e;
    })
}



module.exports.exportPipelineVariables = (pkg) => {
  utils.tools.logBanner('EXPORT PIPELINE VARIABLES');

  let variablesContent = '';

  return Promise.resolve()
  .then(() => {
    utils.tools.logTitle('Exporting pkg remote variables');

    utils.tools.logInfo('Exporting APP_TARGET');

    let appTarget = 'MWP_ONLY';

    if (pkg.build && pkg.build.appTarget) {
      appTarget = pkg.build.appTarget;
    }

    utils.tools.logInfo(`appTarget generated : ${appTarget}`);
    variablesContent += `export APP_TARGET=${appTarget}\n`;

    utils.tools.logInfo(`Exporting COMPOSITE_TYPE : DEFAULT`);
    variablesContent += `export COMPOSITE_TYPE=DEFAULT\n`;

    if (dryRun) {
      utils.tools.logWarning('dryRun...skipping export');
    } else {
      utils.tools.logInfo('Exporting appTarget variable');
      return utils.pipeline.setVariables(process.cwd(), variablesContent);
    }
  })

  .catch((e) => {
    throw e;
  })
}




module.exports.sendSuccessNotification = (pkg, version, pkgMetadata) => {
  utils.tools.logBanner('SEND SUCCESS NOTIFICATION');

  return Promise.resolve()
  .then(() => {
    utils.tools.logTitle('Sending slack success');

    let title, subtitle;
    if (pkg.element) {
      title = '[ELEMENT BUILD SUCCESS & PUBLISHED]';
    } else if (pkg.backend) {
      title = '[BACKEND BUILD SUCCESS & PUBLISHED]';
    } else {
      title = '[PACKAGE BUILD SUCCESS & PUBLISHED]';
    }
    subtitle = 'All commit gates passed with success! - Package published!'

    return utils.notification.package.sendPackageMessage({
      package: pkg,
      version: version,
      metadata: pkgMetadata,
      title: title,
      subtitle: subtitle
    })

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

    .then(() => {
      if (!pkg.backend) {
        return Promise.resolve()
        .then(() => {
          if ((pkg.build && pkg.build.skipLock) || pkg.standalone) {
            utils.tools.logInfo('lock package de-activated...skipping');
          } else {
            return metadataUtils.packageUtils.unlockPackage(pkg);
          }
        })
        .catch((e) => {
          utils.tools.logError('ERROR UNLOCKING PACKAGE');
          console.log(e);
        })
      }
    });
  })
}


module.exports.sendErrorNotification = (pkg, exception, pkgMetadata) => {
  utils.tools.logBanner('SEND ERROR NOTIFICATION');

  return Promise.resolve()
  .then(() => {
    utils.tools.logTitle('ERROR !!!!! an unexpected error occured....');
    console.log(exception);

    return utils.notification.package.sendPackageMessage({
      package: pkg,
      exception: exception,
      metadata: pkgMetadata,
      title: '[INVALID RELEASE]',
      subtitle: 'There were some issues publishing your package, check trace file for details :('
    })

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

    .then(() => {
      if (!pkg.backend) {
        // in case of package locked detected, we don't unlock the package
        if (exception !== 'PACKAGE_LOCKED') {
          return Promise.resolve()
          .then(() => {
            if ((pkg.build && pkg.build.skipLock) || pkg.standalone) {
              utils.tools.logInfo('lock package de-activated...skipping');
            } else {
              return metadataUtils.packageUtils.unlockPackage(pkg);
            }
          })
          .catch((e) => {
            utils.tools.logError('ERROR UNLOCKING PACKAGE');
            console.log(e);
          })
        }
      }
    })

    .catch((e) => {
      throw e;
    })
  })

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


module.exports.close = (pkg) => {
  utils.tools.logTitle('Closing mailstack');

  return Promise.resolve()
    .then(() => {
      utils.notification.messageStack.endStack();
      return utils.notification.messageStack.sendPackageMessage(pkg);
    })

    .catch((e) => {
      utils.tools.logError('ERROR closing mailstack');
      console.log(e);
    })
}
