import Handlebars from "handlebars";

// This is a mechanism for quickly rendering templates which is Ember aware
// templates are highly compatible with Ember so you don't need to worry about calling "get"
// and discourseComputed properties function, additionally it uses stringParams like Ember does
const RawHandlebars = Handlebars.create();

function buildPath(blk, args) {
  let result = {
    type: "PathExpression",
    data: false,
    depth: blk.path.depth,
    loc: blk.path.loc,
  };

  // Server side precompile doesn't have jquery.extend
  Object.keys(args).forEach(function (a) {
    result[a] = args[a];
  });

  return result;
}

function replaceGet(ast) {
  let visitor = new Handlebars.Visitor();
  visitor.mutating = true;

  visitor.MustacheStatement = function (mustache) {
    if (!(mustache.params.length || mustache.hash)) {
      mustache.params[0] = mustache.path;
      mustache.path = buildPath(mustache, {
        parts: ["get"],
        original: "get",
        strict: true,
        falsy: true,
      });
    }
    return Handlebars.Visitor.prototype.MustacheStatement.call(this, mustache);
  };

  // rewrite `each x as |y|` as each y in x`
  // This allows us to use the same syntax in all templates
  visitor.BlockStatement = function (block) {
    if (block.path.original === "each" && block.params.length === 1) {
      let paramName = block.program.blockParams[0];
      block.params = [
        buildPath(block, { original: paramName }),
        { type: "CommentStatement", value: "in" },
        block.params[0],
      ];
      delete block.program.blockParams;
    }

    return Handlebars.Visitor.prototype.BlockStatement.call(this, block);
  };

  visitor.accept(ast);
}

if (Handlebars.Compiler) {
  RawHandlebars.Compiler = function () {};
  RawHandlebars.Compiler.prototype = Object.create(
    Handlebars.Compiler.prototype
  );
  RawHandlebars.Compiler.prototype.compiler = RawHandlebars.Compiler;

  RawHandlebars.JavaScriptCompiler = function () {};

  RawHandlebars.JavaScriptCompiler.prototype = Object.create(
    Handlebars.JavaScriptCompiler.prototype
  );
  RawHandlebars.JavaScriptCompiler.prototype.compiler =
    RawHandlebars.JavaScriptCompiler;
  RawHandlebars.JavaScriptCompiler.prototype.namespace = "RawHandlebars";

  RawHandlebars.precompile = function (value, asObject) {
    let ast = Handlebars.parse(value);
    replaceGet(ast);

    let options = {
      knownHelpers: {
        get: true,
      },
      data: true,
      stringParams: true,
    };

    asObject = asObject === undefined ? true : asObject;

    let environment = new RawHandlebars.Compiler().compile(ast, options);
    return new RawHandlebars.JavaScriptCompiler().compile(
      environment,
      options,
      undefined,
      asObject
    );
  };

  RawHandlebars.compile = function (string) {
    let ast = Handlebars.parse(string);
    replaceGet(ast);

    // this forces us to rewrite helpers
    let options = { data: true, stringParams: true };
    let environment = new RawHandlebars.Compiler().compile(ast, options);
    let templateSpec = new RawHandlebars.JavaScriptCompiler().compile(
      environment,
      options,
      undefined,
      true
    );

    let t = RawHandlebars.template(templateSpec);
    t.isMethod = false;

    return t;
  };
}

export function template() {
  return RawHandlebars.template.apply(this, arguments);
}

export function precompile() {
  return RawHandlebars.precompile.apply(this, arguments);
}

export function compile() {
  return RawHandlebars.compile.apply(this, arguments);
}

export default RawHandlebars;