'use strict';

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

// LOCAL
const tools = require('../../utils/tools');

// INNER MODULES
const innerGlobal = require('./global');

module.exports.getCsdrPackages = () => {
  return innerGlobal.getCsdrConfig(false).packages;
};

module.exports.getCsdrPackagesFull = (flattenChildren = true) => {
  const packages = innerGlobal.getCsdrConfig(flattenChildren).packages;
  const packagesKeys = Object.keys(packages);

  let outputPackages = [];
  packagesKeys.forEach((p) => {
    try {
      outputPackages.push(this.getPackage(p, true));
    } catch (e) {
    }
  });
  return outputPackages;
};

const getPackages = (module.exports.getPackages = () => {
  const packages = innerGlobal.getConfig().packages;
  const packagesKeys = Object.keys(packages);

  let outputPackages = [];
  packagesKeys.forEach((p) => {
    try {
      outputPackages.push(this.getPackage(p));
    } catch (e) {
    }
  });
  return outputPackages;
});

module.exports.getPackageByNpmPkg = (npmPkg, fromCsdrConfig) => {
  let packages;

  // getting all config packages
  if (fromCsdrConfig) {
    packages = innerGlobal.getCsdrConfig().packages;

    // get only packages installed config based on super .euirc.json file generated
  } else {
    packages = innerGlobal.getConfig().packages;
  }

  const pkg = Object.keys(packages).filter((p) => {
    const pkg = packages[p];
    return pkg.npmPkg === npmPkg;
  })[0];

  if (pkg) {
    return this.getPackage(pkg, fromCsdrConfig);
  }
};

module.exports.getPackageByRepository = (repository, fromCsdrConfig) => {
  let packages;

  // getting all config packages
  if (fromCsdrConfig) {
    packages = innerGlobal.getCsdrConfig().packages;

    // get only packages installed config based on super .euirc.json file generated
  } else {
    packages = innerGlobal.getConfig().packages;
  }

  const pkg = Object.keys(packages).filter((p) => {
    const pkg = packages[p];
    return pkg.repository === repository;
  })[0];

  if (pkg) {
    return this.getPackage(pkg, fromCsdrConfig);
  }
};

module.exports.isLocalPackage = (npmPkg) => {
  tools.logInfo(`Checking if ${npmPkg} is localled installed`);

  const packagesFound = getPackages().filter((p) => p.npmPkg === npmPkg);

  return packagesFound.length > 0;
};

module.exports.getPackage = (pkgName, fromCsdrConfig, optional) => {
  let packages;

  // getting all config packages
  if (fromCsdrConfig) {
    packages = innerGlobal.getCsdrConfig().packages;

    // get only packages installed config based on super .euirc.json file generated
  } else {
    packages = innerGlobal.getConfig().packages;
  }

  // checking if package has been provided as input param, if not we default to command line root argument
  // if that case package name is f.e. : pkg:build <package-name>
  if (!pkgName) {
    pkgName = tools.getArgs().root;
  }

  // fetch package from config
  let pkg = packages[pkgName];

  if (!pkg && !optional) {
    throw new Error('PACKAGE_NOT_FOUND');
  }

  // optional call should not do anything
  if (!pkg && optional) return;

  // flagging as CSDR package / non-standalone package
  pkg.csdr = true;
  pkg.standlone = false;

  // additional pkg infos
  pkg.paths = this.getPackagePaths(pkg);
  pkg.tsConfigFileName = `${path.parse(pkg.paths.tsConfig).name}.json`;

  // devops file location
  if (pkg.child) {
    pkg.devopsMetadataFile = `${pkg.parentPkg}-build-metadata.json`;
    pkg.devopsVersionsMetadataFile = `${pkg.parentPkg}-versions-metadata.json`;
    pkg.devopsHistoryMetadataFile = `${pkg.parentPkg}-history-metadata.json`;
    pkg.devopsEnvsMetadataFile = `${pkg.parentPkg}-envs-metadata.json`;
  } else {
    pkg.devopsMetadataFile = `${pkg.name}-build-metadata.json`;
    pkg.devopsVersionsMetadataFile = `${pkg.name}-versions-metadata.json`;
    pkg.devopsHistoryMetadataFile = `${pkg.name}-history-metadata.json`;
    pkg.devopsEnvsMetadataFile = `${pkg.name}-envs-metadata.json`;
  }

  // specific to frontend package
  if (!pkg.parent && !pkg.element && !pkg.backend && !pkg.metadata && pkg.npmPkg) {
    pkg.npmPkgScope = pkg.npmPkg.substr(0, pkg.npmPkg.indexOf('/'));
    pkg.npmPkgName = pkg.npmPkg.substr(pkg.npmPkg.indexOf('/') + 1);
  }

  // check notification method
  if (pkg.notification) {
    if (pkg.notification.client === 'MSTEAMS') {
      pkg.msteamsActive = true;
    } else {
      pkg.msteamsActive = false;
    }
  }

  // set local package flag
  if (tools.isDirExists(pkg.paths.root)) {
    pkg.local = true;
  } else {
    pkg.local = false;
  }

  // derive srcRoot structure flag
  if (pkg.local) {
    if (tools.isDirExists(pkg.paths.srcLib)) {
      pkg.srcRootDefault = true;
    } else {
      pkg.srcRootDefault = false;
    }
  }

  return pkg;
};

