package com.ruoyi.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.shaded.com.google.gson.JsonElement;
import com.ruoyi.business.domain.HwDevice;
import com.ruoyi.business.domain.HwDeviceModeFunction;
import com.ruoyi.business.domain.HwScene;
import com.ruoyi.business.domain.VO.DeviceModeVo;
import com.ruoyi.business.domain.VO.HwDeviceVo;
import com.ruoyi.business.mapper.HwDeviceMapper;
import com.ruoyi.business.mapper.HwDeviceModeFunctionMapper;
import com.ruoyi.business.mapper.HwSceneMapper;
import com.ruoyi.business.service.IHwDeviceService;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.constant.HwDictConstants;
import com.ruoyi.common.core.constant.TdEngineConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.enums.DataTypeEnums;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.NumberUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.datascope.annotation.DataScope;
import com.ruoyi.tdengine.api.RemoteTdEngineService;
import com.ruoyi.tdengine.api.domain.AlterTagVo;
import com.ruoyi.tdengine.api.domain.TdField;
import com.ruoyi.tdengine.api.domain.TdSelectDto;
import com.ruoyi.tdengine.api.domain.TdTableVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 设备信息Service业务层处理
 *
 * @author xins
 * @date 2023-08-24
 */
@Service
public class HwDeviceServiceImpl implements IHwDeviceService {
    @Autowired
    private HwDeviceMapper hwDeviceMapper;
    @Autowired
    private HwSceneMapper hwSceneMapper;
    @Autowired
    private HwDeviceModeFunctionMapper hwDevieModeFunctionMapper;
    @Autowired
    private RemoteTdEngineService remoteTdEngineService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 查询设备信息
     *
     * @param deviceId 设备信息主键
     * @return 设备信息
     */
    @Override
    public HwDevice selectHwDeviceByDeviceId(Long deviceId) {

        return hwDeviceMapper.selectHwDeviceByDeviceId(deviceId);
    }

    /**
     * 查询设备信息,返回监控单元名称
     *
     * @param deviceId 设备信息主键
     * @return 设备信息
     */

    @Override
    public HwDevice selectHwDeviceJoinByDeviceId(Long deviceId) {
        return hwDeviceMapper.selectHwDeviceJoinByDeviceId(deviceId);
    }

    /**
     * 查询设备信息列表
     *
     * @param hwDevice 设备信息
     * @return 设备信息
     */
    @Override
    @DataScope(tenantAlias = "hd")
    public List<HwDevice> selectHwDeviceList(HwDevice hwDevice) {
        return hwDeviceMapper.selectHwDeviceList(hwDevice);
    }

    /**
     * 查询关联设备信息列表
     *
     * @param deviceId 设备信息主键
     * @return 设备信息
     */
    @Override
    public List<HwDevice> selectLinkDeviceList(Long deviceId) {
        return hwDeviceMapper.selectLinkedDevices(deviceId);
    }

    /**
     * 新增设备信息
     *
     * @param hwDevice 设备信息
     * @return 结果
     */
    @Transactional
    @Override
    public int insertHwDevice(HwDevice hwDevice) {
        hwDevice.setCreateTime(DateUtils.getNowDate());
        String deviceType = hwDevice.getDeviceType();
        int deviceId = hwDeviceMapper.insertHwDevice(hwDevice);
        if (deviceType.equals(HwDictConstants.DEVICE_TYPE_GATEWAY_DEVICE)) {
            this.updateMqttAuth(hwDevice);
        } else if (deviceType.equals(HwDictConstants.DEVICE_TYPE_DIRECT_CONNECT_DEVICE)) {
            this.updateMqttAuth(hwDevice);
            this.createTdTable(hwDevice);
        } else if (deviceType.equals(HwDictConstants.DEVICE_TYPE_GATEWAY_SUB_EQUIPMENT)) {
            this.createTdTable(hwDevice);
        }
        return deviceId;
    }

