/***
 * Copyright (c) 2016 - 2022 Alex Grant (@localnerve), LocalNerve LLC
 * Copyrights licensed under the BSD License. See the accompanying LICENSE file for terms.
 *
 * Environment variables used:
 *   NODE_ENV
 *   ASSET_HOST
 *   APP_HOSTNAME
 *
 * Environmentally aware project dir, file, and settings.
 * Avoids file system strings all over project.
 *   With this, it is restricted to package.json and dot files.
 * Dir and file paths are completed to a given baseDir, so can change relative
 * point of reference.
 *
 * Creates a settings configuration with these properties:
 *   config.assets - The dir and file (fs) locations of asset files (source).
 *   config.src - The dir and file (fs) locations of source (code) files.
 *   config.output - The dir and file (fs) locations of compiled source code.
 *   config.dist - The dir and file (fs) locations of built assets.
 *     (js, css, images, fonts, json, xml, html, etc...).
 *   config.web - Dir, asset, and settings from the web (client) perspective.
 */
'use strict';

var path = require('path');
var merge = require('lodash/merge');
var assets = require('./assets');
var utils = require('./utils');

var prependPathToObject = utils.prependPathToObject;

var distbase = 'dist';
var srcbase = 'src';
var assetsbase = 'assets';
var publicbase = '/public';

var applicationDir = 'application';

/***
 * Asset source files and directories.
 */
var assetsDirs = {
  images: 'images',
  fonts: 'fonts'
};
var assetsFiles = {
  five00: '500.html',
  five03: '503.html',
  favicon: path.join(assetsDirs.images, 'favicon.ico'),
  robotsTemplate: 'robots.txt',
  appManifest: 'manifest.json',
  browserConfig: 'browserconfig.xml'
};

/***
 * Directories and files that are in both dist and web
 */
var outputDirs = {
  scripts: 'scripts',
  styles: 'styles'
};
var outputFiles = {
  css: {
    inline: path.join(outputDirs.styles, 'inline.css'),
    other: [
      // add other external stylesheets to async load at startup
      path.join(outputDirs.styles, 'settings.css')
    ]
  },
  inlineScript: path.join(outputDirs.scripts, 'inline.js')
};
Object.assign(outputDirs, assetsDirs);
Object.assign(outputFiles, assetsFiles);

/***
 * Source only dirs and files
 */
var srcDirs = {
  application: applicationDir,
  components: path.join(applicationDir, 'components'),
  configs: path.join('node_modules', 'configs'),
  client: path.join(applicationDir, 'client'),
  styles: path.join(applicationDir, 'client', 'styles'),
  tests: 'tests'
};
var srcFiles = {
  assetsJson: path.join(
    srcDirs.configs, path.basename(__dirname), assets.assetsJsonFile
  ),
  assetsRevManifest: path.join(
    srcDirs.configs, path.basename(__dirname), assets.assetsRevManifest
  ),
  inlineScript: path.join(srcDirs.client, 'inline.js'),
  clientEntry: path.join(srcDirs.client, 'index.js'),
  serviceWorker: {
    registration: path.join(srcDirs.client, 'sw-registration.js'),
    precache: path.join(srcDirs.client, 'sw', 'precache.js'),
    data: path.join(srcDirs.client, 'sw', 'node_modules/sw/data.json'),
    entry: path.join(srcDirs.client, 'sw', 'index.js')
  }
};

/**
 * Get settings to override by environment.
 * production override uses ASSET_HOST env variable.
 *
 * @param {String} env - The environment string.
 * @param {String} baseDir - The base directory.
 * @returns {Object|Undefined} The environment specific overrides or undefined.
 */
function overrides (env, baseDir) {
  var envOverrides = {
    production: {
      dist: {
        baseDir: path.join(baseDir, distbase, 'release')
      },
      loggerFormat: 'tiny',
      web: {
        // ssl: true, // for ssl here on this app (this tier)
        sslRemote: true, // for ssl elsewhere (an appliance or CloudFlare, etc)
        assetAge: 31556926000
      }
    }
  };
  return envOverrides[env];
}

/**
 * Make the settings configuration object.
 *
 * @param {Object} nconf - The nconfig object.
 * @returns The settings configuration object.
 */
function makeConfig (nconf) {
  var env = nconf.get('NODE_ENV');

  // Update relative baseDir if defined
  var baseDir = nconf.get('baseDir') || '.';

  /**
   * The exported settings config
   */
  var config = {
    assets: {
      baseDir: path.join(baseDir, assetsbase)
    },
    dist: {
      baseDir: path.join(baseDir, distbase, 'debug')
    },
    src: {
      baseDir: path.join(baseDir, srcbase)
    },
    web: {
      baseDir: publicbase,
      assetAge: 0,
      assetHost:
        nconf.get('ASSET_HOST') || nconf.get('APP_HOSTNAME') || 'localhost',
      ssl: false,
      sslRemote: false,
      robots: '/robots.txt',
      sitemap: '/sitemap.xml',
      appHostname: nconf.get('APP_HOSTNAME') || 'localhost',
      polyfills: ['Object.assign', 'Promise']
    },
    output: {
      baseDir: path.join(baseDir, 'output')
    },

    distbase: path.join(baseDir, distbase),
    vendor: {
      css: path.join('node_modules', 'foundation-apps', 'scss')
    },

    loggerFormat: 'dev'
  };

  //
  // Environment overrides
  merge(config, overrides(env, baseDir));

  // Assemble config.output and config.src
  [config.src, config.output].forEach(function (conf) {
    Object.assign(
      conf,
      prependPathToObject(srcDirs, conf.baseDir),
      prependPathToObject(srcFiles, conf.baseDir)
    );
  });

  // Assemble config.assets
  Object.assign(
    config.assets,
    prependPathToObject(assetsDirs, config.assets.baseDir),
    prependPathToObject(assetsFiles, config.assets.baseDir)
  );

  // Assemble config.dist and config.web
  [config.dist, config.web].forEach(function (conf) {
    Object.assign(
      conf,
      prependPathToObject(outputDirs, conf.baseDir),
      prependPathToObject(outputFiles, conf.baseDir)
    );
  });

  // A little extra web-only config for determining built assets
  config.web.assets = assets.assetsConfig(
    config.web.scripts, config.web.baseDir
  );

  // A helper to expose package.json
  config.pkgInfo = utils.pkgInfo.bind(null, baseDir);

  return config;
}

module.exports = makeConfig;