module.exports.getPackagePaths = (pkg) => {
  const rootDirectory = process.cwd();
  const resolvePath = (relativePath) => path.resolve(rootDirectory, relativePath);

  let packagesBasePath, folder;
  packagesBasePath = 'packages';
  folder = pkg.folder || pkg.name;

  let packagesPath;
  if (pkg.child) {
    packagesPath = path.join(packagesBasePath, pkg.parentPkg, packagesBasePath);
  } else {
    packagesPath = packagesBasePath;
  }

  let publishFolder = '';
  if (pkg.tsPackage !== false) {
    publishFolder = '/dist';
  } else {
    if (pkg.publishFolder) {
      publishFolder = '/' + pkg.publishFolder;
    } else {
      publishFolder = '';
    }
  }

  // Remotes specific, to switch install root dir to the remote folder itself (independent dependencies / angular & eUI version, etc...)
  let repoNodeModules, remoteNodeModules = false;
  const depsBaseFile = path.join(resolvePath(packagesPath + '/' + folder), 'dependencies-base.json');
  const depsCompositeFile = path.join(resolvePath(packagesPath + '/' + folder), 'dependencies-composite.json');
  if (pkg.remote && (tools.isFileExists(depsBaseFile) || tools.isFileExists(depsCompositeFile))) {
    repoNodeModules = resolvePath(packagesPath + '/' + folder + '/node_modules');
    remoteNodeModules = true;
  } else {
    repoNodeModules = resolvePath('node_modules');
  }

  // Central gathering for pkg paths, always refer to this when fetching pkg paths in code, single sources of truth
  const paths = {
    rootDirectory,
    repoNodeModules: repoNodeModules,
    remoteNodeModules: remoteNodeModules,
    packages: resolvePath(packagesBasePath),
    root: resolvePath(packagesPath + '/' + folder),
    fromRoot: packagesPath + '/' + folder,
    dist: resolvePath(packagesPath + '/' + folder + '/dist'),
    publish: resolvePath(packagesPath + '/' + folder + publishFolder),
    src: resolvePath(packagesPath + '/' + folder + '/src'),
    lib: resolvePath(packagesPath + '/' + folder + '/src/lib'),
    srcLib: resolvePath(packagesPath + '/' + folder + '/src/lib'),
    tsConfig: this.getTSConfigPath(resolvePath, packagesPath + '/' + folder),
  };

  return paths;
};

module.exports.getStandalonePackage = () => {
  const basePath = path.join(process.cwd());
  const { pkgName } = tools.getArgs();

  if (!pkgName) {
    throw new Error('pkgName not provided as pipeline argument');
  }

  const packageJsonPath = path.join(basePath, 'package.json');

  if (!tools.isFileExists(packageJsonPath)) {
    throw new Error('PACKAGE_JSON_NOT_FOUND');
  }

  // get package.json information (current version)
  const packageJson = require(packageJsonPath);

  let pkg = {
    csdr: false,
    standalone: true,
    name: pkgName,
    version: packageJson.version,
    paths: {
      root: basePath,
      dist: path.join(basePath, 'dist'),
      publish: path.join(basePath, 'dist'),
      src: path.join(basePath, 'src'),
      lib: path.join(basePath, 'src', 'lib'),
      srcLib: path.join(basePath, 'src', 'lib'),
      tsConfig: path.join(basePath, 'tsconfig.lib.json'),
    }
  };
  pkg.tsConfigFileName = `${path.parse(pkg.paths.tsConfig).name}.json`;

  const pkgCsdrConfig = path.join(basePath, '.csdr-config.json');

  if (tools.isFileExists(pkgCsdrConfig)) {
    const pkgCsdrConfigJson = require(pkgCsdrConfig);
    pkg = {...pkg, ...pkgCsdrConfigJson};
  }

  return pkg;
}

