package net.northking.cctp.element.internal.keywords;

import com.alibaba.fastjson.JSON;
import net.northking.cctp.element.core.IExecuteContext;
import net.northking.cctp.element.core.annotation.Argument;
import net.northking.cctp.element.core.annotation.Keyword;
import net.northking.cctp.element.core.annotation.Keywords;
import net.northking.cctp.element.core.annotation.Return;
import net.northking.cctp.element.core.exception.ComponentRuntimeException;
import net.northking.cctp.element.core.exception.ExecuteException;
import net.northking.cctp.element.core.type.DataType;
import net.northking.cctp.element.core.type.ParamScope;
import net.northking.cctp.element.internal.protocol.socket.MessageHandler;
import net.northking.cctp.element.internal.protocol.socket.MessageHandlerFactory;
import net.northking.cctp.element.internal.protocol.socket.SocketClient;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * SocketRequest
 *
 * @author fengxiang.zhang
 * @date 2022/4/15
 * @since 1.0
 */
@Keywords
public class SocketRequest {

    private static final Logger logger = LoggerFactory.getLogger(SocketRequest.class);

    /**
     * Socket请求
     *
     * @param ipPort         目标IP和端口
     * @param connectTimeout 连接超时(秒)
     * @param readTimeout    读超时(秒)
     * @param charsetName    字符集
     * @param protocolName   协议名称
     * @param header         请求头
     * @param requestData    请求报文
     * @return 请求响应结果对象
     */
    @Keyword(alias = "Socket请求",attributes = "1")
    @Return(name = "__response", comment = "请求响应结果", type = DataType.OBJECT)
    public Object socketRequest(
            IExecuteContext context,
            @Argument(name = "ipPort", comment = "目标IP和端口", type = DataType.STRING) String ipPort,
            @Argument(name = "connectTimeout", comment = "连接超时(秒)", type = DataType.INTEGER,
                    notNull = false, required = false, defaultValue = "3") Integer connectTimeout,
            @Argument(name = "readTimeout", comment = "读超时(秒)", type = DataType.INTEGER,
                    notNull = false, required = false, defaultValue = "5") Integer readTimeout,
            @Argument(name = "charsetName", comment = "字符集", type = DataType.STRING,
                    notNull = false, required = false, defaultValue = "UTF-8") String charsetName,
            @Argument(name = "protocolName", comment = "协议名称", type = DataType.STRING) String protocolName,
            @Argument(name = "header", comment = "请求头", type = DataType.STRING,
                    notNull = false, required = false,defaultValue="[]") String header,
            @Argument(name = "requestData", comment = "请求报文", type = DataType.STRING) String requestData,
            @Argument(name = "result", comment = "响应结果", type = DataType.STRING, required = false,scope = ParamScope.OUTPUT,
            defaultValue = "{}", notNull = false) String result) throws Exception {

        String[] parts = splitIpPort(ipPort);
        MessageHandler handler = MessageHandlerFactory.getHandler(protocolName, charsetName, header);
        SocketClient client = new SocketClient(parts[0], Integer.parseInt(parts[1]), connectTimeout*1000, readTimeout*1000, handler);
        long start = System.currentTimeMillis();
        client.connect();
        Object toJson = client.send(requestData).time(System.currentTimeMillis() - start).toJson();
        try {
            String outResult = result.substring(2,result.length()-1);
            if (result.contains("#{") && result.contains("}")){
                // #{} 全局变量
                context.setProjectGlobalVariable(outResult,JSON.toJSONString(toJson));
            }
            if (result.contains("${") && result.contains("}")){
                // ${} 脚本变量
                context.setVariable(outResult,JSON.toJSONString(toJson));
            }
        } catch (Exception e) {
            throw new ExecuteException("上下文参数{"+result+"}设置异常");
        }
        return toJson;
    }

    /**
     * 获取Socket目标主机
     */
    private static String[] splitIpPort(String ipPort) {
        String[] hostPort = StringUtils.split(ipPort, ":");
        if (hostPort.length != 2) {
            throw new ComponentRuntimeException("错误的目标主机端口：" + ipPort);
        } else {
            int port;
            try {
                port = Integer.parseInt(hostPort[1]);
            } catch (Exception e) {
                throw new ComponentRuntimeException("目标端口必须是数字");
            }
            if (port < 0 || port > 0xFFFF) {
                throw new ComponentRuntimeException("Port out of range:" + port);
            }
        }
        return hostPort;
    }

}
