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.DoipResponseCode;
import org.bdware.doip.codec.operations.BasicOperations;
import org.bdware.sc.JSEngine;
import org.bdware.sc.boundry.ScriptReturnException;
import org.bdware.sc.conn.ByteUtil;
import org.bdware.sc.entity.DoipMessagePacker;
import org.bdware.sc.node.AnnotationHook;
import org.bdware.sc.node.ArgPacks;
import org.bdware.sc.node.FunctionNode;
import org.bdware.sc.util.JsonUtil;

import java.nio.charset.StandardCharsets;

public class DOOPAfterExecHandler implements AnnotationHook {
    private JsonElement jsonResponseRules;

    public DOOPAfterExecHandler(BasicOperations operations) {
        jsonResponseRules = getRulesForJsonResponse(operations);
    }

    @Override
    public ArgPacks handle(JSEngine desktopEngine, ArgPacks argPacks) {
        Object originDoipMsgPacker = argPacks.arg;
        DoipMessage originDoipMsg = null;
        if (originDoipMsgPacker instanceof DoipMessagePacker) {
            DoipMessagePacker doipMessagePacker = (DoipMessagePacker) originDoipMsgPacker;
            originDoipMsg = doipMessagePacker.rawDoipMsg;
            // if http, directly return
            if (doipMessagePacker.source.equals("http")) {
                if (argPacks.ret != null && argPacks.ret instanceof DoipMessage) {
                    argPacks.ret = JsonUtil.parseObject(
                            JsonDoipMessage.fromDoipMessage((DoipMessage) argPacks.ret));
                }
                return argPacks;
            } else {
                // pack
                if (!(argPacks.ret instanceof DoipMessage)) {
                    JsonObject jsonObjectRes = ((JsonElement) argPacks.ret).getAsJsonObject();
                    if (!jsonObjectRes.has("bodyBase64Encoded")
                            || jsonObjectRes.get("bodyBase64Encoded").getAsBoolean() == true) {
                        if (jsonObjectRes.has("body")) {
                            String body = jsonObjectRes.get("body").getAsString();
                            jsonObjectRes.addProperty("body",
                                    ByteUtil.encodeBASE64(body.getBytes(StandardCharsets.UTF_8)));
                        }
                    }
                    // validate json response
                    ArgSchemaVisitor visitor = new ArgSchemaVisitor(jsonObjectRes);
                    validateJsonElementRulesByArgSchemaVisitor(jsonResponseRules, visitor);
                    JsonDoipMessage returnedMessage =
                            JsonUtil.fromJson(jsonObjectRes, JsonDoipMessage.class);
                    argPacks.ret = returnedMessage.toResponseDoipMessage(originDoipMsg);
                }
                return argPacks;
            }
        } else {
            return argPacks;
        }
    }


    public static JsonElement getRulesForJsonResponse(BasicOperations basicOperations) {
        switch (basicOperations) {
            case Hello:
            case Retrieve:
            case Create:
            case Update:
            case Search:
            case ListOps:
            case Delete:
                return JsonParser.parseString(
                        "{\"header\":{\"response\":\"string\",\"attributes\":{}},\"body\":\"string\"}");
            case Extension:
            case Unknown:
            default:
                return null;
        }
    }

    // old convert jsonResponse from argPack's ret to Doip response in doip chain logic
    public DoipMessage convertJsonResponseToDoipMessage(FunctionNode fn, JsonElement jsonResponse,
            DoipMessage msg) {
        JsonObject jsonParams = jsonResponse.getAsJsonObject();
        // validate json response
        ArgSchemaVisitor visitor = new ArgSchemaVisitor(jsonResponse);
        validateJsonElementRulesByArgSchemaVisitor(jsonResponseRules, visitor);

        JsonObject header =
                jsonParams.get("header") != null ? jsonParams.get("header").getAsJsonObject()
                        : null;
        String body = jsonParams.get("body") != null ? jsonParams.get("body").getAsString() : null;

        if (header != null) {
            String headerRespCode =
                    header.get("response") != null ? header.get("response").getAsString() : null;
            if (headerRespCode != null) {
                for (DoipResponseCode responseCode : DoipResponseCode.values()) {
                    if (responseCode.toString().equals(headerRespCode)) {
                        msg.header.parameters.response = responseCode;
                        break;
                    }
                }
            }
        }
        if (body != null) {
            msg.body.encodedData = body.getBytes(StandardCharsets.UTF_8);
        }

        return msg;
    }

    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);
        }
    }
}
