const SettingsService = require('../services/settings');
const fs = require('fs');
const path = require('path');
const { merge } = require('lodash');

const {
  BASE_URL,
  BASE_PATH,
  MOUNT_PATH,
  STATIC_URL,
  STATIC_ORIGIN,
} = require('../url');

const { RECAPTCHA_PUBLIC, WEBSOCKET_LIVE_URI } = require('../config');

// Grab TALK_CLIENT_* environment variables.
const TALK_CLIENT = /^TALK_CLIENT_/i;

// TALK_CLIENT_ENV is all the environment keys that are loaded at runtime.
const TALK_CLIENT_ENV = Object.keys(process.env)
  .filter(key => TALK_CLIENT.test(key))
  .reduce(
    (env, key) => {
      env[key] = process.env[key];
      return env;
    },
    {
      TALK_RECAPTCHA_PUBLIC: RECAPTCHA_PUBLIC,
      LIVE_URI: WEBSOCKET_LIVE_URI,
      STATIC_URL,
      STATIC_ORIGIN,
    }
  );

// TEMPLATE_LOCALS stores the static data that is provided as a `text/json` on
// to the client from the template.
const TEMPLATE_LOCALS = {
  BASE_URL,
  BASE_PATH,
  MOUNT_PATH,
  STATIC_URL,
  TALK_CLIENT_ENV,
  data: TALK_CLIENT_ENV,
};

// attachStaticLocals will attach the locals to the response only.
const attachStaticLocals = locals => {
  for (const key in TEMPLATE_LOCALS) {
    const value = TEMPLATE_LOCALS[key];

    merge(locals, { [key]: value });
  }
};

// MANIFESTS are all the manifests accessible by Talk.
const MANIFESTS = ['../dist/manifest.json', '../dist/manifest.embed.json'];

// getManifest will retrieve the manifest files and parse the JSON.
function getManifest() {
  return merge(
    {},
    ...MANIFESTS.map(f =>
      fs.readFileSync(path.resolve(__dirname, f), 'utf8')
    ).map(JSON.parse)
  );
}

/**
 * resolveFactory is a function that can be used in templates to resolve an
 * asset from the manifest. In production, the manifest is cached.
 */
const createResolveFactory = (() => {
  if (process.env.NODE_ENV === 'production') {
    // In production, we should attempt to load the manifest early.
    const manifest = getManifest();

    return () => key => `${STATIC_URL}static/${manifest[key]}`;
  }

  // In dev mode, we are more forgiving and we always load the
  // newest version of the manifest.
  return () => {
    let manifest = {};
    try {
      manifest = getManifest();
    } catch (err) {
      console.warn(err);
    }

    return key =>
      key in manifest ? `${STATIC_URL}static/${manifest[key]}` : '';
  };
})();

module.exports = async (req, res, next) => {
  try {
    // Attach the custom css url and organization name.
    const { customCssUrl, organizationName } = await SettingsService.select(
      'customCssUrl',
      'organizationName'
    );
    res.locals.customCssUrl = customCssUrl;
    res.locals.organizationName = organizationName;
  } catch (err) {
    console.warn(err);
  }

  // Always attach the locals.
  attachStaticLocals(res.locals);

  // Resolve will help resolving paths to static files
  // using the manifest.
  res.locals.resolve = createResolveFactory();

  // Forward the request.
  next();
};

module.exports.attachStaticLocals = attachStaticLocals;
module.exports.TEMPLATE_LOCALS = TEMPLATE_LOCALS;
