"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.compileTemplate = compileTemplate;
var _handlebars = _interopRequireDefault(require("handlebars"));
var _jsYaml = require("js-yaml");
var _secrets = require("../../secrets");
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0; you may not use this file except in compliance with the Elastic License
 * 2.0.
 */

const handlebars = _handlebars.default.create();
function compileTemplate(variables, templateStr) {
  const {
    vars,
    yamlValues
  } = buildTemplateVariables(variables, templateStr);
  let compiledTemplate;
  try {
    const template = handlebars.compile(templateStr, {
      noEscape: true
    });
    compiledTemplate = template(vars);
  } catch (err) {
    throw new Error(`Error while compiling agent template: ${err.message}`);
  }
  compiledTemplate = replaceRootLevelYamlVariables(yamlValues, compiledTemplate);
  const yamlFromCompiledTemplate = (0, _jsYaml.safeLoad)(compiledTemplate, {});

  // Hack to keep empty string ('') values around in the end yaml because
  // `safeLoad` replaces empty strings with null
  const patchedYamlFromCompiledTemplate = Object.entries(yamlFromCompiledTemplate).reduce((acc, [key, value]) => {
    if (value === null && typeof vars[key] === 'string' && vars[key].trim() === '') {
      acc[key] = '';
    } else {
      acc[key] = value;
    }
    return acc;
  }, {});
  return replaceVariablesInYaml(yamlValues, patchedYamlFromCompiledTemplate);
}
function isValidKey(key) {
  return key !== '__proto__' && key !== 'constructor' && key !== 'prototype';
}
function replaceVariablesInYaml(yamlVariables, yaml) {
  if (Object.keys(yamlVariables).length === 0 || !yaml) {
    return yaml;
  }
  Object.entries(yaml).forEach(([key, value]) => {
    if (typeof value === 'object') {
      yaml[key] = replaceVariablesInYaml(yamlVariables, value);
    }
    if (typeof value === 'string' && value in yamlVariables) {
      yaml[key] = yamlVariables[value];
    }
  });
  return yaml;
}
function buildTemplateVariables(variables, templateStr) {
  const yamlValues = {};
  const vars = Object.entries(variables).reduce((acc, [key, recordEntry]) => {
    // support variables with . like key.patterns
    const keyParts = key.split('.');
    const lastKeyPart = keyParts.pop();
    if (!lastKeyPart || !isValidKey(lastKeyPart)) {
      throw new Error('Invalid key');
    }
    let varPart = acc;
    for (const keyPart of keyParts) {
      if (!isValidKey(keyPart)) {
        throw new Error('Invalid key');
      }
      if (!varPart[keyPart]) {
        varPart[keyPart] = {};
      }
      varPart = varPart[keyPart];
    }
    if (recordEntry.type && recordEntry.type === 'yaml') {
      const yamlKeyPlaceholder = `##${key}##`;
      varPart[lastKeyPart] = recordEntry.value ? `"${yamlKeyPlaceholder}"` : null;
      yamlValues[yamlKeyPlaceholder] = recordEntry.value ? (0, _jsYaml.safeLoad)(recordEntry.value) : null;
    } else if (recordEntry.value && recordEntry.value.isSecretRef) {
      varPart[lastKeyPart] = (0, _secrets.toCompiledSecretRef)(recordEntry.value.id);
    } else {
      varPart[lastKeyPart] = recordEntry.value;
    }
    return acc;
  }, {});
  return {
    vars,
    yamlValues
  };
}
function containsHelper(item, check, options) {
  if ((Array.isArray(check) || typeof check === 'string') && check.includes(item)) {
    if (options && options.fn) {
      return options.fn(this);
    }
    return true;
  }
  return '';
}
handlebars.registerHelper('contains', containsHelper);

// escapeStringHelper will wrap the provided string with single quotes.
// Single quoted strings in yaml need to escape single quotes by doubling them
// and to respect any incoming newline we also need to double them, otherwise
// they will be replaced with a space.
function escapeStringHelper(str) {
  return "'" + str.replace(/\'/g, "''").replace(/\n/g, '\n\n') + "'";
}
handlebars.registerHelper('escape_string', escapeStringHelper);

// toJsonHelper will convert any object to a Json string.
function toJsonHelper(value) {
  if (typeof value === 'string') {
    // if we get a string we assume is an already serialized json
    return value;
  }
  return JSON.stringify(value);
}
handlebars.registerHelper('to_json', toJsonHelper);
function replaceRootLevelYamlVariables(yamlVariables, yamlTemplate) {
  if (Object.keys(yamlVariables).length === 0 || !yamlTemplate) {
    return yamlTemplate;
  }
  let patchedTemplate = yamlTemplate;
  Object.entries(yamlVariables).forEach(([key, val]) => {
    patchedTemplate = patchedTemplate.replace(new RegExp(`^"${key}"`, 'gm'), val ? (0, _jsYaml.safeDump)(val) : '');
  });
  return patchedTemplate;
}