package com.ruoyi.business.service.impl;

import com.ruoyi.business.domain.HwDevice;
import com.ruoyi.business.domain.HwDeviceMode;
import com.ruoyi.business.domain.HwDeviceModeFunction;
import com.ruoyi.business.domain.HwDeviceModeParameter;
import com.ruoyi.business.mapper.HwDeviceMapper;
import com.ruoyi.business.mapper.HwDeviceModeFunctionMapper;
import com.ruoyi.business.mapper.HwDeviceModeMapper;
import com.ruoyi.business.service.IHwDeviceModeService;
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.StringUtils;
import com.ruoyi.common.datascope.annotation.DataScope;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.RemoteConfigService;
import com.ruoyi.tdengine.api.RemoteTdEngineService;
import com.ruoyi.tdengine.api.domain.TdField;
import com.ruoyi.tdengine.api.domain.TdSuperTableVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 设备模型Service业务层处理
 *
 * @author xins
 * @date 2023-09-04
 */
@Service
public class HwDeviceModeServiceImpl implements IHwDeviceModeService {
    @Autowired
    private HwDeviceModeMapper hwDeviceModeMapper;

    @Autowired
    private HwDeviceModeFunctionMapper hwDeviceModeFunctionMapper;

    @Autowired
    private HwDeviceMapper hwDeviceMapper;

    @Resource
    private RemoteConfigService remoteConfigService;

    @Resource
    private RemoteTdEngineService remoteTdEngineService;

    /**
     * 查询设备模型
     *
     * @param deviceModeId 设备模型主键
     * @return 设备模型
     */
    @Override
    public HwDeviceMode selectHwDeviceModeByDeviceModeId(Long deviceModeId) {
        return hwDeviceModeMapper.selectHwDeviceModeByDeviceModeId(deviceModeId);
    }

    /**
     * 查询设备模型列表
     *
     * @param hwDeviceMode 设备模型
     * @return 设备模型
     */
    @Override
    @DataScope(tenantAlias = "hdm")
    public List<HwDeviceMode> selectHwDeviceModeList(HwDeviceMode hwDeviceMode) {
        return hwDeviceModeMapper.selectHwDeviceModeList(hwDeviceMode);
    }

    /**
     * 新增设备模型
     *
     * @param hwDeviceMode 设备模型
     * @return 结果
     */
    @Transactional
    @Override
    public int insertHwDeviceMode(HwDeviceMode hwDeviceMode) {
        //先判断是否有重复的标识符
        List<HwDeviceModeFunction> hwDeviceModeFunctionList = hwDeviceMode.getHwDeviceModeFunctionList();

        List<String> duplicateFunctionIdentifiers = hwDeviceModeFunctionList.stream()
                .collect(Collectors.groupingBy(p -> p.getFunctionIdentifier(), Collectors.counting())).entrySet().stream()
                .filter(entry -> entry.getValue() > 1) // >1 查找重复的集合；== 查找不重复的集合
                .map(entry -> entry.getKey())
                .collect(Collectors.toList());
        if (duplicateFunctionIdentifiers != null && !duplicateFunctionIdentifiers.isEmpty()) {
            throw new ServiceException("存在重复的标识符:" + duplicateFunctionIdentifiers.toString());
        }
        //再判断是否不是定位的标识符与定位标识符相同
        R<String> keyLongitudeR = remoteConfigService.getConfigKeyStr("hw.gps.longitude");
        R<String> keyLatitudeR = remoteConfigService.getConfigKeyStr("hw.gps.latitude");
        String keyLongitude = keyLongitudeR.getData();
        String keyLatitude = keyLatitudeR.getData();

        hwDeviceModeFunctionList.forEach(d -> {
            if (d.getFunctionIdentifier().equalsIgnoreCase(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME)) {
                throw new ServiceException("标识符不能等于:" + TdEngineConstants.DEFAULT_FIRST_FIELD_NAME);
            }
            if (StringUtils.isEmpty(d.getCoordinate()) &&
                    (d.getFunctionIdentifier().equalsIgnoreCase(keyLongitude)
                            || d.getFunctionIdentifier().equalsIgnoreCase(keyLatitude))) {
                throw new ServiceException("非定位设备模型标识符不能等于:" + keyLongitude + "或" + keyLatitude);
            }
        });

        hwDeviceMode.setCreateTime(DateUtils.getNowDate());
        hwDeviceMode.setCreateBy(SecurityUtils.getUsername());
        int rows = hwDeviceModeMapper.insertHwDeviceMode(hwDeviceMode);
        batchInsertHwDeviceModeFunction(hwDeviceMode);

        this.createTdSuperTable(hwDeviceMode);
        return rows;
    }

