package com.fastbee.data.quartz;

import com.alibaba.fastjson2.JSON;
import com.fastbee.base.service.ISessionStore;
import com.fastbee.base.session.Session;
import com.fastbee.common.core.device.DeviceAndProtocol;
import com.fastbee.common.core.mq.message.ModbusPollMsg;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.common.enums.DeviceStatus;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.spring.SpringUtils;
import com.fastbee.iot.domain.*;
import com.fastbee.iot.enums.DeviceType;
import com.fastbee.iot.mapper.*;
import com.fastbee.iot.model.Action;
import com.fastbee.iot.model.DeviceStatusVO;
import com.fastbee.iot.model.ModbusJobBo;
import com.fastbee.iot.model.ModbusPollBo;
import com.fastbee.iot.service.*;
import com.fastbee.mq.redischannel.producer.MessageProducer;
import com.fastbee.mq.ruleEngine.SceneContext;
import com.fastbee.mq.service.IDataHandler;
import com.fastbee.mq.service.IMqttMessagePublish;
import com.fastbee.mqtt.manager.MqttRemoteManager;
import com.fastbee.ruleEngine.core.FlowLogExecutor;
import com.google.common.annotations.GwtCompatible;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * 任务执行工具
 *
 * @author kerwincui
 */
@Slf4j
public class JobInvokeUtil {

    /**
     * 获取消息推送接口
     */
    private static IMqttMessagePublish messagePublish = SpringUtils.getBean(IMqttMessagePublish.class);

    private static SceneMapper sceneMapper = SpringUtils.getBean(SceneMapper.class);

    private static FlowLogExecutor flowExecutor = SpringUtils.getBean(FlowLogExecutor.class);

    private static IDataHandler dataHandler = SpringUtils.getBean(IDataHandler.class);

    private static IDeviceService deviceService = SpringUtils.getBean(IDeviceService.class);

    private static IModbusJobService modbusJobService = SpringUtils.getBean(IModbusJobService.class);

    private static IModbusParamsService modbusParamsService = SpringUtils.getBean(IModbusParamsService.class);

    private static ISessionStore sessionStore = SpringUtils.getBean(ISessionStore.class);

    private static MqttRemoteManager mqttRemoteManager = SpringUtils.getBean(MqttRemoteManager.class);