    /**
     * @param: hwDevice
     * @description 更新redis中的设备信息
     * @author xins
     * @date 2023-09-19 10:06
     */
    private void updateMqttAuth(HwDevice hwDevice) {
        try {
            HwScene scene = hwSceneMapper.selectHwSceneBySceneId(hwDevice.getSceneId());
            String modeAccount = scene.getModeAccount();
            String modeKey = scene.getModeKey();
            if (StringUtils.isNotEmpty(modeAccount) && StringUtils.isNotEmpty(modeKey)) {
                String deviceInfoStr = redisTemplate.opsForValue().get(HwDictConstants.REDIS_KEY_DEVICE_INFO);
                JSONArray deviceInfoJsonArr = new JSONArray();
//            JSONObject deviceInfoJson;
                if (StringUtils.isNotEmpty(deviceInfoStr)) {
                    deviceInfoJsonArr = JSON.parseArray(deviceInfoStr);
                }
                boolean redisUpdated = false;//是否更新已有的值
                for (Object deviceInfoObj : deviceInfoJsonArr) {
                    JSONObject deviceInfoJson = (JSONObject) deviceInfoObj;
                    if (deviceInfoJson.getString("deviceCode").equals(hwDevice.getDeviceCode())) {
                        deviceInfoJson.put("username", modeAccount);
                        deviceInfoJson.put("password", modeKey);
                        redisUpdated = true;
                    }
                }

                if (!redisUpdated) {
                    JSONObject deviceInfoJson = new JSONObject();
                    deviceInfoJson.put("deviceCode", hwDevice.getDeviceCode());
                    deviceInfoJson.put("username", modeAccount);
                    deviceInfoJson.put("password", modeKey);
                    deviceInfoJsonArr.add(deviceInfoJson);
                }

                redisTemplate.opsForValue().set(HwDictConstants.REDIS_KEY_DEVICE_INFO, deviceInfoJsonArr.toJSONString());
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * @param: hwDevice
     * @description 在tdengine创建table
     * @author xins
     * @date 2023-09-19 10:07
     */
    private void createTdTable(HwDevice hwDevice) {
        TdTableVo tdTableVo = new TdTableVo();
        String databaseName = TdEngineConstants.getDatabaseName(1L);
        String superTableName = TdEngineConstants.getSuperTableName(hwDevice.getDeviceModeId());
        String tableName = TdEngineConstants.getTableName(hwDevice.getDeviceId());

        List<TdField> tagsFields = new ArrayList<>();

        TdField deviceIdTag = new TdField();
        deviceIdTag.setFieldName(TdEngineConstants.ST_TAG_DEVICEID);
        deviceIdTag.setFieldValue(hwDevice.getDeviceId());
        tagsFields.add(deviceIdTag);

        TdField deviceCodeTag = new TdField();
        deviceCodeTag.setFieldName(TdEngineConstants.ST_TAG_DEVICECODE);
        deviceCodeTag.setFieldValue(hwDevice.getDeviceCode());
        deviceCodeTag.setDataTypeCode(DataTypeEnums.NCHAR.getDataCode());
        tagsFields.add(deviceCodeTag);

        TdField deviceNameTag = new TdField();
        deviceNameTag.setFieldName(TdEngineConstants.ST_TAG_DEVICENAME);
        deviceNameTag.setFieldValue(hwDevice.getDeviceName());
        deviceNameTag.setDataTypeCode(DataTypeEnums.NCHAR.getDataCode());
        tagsFields.add(deviceNameTag);

        TdField deviceModeIdTag = new TdField();
        deviceModeIdTag.setFieldName(TdEngineConstants.ST_TAG_DEVICEMODEID);
        deviceModeIdTag.setFieldValue(hwDevice.getDeviceModeId());
        tagsFields.add(deviceModeIdTag);

        TdField monitorUnitIdTag = new TdField();
        monitorUnitIdTag.setFieldName(TdEngineConstants.ST_TAG_MONITORUNITID);
        monitorUnitIdTag.setFieldValue(hwDevice.getMonitorUnitId());
        tagsFields.add(monitorUnitIdTag);

        tdTableVo.setDatabaseName(databaseName);
        tdTableVo.setSuperTableName(superTableName);
        tdTableVo.setTableName(tableName);
        tdTableVo.setTagsFieldValues(tagsFields);

        R<?> tdReturnMsg = this.remoteTdEngineService.createTable(tdTableVo);
        if (tdReturnMsg.getCode() != Constants.SUCCESS) {//抛出异常，回滚事务
            throw new RuntimeException(tdReturnMsg.getMsg());
        }
    }

    /**
     * 查询3.	设备各类型占所有设备数量百分比饼状图（左下角）
     */
    @Override
    public List<DeviceModeVo> selectDeviceModeNameVo() {
        List<DeviceModeVo> list = hwDeviceMapper.selectDeviceModeNameVo();
        Integer value = 0;
        if (list.size() > 10) {
            for (int i = 9; i < list.size(); i++) {
                if (list.get(i).sum == null) {
                    list.get(i).sum = 0;
                }
                value = value + list.get(i).sum;
            }
        }
//        DeviceModeVo hwMonitorUnitTypeVo = new DeviceModeVo();
//        hwMonitorUnitTypeVo.setDeviceModeName("其他");
//        hwMonitorUnitTypeVo.setSum(value);
        Integer sums = 0;
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).sum == null) {
                list.get(i).sum = 0;
            }
            sums = sums + list.get(i).sum;
        }
        for (int i = 0; i < list.size(); i++) {
            list.get(i).setPercentage(NumberUtils.getPercentage(list.get(i).getSum(), sums));
            System.out.println(list.get(i).toString());
        }


        return list;
//        if (list.size() > 10) {
//            list.add(10, hwMonitorUnitTypeVo);
//            return list.subList(0, 10);
//        } else {
//            return list;
//        }
    }