    /**
     * 修改设备模型
     *
     * @param hwDeviceMode 设备模型
     * @return 结果
     */
    @Transactional
    @Override
    public int updateHwDeviceMode(HwDeviceMode hwDeviceMode) {
        hwDeviceMode.setUpdateBy(SecurityUtils.getUsername());
        hwDeviceMode.setUpdateTime(DateUtils.getNowDate());
//        hwDeviceModeMapper.deleteHwDeviceModeFunctionByDeviceModeId(hwDeviceMode.getDeviceModeId());
//        insertHwDeviceModeFunction(hwDeviceMode);
        return hwDeviceModeMapper.updateHwDeviceMode(hwDeviceMode);
    }

    /**
     * 批量删除设备模型
     *
     * @param deviceModeIds 需要删除的设备模型主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteHwDeviceModeByDeviceModeIds(Long[] deviceModeIds) {
        for (Long deviceModeId : deviceModeIds) {
            HwDevice queryDevice = new HwDevice();
            queryDevice.setDeviceModeId(deviceModeId);
            List<HwDevice> deviceList = hwDeviceMapper.selectHwDeviceList(queryDevice);
            if (deviceList != null && !deviceList.isEmpty()) {
                throw new ServiceException("该设备模型下存在设备，不能删除");
            }
        }
        hwDeviceModeMapper.deleteHwDeviceModeFunctionByDeviceModeIds(deviceModeIds);
        return hwDeviceModeMapper.deleteHwDeviceModeByDeviceModeIds(deviceModeIds);
    }

    /**
     * 删除设备模型信息
     *
     * @param deviceModeId 设备模型主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteHwDeviceModeByDeviceModeId(Long deviceModeId) {
        hwDeviceModeMapper.deleteHwDeviceModeFunctionByDeviceModeId(deviceModeId);
        return hwDeviceModeMapper.deleteHwDeviceModeByDeviceModeId(deviceModeId);
    }

    /**
     * 新增设备模型功能信息
     *
     * @param hwDeviceMode 设备模型对象
     */
    private void batchInsertHwDeviceModeFunction(HwDeviceMode hwDeviceMode) {
        List<HwDeviceModeFunction> hwDeviceModeFunctionList = hwDeviceMode.getHwDeviceModeFunctionList();
        Long deviceModeId = hwDeviceMode.getDeviceModeId();
        if (StringUtils.isNotNull(hwDeviceModeFunctionList)) {
            List<HwDeviceModeFunction> list = new ArrayList<>();
            for (HwDeviceModeFunction hwDeviceModeFunction : hwDeviceModeFunctionList) {
                hwDeviceModeFunction.setDeviceModeId(deviceModeId);
                list.add(hwDeviceModeFunction);
            }
            if (list.size() > 0) {
                hwDeviceModeMapper.batchHwDeviceModeFunction(list);
                batchInsertHwDeviceModeParameters(list);
            }
        }
    }

    /**
     * @param: list
     * @description 根据设备功能列表插入参数列表（包括入参和出参）
     * @author xins
     * @date 2023-09-12 9:54
     */
    private void batchInsertHwDeviceModeParameters(List<HwDeviceModeFunction> list) {
        List<HwDeviceModeParameter> toInsertedParameters = new ArrayList<>();
        for (HwDeviceModeFunction insertedDeviceModeFunction : list) {
            List<HwDeviceModeParameter> inputParameters = insertedDeviceModeFunction.getInputParameters();
            List<HwDeviceModeParameter> outputParameters = insertedDeviceModeFunction.getOutputParameters();

            if (inputParameters != null && !inputParameters.isEmpty()) {
                for (HwDeviceModeParameter inputParameter : inputParameters) {
                    inputParameter.setModeFunctionId(insertedDeviceModeFunction.getModeFunctionId());
                    toInsertedParameters.add(inputParameter);
                }
            }

            if (outputParameters != null && !outputParameters.isEmpty()) {
                for (HwDeviceModeParameter outputParameter : outputParameters) {
                    outputParameter.setModeFunctionId(insertedDeviceModeFunction.getModeFunctionId());
                    toInsertedParameters.add(outputParameter);
                }
            }
        }

        if (!toInsertedParameters.isEmpty()) {
            hwDeviceModeMapper.batchHwDeviceModeParameters(toInsertedParameters);
        }
    }

