package org.bdware.sc.engine.hook;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bdware.doip.codec.JsonDoipMessage;
import org.bdware.doip.codec.doipMessage.DoipMessage;
import org.bdware.doip.codec.doipMessage.MessageCredential;
import org.bdware.doip.codec.operations.BasicOperations;
import org.bdware.sc.JSEngine;
import org.bdware.sc.bean.ContractRequest;
import org.bdware.sc.boundry.ScriptReturnException;
import org.bdware.sc.entity.DoipMessagePacker;
import org.bdware.sc.node.AnnotationHook;
import org.bdware.sc.node.ArgPacks;
import org.bdware.sc.util.JsonUtil;

public class DOOPBeforeExecHandler implements AnnotationHook {

    private final BasicOperations httpOperation;
    private JsonElement httpArgsRules;

    public DOOPBeforeExecHandler(BasicOperations operations) {
        httpOperation = operations;
        httpArgsRules = getRulesForHTTPRequest(operations);
    }

    @Override
    public ArgPacks handle(JSEngine desktopEngine, ArgPacks argPacks) {
        Object arg = argPacks.arg;
        DoipMessagePacker doipMsgPackerArg = new DoipMessagePacker();
        if (arg instanceof DoipMessagePacker) {
            doipMsgPackerArg = (DoipMessagePacker) arg;
        } else {
            // validate http request's params
            ContractRequest httpReq = argPacks.request;
            validateHTTPRequestArgs(httpReq);
            // set doipMsgPackerArg struct's params
            doipMsgPackerArg.setSource("http");
            doipMsgPackerArg.rawDoipMsg = convertHttpRequestToDoipMessage(httpReq);

            if (httpReq.verifySignature()) {
                doipMsgPackerArg.rawDoipMsg.credential =
                        new MessageCredential(httpReq.getRequester(), new byte[0]);
            } else {
                doipMsgPackerArg.rawDoipMsg.credential = null;
            }
        }

        argPacks.arg = doipMsgPackerArg;
        return argPacks;
    }

    public void validateHTTPRequestArgs(ContractRequest httpReq) {
        JsonElement originArgs = httpReq.getArg();
        JsonElement httpArgs = null;
        if (originArgs.isJsonObject())
            httpArgs = originArgs;
        else
            httpArgs = JsonParser.parseString(originArgs.getAsString());
        // get args rules and validate http args
        ArgSchemaVisitor visitor = new ArgSchemaVisitor(httpArgs);
        validateJsonElementRulesByArgSchemaVisitor(httpArgsRules, visitor);
    }

    public static JsonElement getRulesForHTTPRequest(BasicOperations basicOperation) {
        switch (basicOperation) {
            case Hello:
            case Delete:
            case ListOps:
                return JsonParser.parseString("{\"!header\":{\"!identifier\":\"string\"}}");
            case Create:
            case Update:
                return JsonParser.parseString(
                        "{\"!header\":{\"!identifier\":\"string\"}, \"!body\":\"string\"}");
            case Search:
                return JsonParser.parseString(
                        "{\"!header\":{\"!identifier\":\"string\", \"!attributes\":{\"!query\":\"string\", \"!pageNum\":\"int\", \"!pageSize\":\"int\", \"!type\":\"string\"}}}");
            case Retrieve:
                return JsonParser.parseString(
                        "{\"!header\":{\"!identifier\":\"string\", \"attributes\":{\"element\":\"string\", \"includeElementData\":\"boolean\"}}}");
            case Extension:
            case Unknown:
            default:
                return null;
        }
    }

    public DoipMessage convertHttpRequestToDoipMessage(ContractRequest httpReq) {
        JsonElement arg = httpReq.getArg();
        if (!arg.isJsonObject()) {
            arg = JsonParser.parseString(arg.getAsString());
        }
        JsonDoipMessage doipMessage = JsonUtil.fromJson(arg, JsonDoipMessage.class);
        return doipMessage.toRequestDoipMessage();
    }


    public static void validateJsonElementRulesByArgSchemaVisitor(JsonElement jsonElement,
            ArgSchemaVisitor visitor) {
        visitor.visit(jsonElement);
        if (!visitor.getStatus()) {
            JsonObject jo = new JsonObject();
            jo.addProperty("msg", visitor.getException());
            jo.addProperty("code", visitor.errorCode);
            throw new ScriptReturnException(jo);
        }
    }
}
