package com.gyjiot.iot.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.gyjiot.common.core.redis.RedisCache;
import com.gyjiot.common.core.redis.RedisKeyBuilder;
import com.gyjiot.common.enums.FunctionReplyStatus;
import com.gyjiot.common.enums.ThingsModelType;
import com.gyjiot.common.utils.DateUtils;
import com.gyjiot.iot.domain.DeviceLog;
import com.gyjiot.iot.domain.FunctionLog;
import com.gyjiot.iot.model.Specs;
import com.gyjiot.iot.service.IDeviceRuntimeService;
import com.gyjiot.iot.service.IFunctionLogService;
import com.gyjiot.iot.service.IThingsModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 设备运行时数据
 *
 * @author gyj.iot
 * @date 2023/2/1 15:11
 */
@Service
public class DeviceRuntimeServiceImpl implements IDeviceRuntimeService {

    @Autowired
    private RedisCache redisCache;
    @Resource
    private IFunctionLogService logService;
    @Resource
    private IThingsModelService thingsModelService;

    /**
     * 根据设备编号查询设备实时运行状态
     *
     * @param serialNumber 设备编号
     * @param type         物模型类型
     * @return 设备实时数据
     */
    @Override
    public List<DeviceLog> runtimeBySerialNumber(String serialNumber, ThingsModelType type, Long productId, Integer slaveId) {
        //获取redis中的物模型
        String thingsModel = thingsModelService.getCacheThingsModelByProductId(productId);
        JSONObject thingModelObject = JSONObject.parseObject(thingsModel);
        String key = type == ThingsModelType.PROP ? "properties" : "functions";
        String properties = thingModelObject.getString(key);
        /*在redis中查找实时数据*/
        String cacheKey = RedisKeyBuilder.buildDeviceRtCacheKey(serialNumber);
        /*物模型标识符 -- value*/
        Map<String, String> cacheMap = redisCache.getCacheMap(cacheKey);
        /*查询是否有计算公式参数*/
        String paramsKey = RedisKeyBuilder.buildDeviceRtParamsKey(serialNumber);
        Map<String, String> params = redisCache.getCacheMap(paramsKey);
        List<DeviceLog> results = new ArrayList<>();
        List<Specs> propList = JSONObject.parseArray(properties, Specs.class);
        for (Specs specs : propList) {
            if (specs.getSlaveId().equals(slaveId)) {
                DeviceLog log = new DeviceLog();
                log.setModelName(specs.getName());
                log.setLogType(type.getCode());
                log.setSpecs(JSONObject.toJSONString(specs.getDatatype()));
                log.setIdentity(specs.getId());
                log.setSerialNumber(serialNumber);
                log.setSlaveId(specs.getSlaveId());
                log.setIsMonitor(specs.getIsMonitor());
                log.setFormula(specs.getFormula());
                if (!CollectionUtils.isEmpty(cacheMap) && cacheMap.containsKey(specs.getId())) {
                    DeviceLog valueLog = JSONObject.parseObject(JSONObject.parse(cacheMap.get(specs.getId())).toString(), DeviceLog.class);
                    log.setLogValue(valueLog.getLogValue());
                    log.setUpdateTime(valueLog.getTs());
                    log.setTs(valueLog.getTs());
                    //if (log.getFormula() != null && !log.getFormula().equals("")) {
                    //    params.put("%s", log.getLogValue());
                    //    BigDecimal value = CaculateUtils.execute(log.getFormula(), params);
                    //    log.setLogValue(value.toString());
                    //}
                }
                results.add(log);
            }
        }
        List<DeviceLog> result = results.stream().
                filter(log -> log.getLogType() == type.getCode()).collect(Collectors.toList());
        for (DeviceLog log : result) {
            parseSpecs(log);
        }
        return result;
    }


    /**
     * 根据设备编号查询设备服务调用日志情况
     *
     * @param serialNumber 设备编号
     * @return 服务下发日志
     */
    @Override
    public List<FunctionLog> runtimeReply(String serialNumber) {
        FunctionLog log = new FunctionLog();
        log.setSerialNumber(serialNumber);
        // 1-服务下发
        log.setFunType(1);
        List<FunctionLog> logList = logService.selectFunctionLogList(log);
        for (FunctionLog l : logList) {
            if (l.getReplyTime() == null) {
                l.setReplyTime(DateUtils.getNowDate());
            }

            l.setShowValue(l.getResultMsg());
            //设备超过10s未回复，则认为下发失败
            if (l.getResultCode().equals(FunctionReplyStatus.NORELY.getCode()) && DateUtils.getTimestamp() - l.getCreateTime().getTime() >10000){
                l.setShowValue(FunctionReplyStatus.FAIl.getMessage());
                l.setResultCode(FunctionReplyStatus.FAIl.getCode());
            }
        }
        return logList;
    }

    /**
     * 定时任务，自动更新设备响应超时状态
     */
    private void autoUpdateFuncLogStatus() {
        FunctionLog functionLog = new FunctionLog();
        functionLog.setResultCode(FunctionReplyStatus.NORELY.getCode());
        List<FunctionLog> logList = logService.selectFunctionLogList(functionLog);
        List<Long> ids = new ArrayList<>();
        for (FunctionLog log : logList) {
            //如果设备回复超过20s，自动更新设备回复状态为设备超时
            if (DateUtils.getTimestamp() - log.getCreateTime().getTime() > 20000) {
                ids.add(log.getId());
            }
        }
        //更新状态为超时未回复
        if (!CollectionUtils.isEmpty(ids)) {
            FunctionLog log = new FunctionLog();
            log.setList(ids);
            log.setResultMsg(FunctionReplyStatus.UNKNOWN.getMessage());
            log.setResultCode(FunctionReplyStatus.UNKNOWN.getCode());
            log.setReplyTime(DateUtils.getNowDate());
            logService.updateFuncLogBatch(log);
        }
    }

    /**
     * 解析数据定义specs
     *
     * @param log
     */
    public void parseSpecs(DeviceLog log) {
        DeviceLog.DataType spesc = new DeviceLog.DataType();
        if (null != log.getSpecs() && !log.getSpecs().equals("null")) {
            DeviceLog.DataType dataType = JSONObject.parseObject(JSONObject.parse(log.getSpecs()).toString(), DeviceLog.DataType.class);
            log.setDataType(dataType);
        } else {
            spesc.setType("integer");
            log.setDataType(spesc);
        }
    }



}