    /**
     * 新增设备模型功能
     *
     * @param hwDeviceModeFunction 设备模型功能
     * @return 结果
     */
    @Override
    @Transactional
    public Long insertHwDeviceModeFunction(HwDeviceModeFunction hwDeviceModeFunction) {
        checkDuplicateIdentifiers(hwDeviceModeFunction);
        int rows = hwDeviceModeMapper.insertHwDeviceModeFunction(hwDeviceModeFunction);
        List<HwDeviceModeFunction> hwDeviceModeFunctions = new ArrayList<>();
        hwDeviceModeFunctions.add(hwDeviceModeFunction);
        batchInsertHwDeviceModeParameters(hwDeviceModeFunctions);

        this.addTdSuperTableColumn(hwDeviceModeFunction);

        return hwDeviceModeFunction.getModeFunctionId();
    }

    /**
     * 修改设备模型功能
     *
     * @param hwDeviceModeFunction 设备模型功能
     * @return 结果
     */
    @Override
    @Transactional
    public int updateHwDeviceModeFunction(HwDeviceModeFunction hwDeviceModeFunction) {
        //查询是否有已发布的设备关联此设备模型
        HwDevice queryHwDevice = new HwDevice();
        queryHwDevice.setDeviceModeId(hwDeviceModeFunction.getDeviceModeId());
        queryHwDevice.setDeviceStatus(HwDictConstants.DEVICE_STATUS_PUBLISH);
        List<HwDevice> hwDevices = hwDeviceMapper.selectHwDeviceList(queryHwDevice);
        if (hwDevices.size() > 0) {
            throw new ServiceException("已有已发布状态设备关联，不能修改");
        }

        checkDuplicateIdentifiers(hwDeviceModeFunction);
        hwDeviceModeMapper.deleteHwDeviceModeParameterByModeFunctionId(hwDeviceModeFunction.getModeFunctionId());
        List<HwDeviceModeFunction> hwDeviceModeFunctions = new ArrayList<>();
        hwDeviceModeFunctions.add(hwDeviceModeFunction);
        batchInsertHwDeviceModeParameters(hwDeviceModeFunctions);

        //与数据库中的数据判断标识符有没有修改，如果修改则在tdengine超级表删除老的字段，增加修改的字段
        String functionMode = hwDeviceModeFunction.getFunctionMode();
        if (functionMode.equals(HwDictConstants.FUNCTION_MODE_ATTRIBUTE)) {
            HwDeviceModeFunction dbHwDeviceModeFunction = hwDeviceModeFunctionMapper
                    .selectHwDeviceModeFunctionByModeFunctionId(hwDeviceModeFunction.getModeFunctionId());

            String dbFunctionIdentifier = dbHwDeviceModeFunction.getFunctionIdentifier();
            String functionIdentifier = hwDeviceModeFunction.getFunctionIdentifier();
            Long dbDataType = dbHwDeviceModeFunction.getDataType();
            Long dataType = hwDeviceModeFunction.getDataType();
            //标识符或数据类型变化时需要先删除超级表column，再增加新的column
            if (!dbFunctionIdentifier.equalsIgnoreCase(functionIdentifier)
                    || !dbDataType.equals(dataType)) {
                this.dropTdSuperTableColumn(dbHwDeviceModeFunction);
                this.addTdSuperTableColumn(hwDeviceModeFunction);
            }
        }

        return hwDeviceModeMapper.updateHwDeviceModeFunction(hwDeviceModeFunction);
    }

    /**
     * 删除设备模型功能信息
     *
     * @param modeFunctionId 设备模型功能主键
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteHwDeviceModeFunctionByModeFunctionId(Long modeFunctionId) {
        HwDeviceModeFunction hwDeviceModeFunction = hwDeviceModeFunctionMapper.selectHwDeviceModeFunctionByModeFunctionId(modeFunctionId);
        hwDeviceModeMapper.deleteHwDeviceModeParameterByModeFunctionId(modeFunctionId);
        int rows = hwDeviceModeMapper.deleteHwDeviceModeFunctionByModeFunctionId(modeFunctionId);
        this.dropTdSuperTableColumn(hwDeviceModeFunction);
        return rows;
    }


    /**
     * @return Map<String, List < HwDeviceModeParameter>>
     * @param: modeFunctionId
     * @description 根据设备功能主键获取参数信息列表(入参和出参按map返回)
     * @author xins
     * @date 2023-09-12 10:45
     */
    @Override
    public Map<String, List<HwDeviceModeParameter>> selectDeviceModeParametersByModeFunctionId(Long modeFunctionId) {
        List<HwDeviceModeParameter> hwDeviceModeParameters = hwDeviceModeMapper.selectDeviceModeParametersByModeFunctionId(modeFunctionId);
        Map<String, List<HwDeviceModeParameter>> hwDeviceModeParametersMap = hwDeviceModeParameters.stream()
                .collect(Collectors.groupingBy(HwDeviceModeParameter::getParameterType));
        return hwDeviceModeParametersMap;
    }