    /**
     * 执行方法
     *
     * @param deviceJob 系统任务
     */
    public static void invokeMethod(DeviceJob deviceJob) throws Exception {
        if (deviceJob.getJobType() == 1) {
            System.out.println("------------------------执行定时任务-----------------------------");
            List<Action> actions = JSON.parseArray(deviceJob.getActions(), Action.class);
            List<ThingsModelSimpleItem> propertys = new ArrayList<>();
            List<ThingsModelSimpleItem> functions = new ArrayList<>();
            for (int i = 0; i < actions.size(); i++) {
                ThingsModelSimpleItem model = new ThingsModelSimpleItem();
                model.setId(actions.get(i).getId());
                model.setValue(actions.get(i).getValue());
                model.setRemark("设备定时");
                if (actions.get(i).getType() == 1) {
                    propertys.add(model);
                } else if (actions.get(i).getType() == 2) {
                    functions.add(model);
                }
            }
            // 发布属性
            if (propertys.size() > 0) {
                messagePublish.publishProperty(deviceJob.getProductId(), deviceJob.getSerialNumber(), propertys, 0);
            }
            // 发布功能
            if (functions.size() > 0) {
                messagePublish.publishFunction(deviceJob.getProductId(), deviceJob.getSerialNumber(), functions, 0);
            }

        } else if (deviceJob.getJobType() == 3) {
            System.out.println("------------------[定时执行场景联动]---------------------");
            Scene scene=sceneMapper.selectSceneBySceneId(deviceJob.getSceneId());
            // 执行场景规则,异步非阻塞
            SceneContext context = new SceneContext("", 0L,0,null);
            flowExecutor.execute2Future(String.valueOf(scene.getChainName()), null, context);
        } else if (4 == deviceJob.getJobType()) {
            System.out.println("------------------[定时执行场景运算型变量]---------------------");
            String s = dataHandler.calculateSceneModelTagValue(deviceJob.getDatasourceId());
            if (StringUtils.isEmpty(s)) {
                System.out.println("------------------[定时执行场景运算型变量失败：+" + s + "]---------------------");
            }
        }else if (5 == deviceJob.getJobType()){
            log.info("----------------执行modbus轮训指令----------------------");
            Long jobId = deviceJob.getJobId();
            ModbusJob modbusJob = new ModbusJob();
            modbusJob.setJobId(jobId);
            modbusJob.setStatus("0");
            List<ModbusJob> modbusJobList = modbusJobService.selectModbusJobList(modbusJob);
            if (CollectionUtils.isEmpty(modbusJobList))return;
            //处理子设备情况
            handleSubDeviceStatus(modbusJobList);
            List<String> commandList = modbusJobList.stream().map(ModbusJob::getCommand).collect(Collectors.toList());
            ModbusPollMsg modbusPollMsg = new ModbusPollMsg();
            modbusPollMsg.setSerialNumber(deviceJob.getSerialNumber());
            modbusPollMsg.setProductId(deviceJob.getProductId());
            modbusPollMsg.setCommandList(commandList);
            DeviceStatusVO deviceStatusVO = deviceService.selectDeviceStatusAndTransportStatus(modbusPollMsg.getSerialNumber());
            modbusPollMsg.setTransport(deviceStatusVO.getTransport());
            if (deviceStatusVO.getStatus() != DeviceStatus.ONLINE.getType()){
                log.info("设备：[{}],不在线",modbusPollMsg.getSerialNumber());
                return;
            }
            log.info("执行modbus轮询指令:[{}]",JSON.toJSONString(commandList));
            MessageProducer.sendPropFetch(modbusPollMsg);
        }
    }

    /**
     * TODO --耗时操作，这里后续放在MQ处理
     * 处理子设备根据设备数据定时更新状态
     */
    public static void handleSubDeviceStatus(List<ModbusJob> modbusJobList){
        for (ModbusJob modbusJob : modbusJobList) {
            String subSerialNumber = modbusJob.getSubSerialNumber();
            Session session = sessionStore.getSession(subSerialNumber);
            if (Objects.isNull(session))continue;
            //如果是网关子设备，则检测子设备是否在特定时间内有数据上报
            if (modbusJob.getDeviceType() == DeviceType.SUB_GATEWAY.getCode()){
                long deterTime = 300L; //这里默认使用5分钟,如果轮询时间大于5分钟，请在配置参数设置更长时间
                ModbusParams params = modbusParamsService.getModbusParamsByDeviceId(modbusJob.getSubDeviceId());
                if (!Objects.isNull(params)){
                    deterTime = Long.parseLong(params.getDeterTimer());
                }

                long lastAccessTime = session.getLastAccessTime();
                //如果现在的时间 - 最后访问时间 > 判断时间则更新为离线
                long time = (System.currentTimeMillis() - lastAccessTime) / 1000;
                if (time > deterTime){
                    //处理设备离线
                    Device updateBo = new Device();
                    updateBo.setStatus(DeviceStatus.OFFLINE.getType());
                    updateBo.setSerialNumber(subSerialNumber);
                    updateBo.setUpdateTime(DateUtils.getNowDate());
                    deviceService.updateDeviceStatus(updateBo);
                    mqttRemoteManager.pushDeviceStatus(params.getProductId(), subSerialNumber, DeviceStatus.OFFLINE);
                    sessionStore.cleanSession(subSerialNumber);
                    log.info("设备[{}],超过：[{}],未上报数据，更新为离线",subSerialNumber,deterTime);
                }

            }
        }
    }

}