module.exports.getBuildablePackages = () => {
  return this.getPackages().filter((pkg) => {
    if (!pkg.build) {
      return true;
    }
    return pkg.build.exec !== false;
  });
};

module.exports.getRemotePackages = () => {
  return this.getPackages().filter((pkg) => {
    return pkg.remote;
  });
};

module.exports.isRemote = (pkgName) => {
  let packagesConfig;

  if (tools.isDirExists(path.join(process.cwd(), '.config'))) {
    packagesConfig = require(path.join(process.cwd(), '.config', '.euirc-packages.json'));
  } else {
    packagesConfig = require(path.join(process.cwd(), '.csdr', '.euirc-csdr-packages.json'));
  }
  const packageConfig = packagesConfig[pkgName];

  if (packageConfig) {
    return packageConfig.remote || false;
  } else {
    return false;
  }
}

module.exports.getBuildablePackagesWithDeps = () => {
  return this.getDepGraph()
    .overallOrder()
    .filter((pkg) => {
      if (!pkg.build) {
        return true;
      }
      return pkg.build.exec !== false;
    });
};

module.exports.getDepGraph = () => {
  const DepGraph = require('dependency-graph').DepGraph;

  const graph = new DepGraph();
  const packages = this.getPackages();

  packages.forEach((p) => {
    graph.addNode(p.name, p);
  });

  packages.forEach((p) => {
    if (p.deps) {
      p.deps.forEach((dep) => {
        if (graph.hasNode(dep)) {
          graph.addDependency(p.name, dep);
        }
      });
    }
  });

  return graph;
};

const getEuiVersionCore = (pkg) => {
  let version;

  const depsCompositeFile = path.join(pkg.paths.root, 'dependencies-composite.json');

  if (tools.isFileExists(depsCompositeFile)) {
    const deps = tools.getJsonFileContent(depsCompositeFile);
    const euiDepsBase = deps['@eui/deps-base'];

    if (euiDepsBase) {
      let euiVersion = euiDepsBase.split('.')[0];

      if (euiVersion.substr(0, 1) === '^') {
        euiVersion = euiVersion.substr(1);
      }
      version = `${euiVersion}.x`;

      if (version === '7.x') {
        version = 'DEFAULT';
      }
    }
  }

  return version;
};

const getPackageEuiVersion = (module.exports.getPackageEuiVersion = (pkg, log = false) => {
  if (pkg.parent !== true && pkg.tsPackage !== false) {
    let version;

    if (pkg.build && pkg.build.euiVersion) {
      version = pkg.build.euiVersion;
    }

    if (!version) {
      if (pkg.euiVersion) {
        version = pkg.euiVersion;
      }
    }

    if (!version) {
      version = getEuiVersionCore(pkg);
    }

    if (!version && pkg.standalone) {
      version = getEuiVersionFromPackageJson(pkg);
    }

    if (log) {
      if (version) {
        tools.logInfo(`checking -->${pkg.name}<-- => version found : ${version}\n`);
      } else {
        tools.logInfo(`checking -->${pkg.name}<-- => version detection skipped`);
      }
    }

    return version;
  }
});

module.exports.getLocalPackagesEuiVersion = (log = true) => {
  tools.logTitle('getting local packages installed eUI version...');

  const pkgs = getPackages();
  let versionsFound = [];

  pkgs.forEach((p) => {
    const version = getPackageEuiVersion(p, log);

    if (version) {
      versionsFound.push(version);
    }
  });

  versionsFound = tools.removeArrayDuplicates(versionsFound);

  return versionsFound;
};

module.exports.getTSConfigPath = (resolvePath, packagePath) => {
  const tsPath = resolvePath(`${packagePath}/tsconfig.json`);
  const oldPath = resolvePath(`${packagePath}/tsconfig.lib.json`);

  return tools.isFileExists(tsPath) ? tsPath : oldPath;
};

module.exports.getTSConfigFileName = (tsConfigPath) => {
  return tsConfigPath.split('/')[tsConfigPath.split('/').length - 1];
};

module.exports.getPackageScopes = () => {
  const packagesObj = this.getCsdrPackages();
  let packages = Object.keys(packagesObj);
  packages = packages.map((p) => packagesObj[p]).filter((p) => !p.backend && !p.remote && p.npmPkg !== undefined);
  const scopes = packages.map((p) => p.npmPkg.split('/')[0]);

  return tools.removeArrayDuplicates(scopes);
};