    /**
     * @param: hwDeviceModeFunction
     * @description 在编辑设备模型添加功能或者编辑功能时判断属性值
     * @author xins
     * @date 2023-09-13 13:38
     */
    private void checkDuplicateIdentifiers(HwDeviceModeFunction hwDeviceModeFunction) {
        if (hwDeviceModeFunction.getFunctionIdentifier().equalsIgnoreCase(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME)) {
            throw new ServiceException("标识符不能等于:" + TdEngineConstants.DEFAULT_FIRST_FIELD_NAME);
        }

        R<String> keyLongitudeR = remoteConfigService.getConfigKeyStr("hw.gps.longitude");
        R<String> keyLatitudeR = remoteConfigService.getConfigKeyStr("hw.gps.latitude");
        String keyLongitude = keyLongitudeR.getData();
        String keyLatitude = keyLatitudeR.getData();

        if (StringUtils.isEmpty(hwDeviceModeFunction.getCoordinate()) &&
                (hwDeviceModeFunction.getFunctionIdentifier().equalsIgnoreCase(keyLongitude)
                        || hwDeviceModeFunction.getFunctionIdentifier().equalsIgnoreCase(keyLatitude))) {
            throw new ServiceException("非定位设备模型标识符不能等于:" + keyLongitude + "或" + keyLatitude);
        }

        Long deviceModeId = hwDeviceModeFunction.getDeviceModeId();
        HwDeviceModeFunction queryDeviceModeFunction = new HwDeviceModeFunction();
        queryDeviceModeFunction.setDeviceModeId(deviceModeId);
        List<HwDeviceModeFunction> hwDeviceModeFunctions = hwDeviceModeFunctionMapper.selectHwDeviceModeFunctionList(queryDeviceModeFunction);

        /**
         * 判断时如果是修改的标识符则不能跟自己比较
         */
        long duplicateCount = hwDeviceModeFunctions.stream().filter(dmf ->
                ((hwDeviceModeFunction.getModeFunctionId() == null) ||
                        (hwDeviceModeFunction.getModeFunctionId() != null && !hwDeviceModeFunction.getModeFunctionId().equals(dmf.getModeFunctionId())))
                        && dmf.getFunctionIdentifier().equalsIgnoreCase(hwDeviceModeFunction.getFunctionIdentifier())).count();
        if (duplicateCount > 0) {
            throw new ServiceException("标识符重复");
        }
    }