    /**
     * 修改设备信息
     *
     * @param hwDevice 设备信息
     * @return 结果
     */
    @Transactional
    @Override
    public int updateHwDevice(HwDevice hwDevice) {
        HwDevice dbDevice = hwDeviceMapper.selectHwDeviceByDeviceId(hwDevice.getDeviceId());
        if (dbDevice.getDeviceStatus().equals(HwDictConstants.DEVICE_STATUS_PUBLISH)) {
            throw new ServiceException("已发布状态不能修改");
        }
        hwDevice.setUpdateTime(DateUtils.getNowDate());
        int rows = hwDeviceMapper.updateHwDevice(hwDevice);
        this.updateTdEngine(hwDevice, dbDevice);
        return rows;
    }

    /**
     * @param: hwDevice 前端传的对象
     * @param: dbDevice 目前数据库的对象
     * @description 更新TdEngine数据库tag
     * @author xins
     * @date 2023-09-19 10:55
     */
    private void updateTdEngine(HwDevice hwDevice, HwDevice dbDevice) {
        String deviceType = hwDevice.getDeviceType();
        String databaseName = TdEngineConstants.getDatabaseName(1L);//TODO
        String tableName = TdEngineConstants.getTableName(hwDevice.getDeviceId());
        AlterTagVo alterTagVo = new AlterTagVo();
        alterTagVo.setDatabaseName(databaseName);
        alterTagVo.setTableName(tableName);
        R<?> tdReturnMsg;
        if (deviceType.equals(HwDictConstants.DEVICE_TYPE_DIRECT_CONNECT_DEVICE)
                || deviceType.equals(HwDictConstants.DEVICE_TYPE_GATEWAY_SUB_EQUIPMENT)) {
            if (!hwDevice.getDeviceCode().equals(dbDevice.getDeviceCode())) {
                alterTagVo.setTagName(TdEngineConstants.ST_TAG_DEVICECODE);
                alterTagVo.setTagValue("'"+hwDevice.getDeviceCode()+"'");

                tdReturnMsg = this.remoteTdEngineService.alterTableTag(alterTagVo);
                if (tdReturnMsg.getCode() != Constants.SUCCESS) {//抛出异常，回滚事务
                    throw new RuntimeException(tdReturnMsg.getMsg());
                }
            }
            if (!hwDevice.getDeviceName().equals(dbDevice.getDeviceName())) {
                alterTagVo.setTagName(TdEngineConstants.ST_TAG_DEVICENAME);
                alterTagVo.setTagValue("'"+hwDevice.getDeviceName()+"'");
                tdReturnMsg = this.remoteTdEngineService.alterTableTag(alterTagVo);
                if (tdReturnMsg.getCode() != Constants.SUCCESS) {//抛出异常，回滚事务
                    throw new RuntimeException(tdReturnMsg.getMsg());
                }
            }

            if (!hwDevice.getMonitorUnitId().equals(dbDevice.getMonitorUnitId())) {
                alterTagVo.setTagName(TdEngineConstants.ST_TAG_MONITORUNITID);
                alterTagVo.setTagValue(hwDevice.getMonitorUnitId());
                tdReturnMsg = this.remoteTdEngineService.alterTableTag(alterTagVo);
                if (tdReturnMsg.getCode() != Constants.SUCCESS) {//抛出异常，回滚事务
                    throw new RuntimeException(tdReturnMsg.getMsg());
                }
            }

            if (!hwDevice.getDeviceModeId().equals(dbDevice.getDeviceModeId())) {
                alterTagVo.setTagName(TdEngineConstants.ST_TAG_DEVICEMODEID);
                alterTagVo.setTagValue(hwDevice.getDeviceModeId());
                tdReturnMsg = this.remoteTdEngineService.alterTableTag(alterTagVo);
                if (tdReturnMsg.getCode() != Constants.SUCCESS) {//抛出异常，回滚事务
                    throw new RuntimeException(tdReturnMsg.getMsg());
                }
            }
        }
    }

