package org.bdware.sc.debugger;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.client.SmartContractClient;
import org.bdware.sc.ContractProcess;
import org.bdware.sc.bean.Contract;
import org.bdware.sc.bean.ContractExecType;
import org.bdware.sc.conn.ResultCallback;
import org.bdware.sc.get.GetMessage;
import org.bdware.sc.http.HttpUtil;
import org.bdware.sc.util.FileUtil;
import org.bdware.sc.util.JsonUtil;
import org.zz.gmhelper.SM2KeyPair;

import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

public class DebugMain {
    static Logger LOGGER = LogManager.getLogger(DebugMain.class);

    public static void runWithConf(String configPath) {
        String content = FileUtil.getFileContent(configPath);
        DebugConfig config = JsonUtil.fromJson(content, DebugConfig.class);
        inject(config);
        String keyPairStr = "{\"publicKey\":\"%s\",\"privateKey\":\"%s\"}";
        SM2KeyPair pair = SM2KeyPair.fromJson(String.format(keyPairStr, config.publicKey, config.privateKey));
        String uriFormat = "ws://%s/SCIDE/SCExecutor";
        if (config.killBeforeStart != null && config.killBeforeStart.length() > 0) {
            AtomicInteger counter = new AtomicInteger(0);

            SmartContractClient client = new SmartContractClient(String.format(uriFormat, config.agentAddress), pair) {
                public void onLogin(JsonObject obj) {
                    counter.incrementAndGet();
                }

                public void onKillContractProcess(JsonObject obj) {
                    counter.incrementAndGet();
                }

            };
            client.waitForConnect();
            client.login();
            try {
                for (; counter.get() == 0; )
                    Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            client.sendMsg("{\"action\":\"killContractProcess\",\"name\":\"" + config.killBeforeStart + "\"}");
            try {
                for (; counter.get() == 1; )
                    Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        config.contract.setCreateParam(config.createParam);
        ContractProcess.main(new String[]{"-port=" + config.port, "-cmi=" + config.cmi, "-disablePID"});
        ResultCallback printCallback
                = new ResultCallback() {
            @Override
            public void onResult(String str) {
                LOGGER.debug(str);
            }
        };
        ContractProcess.instance.handler.setDBInfo(wrap("", config.dbPath), printCallback);
        ContractProcess.instance.handler.registerMangerPort(wrap("", Integer.valueOf(config.cPort)), printCallback);

        ContractProcess.instance.handler.setContractBundle(wrap("", config.contract), printCallback);

        String urlFormat = "http://%s/SCIDE/SCManager?action=reconnectPort&owner=%s&port=%d%s";
        String cpHost = "";
        if (config.cpHost != null && config.cpHost.length() > 0)
            cpHost = "&host=" + config.cpHost;
        String url = String.format(urlFormat, config.agentAddress,
                config.publicKey, ContractProcess.instance.server.mainPort.get(), cpHost);
        Map<String, Object> resp = HttpUtil.httpGet(url);

        String data = (String) resp.get("response");
//        LOGGER.info(JsonUtil.toPrettyJson(JsonUtil.parseStringAsJsonObject(data)));
        LOGGER.info("start done!");
    }

    public static void main(String[] args) {
        runWithConf("./debugconf.json");
    }

    private static void inject(DebugConfig config) {
        String urlFormat = ("http://%s/SCIDE/SCManager?action=%s&arg=%s");
        String url = String.format(urlFormat, config.agentAddress, "getAgentConfig", "");
        Map<String, Object> resp = HttpUtil.httpGet(url);
        String data = (String
                ) resp.get("response");
        JsonObject jsonObject = JsonUtil.parseStringAsJsonObject(data);
        config.cmi = jsonObject.get("cmi").getAsString();
        config.dbPath = jsonObject.get("dbPath").getAsString();
        config.cPort = jsonObject.get("cPort").getAsInt();
        config.port = jsonObject.get("port").getAsInt();
        JsonObject ownerAndScript = new JsonObject();
        String arg = "abc&owner=" + config.publicKey + "&script=" + config.ypkPath;
        url = String.format(urlFormat, config.agentAddress, "allocateKeyPair", arg);
        resp = HttpUtil.httpGet(url);
        LOGGER.info(url);
        String contractStr = (String) resp.get("response");
        LOGGER.info("[ContratStr] " + contractStr);
        Contract contract = JsonUtil.fromJson(contractStr, Contract.class);
        config.contract = contract;
        contract.setType(ContractExecType.Sole);
    }

    private static GetMessage wrap(String operation, Object arg) {
        return wrap(operation, JsonUtil.toJson(arg));
    }

    private static GetMessage wrap(String operation, String arg) {
        GetMessage msg = new GetMessage();
        msg.method = operation;
        msg.arg = arg;
        return msg;
    }

    static class DebugConfig {
        String agentAddress;
        public JsonElement createParam;
        String publicKey;
        String privateKey;
        String killBeforeStart;
        String ypkPath;
        String cpHost;
        //AutoAppend
        int port;
        String cmi;
        String dbPath;
        int cPort;
        Contract contract;
    }
}