    private void createTdSuperTable(HwDeviceMode hwDeviceMode) {
        TdSuperTableVo tdSuperTableVo = new TdSuperTableVo();
        Long sceneId = hwDeviceMode.getSceneId();
        String dbName = TdEngineConstants.DEFAULT_DB_NAME_PREFIX + sceneId;
        String superTableName = TdEngineConstants.DEFAULT_SUPER_TABLE_NAME_PREFIX + hwDeviceMode.getDeviceModeId();

        List<TdField> tagFields = new ArrayList<TdField>();
        TdField tagField = new TdField();
        tagField.setFieldName(TdEngineConstants.ST_TAG_DEVICECODE);
        tagField.setDataTypeCode(TdEngineConstants.ST_TAG_DEVICECODE_TYPE);
        tagField.setSize(TdEngineConstants.ST_TAG_DEVICECODE_SIZE);
        tagFields.add(tagField);

        tagField = new TdField();
        tagField.setFieldName(TdEngineConstants.ST_TAG_DEVICENAME);
        tagField.setDataTypeCode(TdEngineConstants.ST_TAG_DEVICENAME_TYPE);
        tagField.setSize(TdEngineConstants.ST_TAG_DEVICENAME_SIZE);
        tagFields.add(tagField);

        tagField = new TdField();
        tagField.setFieldName(TdEngineConstants.ST_TAG_DEVICEID);
        tagField.setDataTypeCode(TdEngineConstants.ST_TAG_DEVICEID_TYPE);
        tagFields.add(tagField);

        tagField = new TdField();
        tagField.setFieldName(TdEngineConstants.ST_TAG_DEVICEMODEID);
        tagField.setDataTypeCode(TdEngineConstants.ST_TAG_DEVICEMODEID_TYPE);
        tagFields.add(tagField);

        tagField = new TdField();
        tagField.setFieldName(TdEngineConstants.ST_TAG_MONITORUNITID);
        tagField.setDataTypeCode(TdEngineConstants.ST_TAG_MONITORUNITID_TYPE);
        tagFields.add(tagField);

        List<TdField> schemaFields = new ArrayList<TdField>();
        List<HwDeviceModeFunction> hwDeviceModeFunctions = hwDeviceMode.getHwDeviceModeFunctionList();
        TdField schemaField;
        for (HwDeviceModeFunction hwDeviceModeFunction : hwDeviceModeFunctions) {
            String functionMode = hwDeviceModeFunction.getFunctionMode();
            if (functionMode.equalsIgnoreCase(HwDictConstants.FUNCTION_MODE_ATTRIBUTE)) {
                schemaField = new TdField();
                schemaField.setFieldName(hwDeviceModeFunction.getFunctionIdentifier());
                Long dataType = hwDeviceModeFunction.getDataType();
                schemaField.setDataTypeCode(dataType.intValue());
                if (String.valueOf(dataType).equals(String.valueOf(DataTypeEnums.NCHAR.getDataCode()))) {
                    schemaField.setSize(Integer.valueOf(hwDeviceModeFunction.getDataDefinition()));
                }
                schemaFields.add(schemaField);
            }
        }

        tdSuperTableVo.setDatabaseName(dbName);
        tdSuperTableVo.setSuperTableName(superTableName);
        tdSuperTableVo.setFirstFieldName(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME);
        tdSuperTableVo.setSchemaFields(schemaFields);
        tdSuperTableVo.setTagsFields(tagFields);

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

    private void addTdSuperTableColumn(HwDeviceModeFunction hwDeviceModeFunction) {
        String functionMode = hwDeviceModeFunction.getFunctionMode();
        if (functionMode.equals(HwDictConstants.FUNCTION_MODE_ATTRIBUTE)) {
            Long deviceModeId = hwDeviceModeFunction.getDeviceModeId();
            HwDeviceMode hwDeviceMode = hwDeviceModeMapper.selectHwDeviceModeByDeviceModeId(deviceModeId);
            String dbName = TdEngineConstants.DEFAULT_DB_NAME_PREFIX + hwDeviceMode.getSceneId();
            String superTableName = TdEngineConstants.DEFAULT_SUPER_TABLE_NAME_PREFIX + deviceModeId;
            TdSuperTableVo tdSuperTableVo = new TdSuperTableVo();
            TdField schemaField = new TdField();
            schemaField.setFieldName(hwDeviceModeFunction.getFunctionIdentifier());
            Long dataType = hwDeviceModeFunction.getDataType();
            schemaField.setDataTypeCode(dataType.intValue());
            //一个integer类型，一个long类型，需要转换为string类型比较
            if (String.valueOf(dataType).equals(String.valueOf(DataTypeEnums.NCHAR.getDataCode()))) {
                schemaField.setSize(Integer.valueOf(hwDeviceModeFunction.getDataDefinition()));
            }
            tdSuperTableVo.setDatabaseName(dbName);
            tdSuperTableVo.setSuperTableName(superTableName);
            tdSuperTableVo.setField(schemaField);
            R<?> tdReturnMsg = this.remoteTdEngineService.addSuperTableColumn(tdSuperTableVo);
            if(tdReturnMsg.getCode() != Constants.SUCCESS){//抛出异常，回滚事务
                throw new RuntimeException(tdReturnMsg.getMsg());
            }
        }
    }

    private void dropTdSuperTableColumn(HwDeviceModeFunction hwDeviceModeFunction) {
        String functionMode = hwDeviceModeFunction.getFunctionMode();
        if (functionMode.equals(HwDictConstants.FUNCTION_MODE_ATTRIBUTE)) {
            Long deviceModeId = hwDeviceModeFunction.getDeviceModeId();
            HwDeviceMode hwDeviceMode = hwDeviceModeMapper.selectHwDeviceModeByDeviceModeId(deviceModeId);
            String dbName = TdEngineConstants.DEFAULT_DB_NAME_PREFIX + hwDeviceMode.getSceneId();
            String superTableName = TdEngineConstants.DEFAULT_SUPER_TABLE_NAME_PREFIX + deviceModeId;
            TdSuperTableVo tdSuperTableVo = new TdSuperTableVo();
            TdField schemaField = new TdField();
            schemaField.setFieldName(hwDeviceModeFunction.getFunctionIdentifier());
            tdSuperTableVo.setDatabaseName(dbName);
            tdSuperTableVo.setSuperTableName(superTableName);
            tdSuperTableVo.setField(schemaField);

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

}