    /**
     * 批量删除设备信息
     *
     * @param deviceIds 需要删除的设备信息主键
     * @return 结果
     */
    @Override
    public int deleteHwDeviceByDeviceIds(Long[] deviceIds) {
        return hwDeviceMapper.deleteHwDeviceByDeviceIds(deviceIds);
    }

    /**
     * 删除设备信息信息
     *
     * @param deviceId 设备信息主键
     * @return 结果
     */
    @Override
    public int deleteHwDeviceByDeviceId(Long deviceId) {
        return hwDeviceMapper.deleteHwDeviceByDeviceId(deviceId);
    }


    /**
     * @return Map<String, List < HwDeviceVo>>
     * @description 设备监测页面，根据监控单元获取设备监控信息
     * @author xins
     * @date 2023-09-15 16:59
     */
    @Override
    public Map<String, List<HwDeviceVo>> getDevicesByMonitor(HwDevice queryHwDevice) {
        Map<String, List<HwDeviceVo>> devicesMap = new HashMap<String, List<HwDeviceVo>>();
        List<HwDeviceVo> controlDeviceVos = new ArrayList<HwDeviceVo>();
        List<HwDeviceVo> acquisitionDeviceVos = new ArrayList<HwDeviceVo>();
        Long sceneId = queryHwDevice.getSceneId();
        String databaseName = TdEngineConstants.getDatabaseName(sceneId);

        List<HwDevice> hwDevices = hwDeviceMapper.selectHwDeviceList(queryHwDevice);
        hwDevices.forEach(hwDevice -> {

            Long deviceId = hwDevice.getDeviceId();
            String tableName = TdEngineConstants.getDeviceDataTableName(deviceId);
            TdSelectDto tdSelectDto = new TdSelectDto();
            tdSelectDto.setDatabaseName(databaseName);
            tdSelectDto.setTableName(tableName);
            List<Map<String, Object>> deviceLatestDataMapList = (List<Map<String, Object>>) this.remoteTdEngineService.getLatestData(tdSelectDto).getData();

            HwDeviceModeFunction queryDeviceModeFunction = new HwDeviceModeFunction();
            queryDeviceModeFunction.setDeviceModeId(hwDevice.getDeviceModeId());
            List<HwDeviceModeFunction> deviceModeFunctions = hwDevieModeFunctionMapper.selectHwDeviceModeFunctionList(queryDeviceModeFunction);
            List<HwDeviceModeFunction> dds = deviceModeFunctions.stream()
                    .filter(dmf -> dmf.getFunctionMode().equals(HwDictConstants.FUNCTION_MODE_ATTRIBUTE)
                            && dmf.getDataType() != null && String.valueOf(dmf.getDataType()).equals(String.valueOf(DataTypeEnums.TINYINT.getDataCode()))).collect(Collectors.toList());

            HwDeviceVo hwDeviceVo = new HwDeviceVo();
            hwDeviceVo.setDeviceName(hwDevice.getDeviceName());
            if (dds != null && dds.size() > 0) {
                hwDeviceVo.setDeviceType(HwDictConstants.CONTROL_DEVICE);
                HwDeviceModeFunction dd = dds.get(0);
                List<Map<String, Object>> ddValueMapList = new ArrayList<Map<String, Object>>();
                Map<String, Object> ddValueMap = new HashMap<String, Object>();

                deviceLatestDataMapList.forEach(deviceLatestDataMap -> {
                    ddValueMap.put(dd.getFunctionIdentifier(), deviceLatestDataMap.get(dd.getFunctionIdentifier()) == null
                            ? 0 : deviceLatestDataMap.get(dd.getFunctionIdentifier()));
                });
                ddValueMapList.add(ddValueMap);

                hwDeviceVo.setDeviceDataMaps(ddValueMapList);
                controlDeviceVos.add(hwDeviceVo);
            } else {
                hwDeviceVo.setDeviceType(HwDictConstants.ACQUISITION_DEVICE);
                hwDeviceVo.setDeviceDataMaps(deviceLatestDataMapList);
                acquisitionDeviceVos.add(hwDeviceVo);
            }

        });

        devicesMap.put(HwDictConstants.CONTROL_DEVICE_NAME, controlDeviceVos);
        devicesMap.put(HwDictConstants.ACQUISITION_DEVICE_NAME, acquisitionDeviceVos);

        return devicesMap;
    }


    /**
     * 查询设备信息列表,join监控单元、设备模型等表
     *
     * @param hwDevice 设备信息
     * @return 设备信息
     */
    @Override
    @DataScope(tenantAlias = "hd")
    public List<HwDevice> selectHwDeviceJoinList(HwDevice hwDevice) {
        return hwDeviceMapper.selectHwDeviceJoinList(hwDevice);
    }
}
