package com.baulsupp.oksocial.jjs;

import com.baulsupp.oksocial.commands.ShellCommand;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import okhttp3.OkHttpClient;
import okhttp3.Request;

public class JavascriptApiCommand implements ShellCommand {
  public JavascriptApiCommand() {
  }

  @Override public String name() {
    return "okapi";
  }

  @Override public List<Request> buildRequests(OkHttpClient client,
      Request.Builder requestBuilder, List<String> arguments) throws Exception {
    boolean multiple = false;

    if (arguments.get(0).equals("-m")) {
      multiple = true;
      arguments.remove(0);
    }

    Path script = FileSystems.getDefault().getPath(arguments.remove(0));

    ScriptEngineManager engineManager =
        new ScriptEngineManager();
    ScriptEngine engine =
        engineManager.getEngineByName("nashorn");

    engine.eval("param = Java.type(\"com.baulsupp.oksocial.jjs.OkShell\").readParam;");

    engine.put("client", client);
    engine.put("clientBuilder", client.newBuilder());
    engine.put("requestBuilder", requestBuilder);

    String lines = Files.lines(script, StandardCharsets.UTF_8).skip(1).collect(
        Collectors.joining("\n"));

    if (multiple) {
      // TODO how to do this without engine.eval
      engine.put("a", arguments);
      Object argumentsJs = engine.eval("Java.from(a)");

      engine.put("arguments", argumentsJs);

      Object result = engine.eval(lines);

      return toRequestList(requestBuilder, result);
    } else {
      return arguments.stream().map(item -> {
        engine.put("item", item);
        Object result = eval(engine, lines);

        return toRequest(requestBuilder, result);
      }).collect(Collectors.toList());
    }
  }

  public Object eval(ScriptEngine engine, String script) {
    try {
      return engine.eval(script);
    } catch (ScriptException e) {
      throw Throwables.propagate(e);
    }
  }

  private List<Request> toRequestList(Request.Builder requestBuilder, Object result) {
    if (result instanceof ScriptObjectMirror) {
      ScriptObjectMirror m = (ScriptObjectMirror) result;

      List<Request> list = Lists.newArrayList();

      for (Object o : m.values()) {
        list.add(toRequest(requestBuilder, o));
      }

      return list;
    } else {
      return Arrays.asList(toRequest(requestBuilder, result));
    }
  }

  private Request toRequest(Request.Builder requestBuilder, Object o) {
    if (o instanceof Request) {
      return (Request) o;
    } else if (o instanceof String) {
      return requestBuilder.url((String) o).build();
    } else if (o == null) {
      throw new NullPointerException();
    } else {
      throw new IllegalStateException("unable to use result " + o + " " + o.getClass());
    }
  }
}

@@ -81,16 +81,66 @@
 ommand;%0A
+import com.baulsupp.oksocial.util.UsageException;%0A
 import c
@@ -169,24 +169,24 @@
 Throwables;%0A
-
 import com.g
@@ -1315,27 +1315,28 @@
 ;%0A%0A    e
-ngine.eval(
+val(engine, 
 %22param =
@@ -1883,27 +1883,28 @@
 sult = e
-ngine.eval(
+val(engine, 
 lines);%0A
@@ -2294,24 +2294,24 @@
 al(script);%0A
-
     %7D catch 
@@ -2328,24 +2328,232 @@
 eption e) %7B%0A
+      Throwable cause = e.getCause();%0A      while (cause != null) %7B%0A        if (cause instanceof UsageException) %7B%0A          throw (UsageException) cause;%0A        %7D%0A        cause = cause.getCause();%0A      %7D%0A%0A
       throw 

handle usage exceptions in scripts