'use strict';

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

// LOCAL
const tools = require('../tools');
const notificationUtils = require('../notification/notification-utils');
const configUtils = require('../../csdr/config/config-utils');

// FETCH ARGS
const { dryRun, registry, skipPublish } = tools.getArgs();



const getPkgVersion = (pkg) => {
  const pathPkgJson = path.resolve(pkg.paths.publish, 'package.json');
  delete require.cache[pathPkgJson];
  require(pathPkgJson);
  const { version } = require(pathPkgJson);
  return version;
}

async function runCommandWithOutput(_args, cwdFolder) {
  const { error, stdout, stderr } = await execa('npm', _args, { cwd: cwdFolder });

  return {
    error, stdout, stderr
  };
}

const publishCore = module.exports.publishCore = (pkg, registry, isPublicAccess) => {
  tools.logInfo(`Publishing package "${pkg.name}" from "${pkg.paths.publish}" on ${registry}`);

  let args = ['publish', pkg.paths.publish, '--registry', registry];

  // let command = `npm publish ${pkg.paths.publish} --registry ${registry}`;

  if (isPublicAccess) {
    args.push('--access public');
    // command += ' --access public';
  }

  if (dryRun || skipPublish) {
    tools.logInfo(`DRY-RUN: npm publish command to be executed : ${args}`);

    return notificationUtils.package.sendPackageMessage({
      package: pkg,
      text: `:rocket: *${pkg.name}* - *VERSION* published to ${registry}`
    });

  } else {

    tools.logInfo(`Executing command : ${args}`);

    const version = getPkgVersion(pkg);

    return runCommandWithOutput(args, process.cwd())
    // return execa.shell(command, { stdio: 'inherit' })
      .then((out) => {
        tools.logInfo('NPM output');
        console.log(out.stdout);

        tools.logSuccess('OK => Package release succeeded! version : ' + version);

        return notificationUtils.package.sendPackageMessage({
          package: pkg,
          text: `:rocket: *${pkg.name}* - *${version}* published to ${registry}`
        });

      })

      .then(() => {
        tools.logInfo('Write version properties file');

        const versionFile = path.resolve(pkg.paths.root, '.version.properties');
        tools.writeFileContent(versionFile, version);
      })

      .catch((e) => {
        // continue publication of other sub-packages in the same group (eUI f.e.) if one of the sub-packages
        // have already been published, as npmjs does not allow re-publication of same pkg version
        if (e.stderr !== undefined && e.stderr.indexOf('You cannot publish over the previously published versions') > -1) {
          console.log('PACKAGE ALREADY PUBLISHED');
          return Promise.resolve();

        } else {
          tools.logError(`Error publishing package "${pkg.name}"`);
          console.log(e.stderr);

          return notificationUtils.package.sendPackageMessage({
            package: pkg,
            version: version,
            exception: e,
            title: '[PACKAGE RELEASE FAILED]',
            subtitle: `Error while publishing Package to *${registry}*`
          })
            .then(() => {
              process.exit(1);
            });
        }
      });
  }
}




const publishPackage = (pkg, parentPkg) => {

  let configOptions;

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

  let pkgPublicRegistry;

  if (parentPkg) {
    if (parentPkg.build && parentPkg.build.publicRegistry) {
      pkgPublicRegistry = parentPkg.build.publicRegistry;
    }
  }

  if (pkg.build && pkg.build.publicRegistry) {
    pkgPublicRegistry = pkg.build.publicRegistry;
  }

  return Promise.resolve()
    // publish on default registry set in config and when no public registry set for package
    .then(() => {
      if (configOptions.NPM_REGISTRY_DEFAULT) {
      // to be activated once we're on digit.c registry
      // if (configOptions.NPM_REGISTRY_DEFAULT && !NPM_PKG_PUBLIC_REGISTRY) {
        return publishCore(pkg, configOptions.NPM_REGISTRY_DEFAULT);
      }
    })

    // publish on public registry set on package
    .then(() => {
      if (pkgPublicRegistry) {
        return publishCore(pkg, pkgPublicRegistry, true);
      }
    })


    // if registry has been passed as argument, try to publish on it
    .then(() => {
      if (registry) {
        return publishCore(pkg, registry);
      }
    })


    .then(() => {
      // Publishing to extra second registry if provided shouldn't break the pipeline, error is just logged
      return Promise.resolve()
        .then(() => {
          if (configOptions.NPM_REGISTRY_DEFAULT_2) {
            return publishCore(pkg, configOptions.NPM_REGISTRY_DEFAULT_2);
          }
        })
        .catch((e) => {
          tools.logWarning(`WARNING: an error occured while trying to publish to ${configOptions.NPM_REGISTRY_DEFAULT_2}`);
          console.log(e)
        })
    })

    .then(() => {
      tools.logSuccess(`${pkg.name} ===> OK, published !!`);
    })

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







module.exports.publish = (pkg) => {
  tools.logInfo(`Publishing frontend root package ${pkg.name}`);

  return Promise.resolve()

    // checking package relation
    .then(() => {

      if (!pkg.parent) {
        if (pkg.remote && pkg.virtual) {
          return publishPackage(pkg);

        } else {
          // building normal package without linked children
          let pkgFull;

          if (pkg.standalone) {
            pkgFull = pkg;

          } else {
            pkgFull = configUtils.packages.getPackage(pkg.name);  // TO CHECK
          }

          return publishPackage(pkgFull);
        }

      } else {

        // handling sub-packages
        return Promise.resolve()
          .then(() => {
            var linkedPackages = Object.keys(pkg.packages).map(p => {
              const pkg = configUtils.packages.getPackage(p);
              return pkg;
            });
            const outputPackages = tools.removeArrayDuplicates(linkedPackages, 'name');

            tools.logInfo('found linked packages : ');
            outputPackages.map(p => {
              console.log(p.name);
            });
            return outputPackages;
          })

          .then((linkedPackages) => Promise.resolve().then(() => {
            return linkedPackages.reduce((promise, childPkg) => {
              return promise.then(() => (
                publishPackage(childPkg, pkg)
              )).catch((e) => {
                throw e;
              })
            }, Promise.resolve());
          }))

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

    })
    .then(() => {
      tools.logSuccess();
    })
    .catch((e) => {
      throw e;
    })
}
