'use strict';

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

const tools = require('../../tools');
const mavenUtils = require('../../maven-utils');
const configUtils = require('../../../csdr/config/config-utils');
const versionUtils = require('../../../csdr/version/version-utils');

const preBuildUtils = require('../../pre-build/pre-build-utils');

const getProjectInfos = () => {
  // getting the project from installed config
  const prj = configUtils.projects.getProject();

  // exit if project can't be found in the local installed config
  if (!prj) {
    tools.logError('ERROR!!! Unable to find project to be used');
    tools.logInfo('usage : eui-scripts build-app <project-name>');
    throw new Error('PROJECT_NOT_FOUND');
  }

  return prj;
};

module.exports.angular = (
  args = {
    project: null,
    envTarget: null,
    isSnapshot: false,
    version: null,
    configEnvTargetIn: null,
  }
) => {
  let {
    skipLint,
    skipTest,
    skipCompile,
    configuration,
    baseHref,
    servePath,
    watch,
    dryRun,
    maxSpaceSize,
    statsJson,
    extraWebpackConfig,
    ci,
    deployUrl,
    sourceMap,
    configEnvTarget,
  } = tools.getArgs();

  let currentProject;

  if (args.project) {
    currentProject = args.project;
  } else {
    currentProject = getProjectInfos();
  }

  const envTarget = args.envTarget;
  const isSnapshot = args.isSnapshot;
  const version = args.version;
  const configEnvTargetIn = args.configEnvTargetIn;

  tools.logTitle(`Building application : ${currentProject.name}...`);

  tools.logInfo('With build params : ');
  console.log(envTarget, isSnapshot, version, configEnvTargetIn);

  const ng = path.resolve(currentProject.paths.nodeModulesPath, '@angular', 'cli', 'bin', 'ng');

  // checking if configEnvTarget is passed as param, if not take the global script param
  // as this angular build method can be called in stand-alone (outside of CSDR pipeline flow)
  if (configEnvTargetIn) {
    configEnvTarget = configEnvTargetIn;
    tools.logInfo(`ConfigEnvTarget known : ${configEnvTarget}`);
  } else {
    if (configEnvTarget && typeof configEnvTarget === 'boolean') {
      configEnvTarget = null;
      tools.logInfo(`ConfigEnvTarget unknown`);

      if (currentProject.build && currentProject.build.csdrFileReplacement) {
        configEnvTarget = envTarget;
        tools.logInfo(`ConfigEnvTarget known (from envTarget): ${configEnvTarget}`);
      }
    }
  }

  // getting param from project config
  if (currentProject.build) {
    if (currentProject.build.skipLint) {
      skipLint = true;
    }
    if (currentProject.build.skipTest) {
      skipTest = true;
    }
    if (currentProject.build.maxSpaceSize) {
      maxSpaceSize = true;
    }
    if (currentProject.build.sourceMap) {
      sourceMap = true;
    }
    // getting baseHref from project config, dynamic or statically defined
    baseHref = configUtils.projects.getBaseHref(currentProject, version, baseHref);
    tools.logInfo(`base-href found : ${baseHref}`);
    // getting servePath from project config, dynamic or statically defined
    servePath = configUtils.projects.getServePath(currentProject, version, servePath);
    tools.logInfo(`serve-path found : ${servePath}`);
  }

  return Promise.resolve()

    .then(() => {
      tools.logTitle(`Building application : ${currentProject.name}...`);
      console.log(currentProject);
    })

    .then(() => {
      return preBuildUtils.projects.preBuild(currentProject, envTarget, true, configEnvTarget);
    })

    .then(() => {
      if (!skipLint) {
        tools.logInfo(`TS Linting application...`);
        let tsLintPath = currentProject.paths.tslintPath;
        let esLintPath = currentProject.paths.eslintPath;

        // TODO: remove TSLint since it's deprecated and should not be used by projects in the future
        if (tsLintPath) {
          tsLintPath = path.join(tsLintPath);
          let tsConfigPath = path.join(currentProject.paths.angularPath, 'tsconfig.app.json');
          tools.logWarning('TSLint is deprecated and will be removed in the future. Consider migrating to ESLint');
          if (!tools.isFileExists(tsConfigPath)) {
            tsConfigPath = path.join(currentProject.paths.angularPath, 'src', 'tsconfig.app.json');
          }
          if (tools.isFileExists(tsConfigPath)) {
            tools.logInfo(`running tslint -c ${tsLintPath} -p ${tsConfigPath}`);
            return tools.runScript(`tslint -c ${tsLintPath} -p ${tsConfigPath}`);
          }
        } else if (esLintPath) {
          // the project path information are located inside the .eslintrc.json
          tools.logInfo(`running ng lint ${currentProject.name}`);
          return tools.runScript(`ng lint ${currentProject.name}`);
        } else {
          tools.logError(`No ESLint or TSLint configuration found for the project`);
        }
      }
    })

    .then(() => {
      if (!skipLint) {
        tools.logSuccess();
      }
    })

    .then(() => {
      if (!skipLint) {
        tools.logSuccess();
      }
      if (!skipTest) {
        tools.logInfo(`Launching application unit testing...`);

        let args;

        if (maxSpaceSize) {
          args = ['--max_old_space_size=8096', ng, 'test', currentProject.name];
        } else {
          args = [ng, 'test', currentProject.name];
        }

        if (watch) {
          args.push(`--watch=true`);
        } else {
          args.push(`--watch=false`);
        }

        args.push('--code-coverage');

        tools.logInfo(`ng test : watching = ${watch || false}`);

        tools.logInfo(`running Angular TEST with args: ${args} on :`);
        console.log(currentProject.paths.angularPath);

        if (!dryRun) {
          return execa('node', args, { cwd: currentProject.paths.angularPath, stdio: 'inherit' });
        }
      }
    })

    .then(() => {
      if (!skipTest) {
        tools.logSuccess();
      }
      tools.logInfo(`Launching application build...`);

      let args;

      if (maxSpaceSize) {
        args = ['--max_old_space_size=8096', ng, 'build', currentProject.name, '--aot'];
      } else {
        args = [ng, 'build', currentProject.name, '--aot'];
      }

      if (statsJson) {
        args.push('--stats-json');
      }

      if (configuration) {
        args.push(`--configuration=${configuration}`);
      } else {
        let isOptimizedBuild = false;
        if (envTarget && (envTarget === 'ACC' || envTarget === 'DLT' || envTarget === 'TRN' || envTarget === 'PPD' || envTarget === 'PROD')) {
          isOptimizedBuild = true;
        }

        if (currentProject.build && currentProject.build.fullOptimizedBuild) {
          isOptimizedBuild = true;
        }

        if (!envTarget && isSnapshot && currentProject.build && currentProject.build.snapshotBuildOptimized) {
          isOptimizedBuild = true;
        }

        if (envTarget && currentProject.build && currentProject.build.devBuildOptimized) {
          isOptimizedBuild = true;
        }

        if (!envTarget && !isSnapshot) {
          isOptimizedBuild = true;
        }

        if (!isOptimizedBuild) {
          if (currentProject.build && currentProject.build.openid === false) {
            args.push(`--configuration=production-no-openid`);
          } else {
            args.push(`--configuration=production`);
          }
        } else {
          if (currentProject.build && currentProject.build.openid === false) {
            args.push(`--configuration=production-optimized-no-openid`);
          } else {
            args.push(`--configuration=production-optimized`);
          }
        }
      }

      if (baseHref) {
        args.push(`--base-href=${baseHref}`);
      }

      if (servePath) {
        args.push(`--serve-path=${servePath}`);
      }

      if (deployUrl) {
        args.push(`--deploy-url=${deployUrl}`);
      }

      if (ci) {
        args.push('--progress=false');
      }

      if (sourceMap) {
        args.push('--source-map');
      }

      const webpackExtraFile = path.join(currentProject.paths.angularPath, 'webpack.extra.js');
      if (tools.isFileExists(webpackExtraFile) && extraWebpackConfig) {
        args.push(`--extra-webpack-config=${webpackExtraFile}`);
      }

      tools.logInfo(`ng build for configuration : ${configuration || 'production'}`);

      tools.logInfo(`running Angular BUILD with args: ${args} on :`);
      console.log(currentProject.paths.angularPath);

      if (!dryRun && !skipCompile) {
        return execa('node', args, { cwd: currentProject.paths.angularPath, stdio: 'inherit' });
      }
    })

    .then(() => {
      const projectVersion = parseInt(configUtils.projects.getProjectEuiVersion(currentProject));

      if (currentProject.build && currentProject.build.skipBrowserOutputPath) {
        if (currentProject.build.esbuild || projectVersion >= 18) {
          tools.copydir(path.join(currentProject.paths.rootPath, 'dist', 'browser'), path.join(currentProject.paths.rootPath, 'dist'));
          tools.remove(path.join(currentProject.paths.rootPath, 'dist', 'browser'));
        }
      }
    })

    .then(() => {
      const desti18ncompiledPath = path.join(currentProject.paths.rootPath, 'dist', 'assets', 'i18n-compiled');
      if (tools.isDirExists(desti18ncompiledPath)) {
        tools.logTitle('Post-build assets i18n-compiled folder content:');
        const i18nfiles = tools.getFiles(desti18ncompiledPath);
        console.log(i18nfiles);
      }
    })

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

module.exports.maven = () => {
  const currentProject = getProjectInfos();

  return Promise.resolve()

    .then(() => {
      return mavenUtils.execute(currentProject.folder, 'clean package');
    })

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

module.exports.postBuild = (project) => {
  const { dryRun } = tools.getArgs();

  tools.logTitle('Executing project post-build script');

  return Promise.resolve()
    .then(() => {
      tools.logInfo('Executing post-build.js script if present in app folder');
      const postbuildScript = path.join(project.folder, 'post-build.js');

      if (!dryRun && tools.isFileExists(postbuildScript)) {
        return tools.runScript(`node ${postbuildScript}`);
      } else {
        tools.logInfo('DRY-RUN OR post-build.js script not found in app root --- SKIPPING');
      }
    })

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


const generateProjectDistributionList = module.exports.generateProjectDistributionList = (project, isSnapshot, envTarget) => {
  let projectVersion;

  return Promise.resolve()
    .then(() => {
      return versionUtils.app.getCurrentVersion(project);
    })

    .then((version) => {
      projectVersion = version;

      tools.logInfo(`Current project version : ${version}`);
      const baseHref = configUtils.projects.getBaseHref(project, version);
      const servePath = configUtils.projects.getServePath(project, version);

      tools.logInfo(`Distribution folder : ${baseHref}`);

      // getting bucketName if provided
      let bucketName;
      if (project.build && project.build.distribution) {
        // PROD bucket is taken as a default
        bucketName = project.build.distribution.bucketName;

        // if we are in snapshot mode and if the project as a specific bucket defined for snapshot / DEV, we take that one
        if (isSnapshot && project.build.distribution.bucketNameSnapshot) {
          bucketName = project.build.distribution.bucketNameSnapshot;
        }

        // if bucket has been defined for specific envTarget
        if (envTarget) {
          if (project.build.distribution.envs) {
            const envBucketName = project.build.distribution.envs[envTarget.toLowerCase()];
            if (envBucketName) {
              bucketName = envBucketName;
            }
          }
        }
      }

      // final output bucket location
      let outputFolder;
      if (bucketName) {
        outputFolder = bucketName + baseHref.substr(0, baseHref.length - 1);
      } else {
        outputFolder = baseHref.substr(0, baseHref.length - 1);
      }

      // returning app distribution folder and bucket info
      return {
        bucketName: bucketName || '',
        folder: outputFolder,
      };
    })

    // adding packages doc information if defined in config
    .then((output) => {
      const finalOutput = output;

      if (!(project.build && project.build.distribution && project.build.distribution.docPackages)) {
        tools.logInfo(`no docPackages entry found in config...skipping`);
        return finalOutput;
      }

      project.build.distribution.docPackages.forEach((docPkg, i) => {
        tools.logInfo(`copying package doc to assets for : ${docPkg.name}...`);

        const nmDocsPath = path.join(process.cwd(), 'node_modules', docPkg.name, 'docs');
        const distDocPkgFolder = 'assets/docs/' + docPkg.folder;
        const destDocPath = path.join(project.paths.angularPath, 'dist', distDocPkgFolder);
        tools.copydir(nmDocsPath, destDocPath);

        finalOutput[`docFolder${i + 1}`] = distDocPkgFolder;
        const projectMajorVersion = projectVersion.split('.')[0];
        finalOutput[`docS3Folder${i + 1}`] = `${output.bucketName}/${docPkg.folder}-${projectMajorVersion}.x`;
      });

      tools.logSuccess();

      return finalOutput;
    })

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



module.exports.generateProjectDistributionFile = (project, isSnapshot, envTarget) => {
  tools.logTitle('Generating distribution file');

  return Promise.resolve()
    .then(() => {
      return generateProjectDistributionList(project, isSnapshot, envTarget);
    })

    // write final distribution file
    .then((distFileJson) => {
      tools.logInfo('Writing distribution file');
      console.log(distFileJson);
      return tools.writeJsonFileSync(path.join(process.cwd(), project.folder, 'distribution.json'), distFileJson);
    })

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