package org.bdware.sc.engine.hook;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.doip.codec.JsonDoipMessage;
import org.bdware.doip.codec.doipMessage.DoipMessage;
import org.bdware.sc.JSEngine;
import org.bdware.sc.boundry.ScriptReturnException;
import org.bdware.sc.node.AnnotationHook;
import org.bdware.sc.node.AnnotationNode;
import org.bdware.sc.node.ArgPacks;
import org.bdware.sc.node.FunctionNode;
import org.bdware.sc.util.JsonUtil;

@YJSAnnotation(name = "ResultSchema")
public class ResultSchemaHandler implements AnnotationHook {
    private AnnotationNode a;
    private static final Logger LOGGER = LogManager.getLogger(ResultSchemaHandler.class);

    public ResultSchemaHandler(AnnotationNode annotationNode) {
        a = annotationNode;
    }

    @Override
    public ArgPacks handle(JSEngine desktopEngine, ArgPacks argPacks) throws ScriptReturnException {
        Object ret = argPacks.ret;
        if (ret != null) {
            JsonElement je = null;
            if (ret instanceof DoipMessage) {
                JsonDoipMessage jo = JsonDoipMessage.fromDoipMessage((DoipMessage) ret);
                je = JsonUtil.parseObjectAsJsonObject(jo);
            } else if (ret instanceof JsonElement) {
                je = (JsonElement) ret;
            }
            if (je == null) {
                JsonObject jo = new JsonObject();
                jo.addProperty("msg", "[Illegal Type] result should not be empty or null");
                jo.addProperty("code", 1004);
                throw new ScriptReturnException(jo);
            }
            ArgSchemaVisitor visitor;
            if (je.isJsonObject())
                visitor = new ArgSchemaVisitor(je.getAsJsonObject());
            else {
                try {
                    JsonElement obj = JsonParser.parseString(je.getAsString());
                    visitor = new ArgSchemaVisitor(obj);
                    // IMPORTANT automatically convert arg type here
                    argPacks.ret = obj;
                } catch (Exception e) {
                    e.printStackTrace();
                    JsonObject jo = new JsonObject();
                    jo.addProperty("msg", "[Illegal Type] result should be JSON");
                    jo.add("errorResult", je);
                    jo.addProperty("code", 1004);
                    throw new ScriptReturnException(jo);
                }
            }
            if (je.toString().isEmpty() && !a.getArgs().get(0).equals("")) {
                JsonObject jo = new JsonObject();
                jo.addProperty("msg", "[Empty result] result should not be empty");
                jo.addProperty("code", 1003);
                throw new ScriptReturnException(jo);
            }
            visitor.visit(JsonParser.parseString(a.getArgs().get(0)));
            if (!visitor.getStatus()) {
                JsonObject jo = new JsonObject();
                jo.addProperty("msg", visitor.getException());
                jo.addProperty("code", visitor.errorCode);
                jo.add("errorResult", je);
                jo.add("resultSchema", JsonParser.parseString(a.getArgs().get(0)));
                throw new ScriptReturnException(jo);
            }
        }
        return argPacks;
    }

    public static ResultSchemaHandler fromAnnotationNode(FunctionNode funNode,
            AnnotationNode annoNode) {
        return new ResultSchemaHandler(annoNode);
    }
}
