package com.cisdi.data.caliper;

import com.alibaba.fastjson.JSON;
import com.cisdi.data.caliper.gateway.CaliperClient;
import com.cisdi.data.caliper.gateway.CaliperCustomParam;
import com.cisdi.data.caliper.gateway.CaliperDaemonTask;
import com.cisdi.data.caliper.gateway.CaliperTaskStartVo;
import com.cisdi.data.common.exception.BusinessException;
import com.cisdi.data.common.utils.Strings;
import com.cisdi.data.sdk.consts.ServiceName;
import com.cisdi.data.sdk.enums.GatewayState;
import com.cisdi.data.sdk.gateway.base.PulledSocketGatewayBase;
import com.cisdi.data.sdk.param.PullSocketParam;
import com.cisdi.data.sdk.procotol.message.SocketReturnMessage;
import com.cisdi.data.sdk.service.RouteService;
import com.cisdi.data.sdk.vo.DeviceVo;
import com.cisdi.data.sdk.vo.ExeResultVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * 采用TCP/IP通信，本机做客户端，连上服务器后主动上报采集数据，数据采用大端单精度浮点数上传。具体如下：
 * 上传数据: f5 f5 f5 02 3f 9e 04 19 40 0f 02 0c f6 f6 f6
 * <p>
 * 起始帧 0xF5 0xF5 0xF5
 * 数据长度(字节) 0x08
 * 宽度(直径1.2345mm) 0x3F 0x9E 0x04 0x19
 * 厚度(直径2.2345mm) 0x40 0x0F 0x02 0x0C
 * 校验码0x57 数据之和取低八位数据  0x3f+0x9e+0x04+0x19+0x40+0x0f+0x02+0x0c=0x157
 * 结束帧 0xF6 0xF6 0xF6
 *
 * @className: CaliperGateway
 * @description: TODO
 * @author: zhang.zhipeng
 * @date: 2024/9/10 15:19
 */
@Slf4j
public class CaliperGateway extends PulledSocketGatewayBase {

    private String deviceId;
    private AtomicBoolean shouldRun = null;
    private static final int Min_Pull_Internal = 1; // 允许最小的拉取间隔为1毫秒
    private static final int Min_Timeout = 1000; // 允许最小的超时时间间隔，为1000毫秒 

    @Override
    public ExeResultVo sendReturnMessage(SocketReturnMessage returnMsg) {
        throw new BusinessException("Caliper协议不支持下发命令。");
    }

    @Override
    public void start() {
        if (state == GatewayState.RUNNING) {
            return;
        }

        try {
            startInternal();
            state = GatewayState.RUNNING;
            log.info("Caliper socket拉取网关:{}启动成功", instanceVo.getRunId());
        } catch (Exception e) {
            log.error("Caliper socket拉取网关:{}启动失败", instanceVo.getRunId());
            log.error(e.getLocalizedMessage(), e);
        }
    }

    private void startInternal() {
        socketParam = JSON.parseObject(getInstanceVo().getParameter(), PullSocketParam.class);

        if (StringUtils.isEmpty(socketParam.getPeerIp()) || socketParam.getPeerPort() == null) {
            throw new BusinessException("未正确配置拉取对端ip地址和对端端口。");
        }

        if (socketParam.getPullInternal() == null) {
            throw new BusinessException("未正确配置拉取间隔");
        }

        if (socketParam.getTimeout() == null) {
            throw new BusinessException("未正确配置拉取超时时间");
        }

        socketParam.setPullInternal(socketParam.getPullInternal() < Min_Pull_Internal ? Min_Pull_Internal : socketParam.getPullInternal());

        socketParam.setTimeout(socketParam.getTimeout() < Min_Timeout ? Min_Timeout : socketParam.getTimeout());

        RouteService routeService = (RouteService) serviceProvider.getByName(ServiceName.Route);
        List<DeviceVo> deviceVos = routeService.findByRunId(instanceVo.getRunId());

        if (deviceVos.size() > 1) {
            throw new BusinessException("网关Id:" + instanceVo.getRunId() + "为Caliper socket拉取网关，只允许关联一个设备Id");
        }

        deviceId = !deviceVos.isEmpty() ? deviceVos.get(0).getDeviceId() : null;

        if (StringUtils.isEmpty(deviceId)) {
            throw new BusinessException("runId=" + getInstanceVo().getRunId() + "未正确配置网关和设备关联");
        }

        log.info("Caliper socket拉取网关:{}读取启动配置参数:{}", getInstanceVo().getRunId(), socketParam);

        closePrevState();
        shouldRun = new AtomicBoolean(true);

        String parameter = socketParam.getOtherParameter();
        Set<String> targetServerSet = new HashSet<>();
        targetServerSet.add(String.format("%s:%s", socketParam.getPeerIp(), socketParam.getPeerPort()));
        CaliperCustomParam customParam = null;
        if (parameter != null) {
            customParam = JSON.parseObject(parameter, CaliperCustomParam.class);
            if (customParam != null) {
                 //Caliper服务端参数去重
                customParam.setServerList(customParam.getServerList().stream().distinct().collect(Collectors.toList()));
                for (String server : customParam.getServerList()) {
                    targetServerSet.add(server.trim());
                }
            }
        }

        int index = 1;
        for (String server : targetServerSet) {
            // 复制已有参数
            PullSocketParam serverParam = JSON.parseObject(JSON.toJSONString(socketParam), PullSocketParam.class);

            serverParam.setPeerIp(Strings.split(server, ":")[0]);
            serverParam.setPeerPort(Integer.valueOf(Strings.split(server, ":")[1]));

            // 启动线程
            CaliperTaskStartVo startVo = new CaliperTaskStartVo();
            startVo.setDeviceId(deviceId);
            startVo.setGatewayBase(this);
            startVo.setServiceProvider(serviceProvider);
            startVo.setSocketParam(serverParam);
            startVo.setShouldRun(shouldRun);
            startVo.setCustomParam(customParam);

            CaliperClient fireClient = new CaliperClient(startVo);
            CaliperDaemonTask daemonTask = new CaliperDaemonTask(fireClient, startVo);
            startVo.setCaliperDaemonTask(daemonTask);
            Thread thread = new Thread(daemonTask, "Fire-Daemon-Task-runid-" + instanceVo.getRunId() + "-s" + index++);
            thread.start();
        }


        state = GatewayState.RUNNING;
    }

    @Override
    public void shutdown() {
        if (state == GatewayState.CLOSED) {
            return;
        }

        closePrevState();

        log.info("Caliper socket拉取网关:{}关闭中", getInstanceVo().getRunId());

        state = GatewayState.CLOSED;

        log.info("Caliper socket拉取网关:{}关闭成功", getInstanceVo().getRunId());
    }

    private void closePrevState() {
        if (shouldRun != null) {
            shouldRun.set(false);
            shouldRun = null;
        }
    }

    @Override
    public Set<String> getActiveDeviceIds() {
        Set<String> set = new HashSet<String>();
        if (deviceId != null) {
            set.add(deviceId);
        }
        return set;
    }
}
