package com.hubspot.jinjava.lib.filter;

import com.hubspot.jinjava.doc.annotations.JinjavaDoc;
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
import com.hubspot.jinjava.interpret.InvalidArgumentException;
import com.hubspot.jinjava.interpret.InvalidReason;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
import com.hubspot.jinjava.interpret.TemplateSyntaxException;
import com.hubspot.jinjava.lib.exptest.ExpTest;
import com.hubspot.jinjava.util.ForLoop;
import com.hubspot.jinjava.util.ObjectIterator;
import com.hubspot.jinjava.util.Variable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@JinjavaDoc(
  value = "Filters a sequence of objects by applying a test to an attribute of an object and only selecting the ones with the test succeeding.",
  input = @JinjavaParam(
    value = "sequence",
    type = "sequence",
    desc = "Sequence to test",
    required = true
  ),
  params = {
    @JinjavaParam(
      value = "attr",
      desc = "Attribute to test for and select items that contain it",
      required = true
    ),
    @JinjavaParam(
      value = "exp_test",
      type = "name of expression test",
      defaultValue = "truthy",
      desc = "Specify which expression test to run for making the selection"
    )
  },
  snippets = {
    @JinjavaSnippet(
      desc = "This loop would select any post containing content.post_list_summary_featured_image",
      code = "{% for content in contents|selectattr('post_list_summary_featured_image') %}\n" +
      "    <div class=\"post-item\">Post in listing markup</div>\n" +
      "{% endfor %}"
    )
  }
)
public class SelectAttrFilter implements AdvancedFilter {

  @Override
  public String getName() {
    return "selectattr";
  }

  @Override
  public Object filter(
    Object var,
    JinjavaInterpreter interpreter,
    Object[] args,
    Map<String, Object> kwargs
  ) {
    return applyFilter(var, interpreter, args, kwargs, true);
  }

  protected Object applyFilter(
    Object var,
    JinjavaInterpreter interpreter,
    Object[] args,
    Map<String, Object> kwargs,
    boolean acceptObjects
  ) {
    List<Object> result = new ArrayList<>();

    if (args.length < 1) {
      throw new TemplateSyntaxException(
        interpreter,
        getName(),
        "requires at least 1 argument (attr to filter on)"
      );
    }

    if (args[0] == null) {
      throw new InvalidArgumentException(interpreter, this, InvalidReason.NULL, 0);
    }

    String attr = args[0].toString();

    Object[] expArgs = new String[] {};

    ExpTest expTest = interpreter.getContext().getExpTest("truthy");
    if (args.length > 1) {
      if (args[1] == null) {
        throw new InvalidArgumentException(interpreter, this, InvalidReason.NULL, 1);
      }

      expTest = interpreter.getContext().getExpTest(args[1].toString());
      if (expTest == null) {
        throw new InvalidArgumentException(
          interpreter,
          this,
          InvalidReason.EXPRESSION_TEST,
          1,
          args[1].toString()
        );
      }

      if (args.length > 2) {
        expArgs = Arrays.copyOfRange(args, 2, args.length);
      }
    }

    ForLoop loop = ObjectIterator.getLoop(var);
    while (loop.hasNext()) {
      Object val = loop.next();

      Object attrVal = new Variable(
        interpreter,
        String.format("%s.%s", "placeholder", attr)
      )
      .resolve(val);
      if (acceptObjects == expTest.evaluate(attrVal, interpreter, expArgs)) {
        result.add(val);
      }
    }

    return result;
  }
}

@@ -580,50 +580,8 @@
 or;%0A
-import com.hubspot.jinjava.util.Variable;%0A
 impo
@@ -673,16 +673,63 @@
 til.Map;
+%0Aimport java.util.concurrent.ThreadLocalRandom;
 %0A%0A@Jinja
@@ -3255,388 +3255,800 @@
     
-ForLoop loop = ObjectIterator.getLoop(var);%0A    while (loop.hasNext()) %7B%0A      Object val = loop.next();%0A%0A      Object attrVal = new Variable(%0A        interpreter,%0A        String.format(%22%25s.%25s%22, %22placeholder%22, attr)%0A      )%0A      .resolve(val);%0A      if (acceptObjects == expTest.evaluate(attrVal, interpreter, expArgs)) %7B%0A        result.add(val);%0A      %7D%0A    %7D%0A%0A    return result
+String tempValue = generateTempVariable();%0A    String expression = generateTempVariable(tempValue, attr);%0A    ForLoop loop = ObjectIterator.getLoop(var);%0A    while (loop.hasNext()) %7B%0A      Object val = loop.next();%0A      interpreter.getContext().put(tempValue, val);%0A%0A      Object attrVal = interpreter.resolveELExpression(%0A        expression,%0A        interpreter.getLineNumber()%0A      );%0A%0A      if (acceptObjects == expTest.evaluate(attrVal, interpreter, expArgs)) %7B%0A        result.add(val);%0A      %7D%0A    %7D%0A%0A    return result;%0A  %7D%0A%0A  private String generateTempVariable() %7B%0A    return %22jj_temp_%22 + Math.abs(ThreadLocalRandom.current().nextInt());%0A  %7D%0A%0A  private String generateTempVariable(String tempValue, String expression) %7B%0A    return String.format(%22%25s.%25s%22, tempValue, expression).trim()
 ;%0A  

Evaluate first argument of select_attr as expression