package cn.iocoder.yudao.module.system.service.information;

import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.constant.device.DeviceChcheConstant;
import cn.iocoder.yudao.module.system.controller.admin.information.dto.DeviceInformationDTO;
import cn.iocoder.yudao.module.system.controller.admin.information.dto.MapBindDeviceInfoDTO;
import cn.iocoder.yudao.module.system.controller.admin.information.dto.StatisticsInformationDTO;
import cn.iocoder.yudao.module.system.controller.admin.information.vo.DeviceInformationPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.information.vo.DeviceInformationRespVO;
import cn.iocoder.yudao.module.system.controller.admin.information.vo.DeviceInformationSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO;
import cn.iocoder.yudao.module.system.dal.dataobject.information.DeviceInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.positionmap.PositionMapDO;
import cn.iocoder.yudao.module.system.dal.mysql.information.DeviceInformationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.positionmap.PositionMapMapper;
import cn.iocoder.yudao.module.system.enums.device.DeviceStatusEnum;
import cn.iocoder.yudao.module.system.enums.device.PictureConfigEnum;
import cn.iocoder.yudao.module.system.service.dict.DictDataService;
import cn.iocoder.yudao.module.system.util.redis.RedisUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.hutool.core.collection.CollUtil.isNotEmpty;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.INFORMATION_MAC_EXIST;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.INFORMATION_NOT_EXISTS;

/**
 * 设备信息 Service 实现类
 *
 * @author 陈宾顺
 */
@Service
@Validated
@Slf4j
public class DeviceInformationServiceImpl extends ServiceImpl<DeviceInformationMapper, DeviceInformationDO> implements DeviceInformationService {

    @Resource
    private DeviceInformationMapper informationMapper;

    @Resource
    private DictDataService dictDataService;

    @Resource
    private PositionMapMapper positionMapMapper;

    @Resource
    private RedisUtil redisUtil;

    @Override
    public Long createInformation(DeviceInformationSaveReqVO createReqVO) {
        // -- 先判断库里是否有相通的mac地址数据
        long count = this.count(new LambdaQueryWrapper<DeviceInformationDO>().eq(DeviceInformationDO::getMacAddress, createReqVO.getMacAddress()));
        if (count > 0L) {
            throw exception(INFORMATION_MAC_EXIST);
        }

        //默认图片
        if (PictureConfigEnum.DEFAULT.getType().equals(createReqVO.getPictureConfig())) {
            List<DictDataDO> deviceType = dictDataService.getDictDataListByDictType("device_type");
            DictDataDO dictDataDO = deviceType.stream()
                    .filter(v -> Integer.valueOf(v.getValue()).equals(createReqVO.getDeviceType()))
                    .findFirst()
                    .orElse(new DictDataDO());
            createReqVO.setUrl(dictDataDO.getRemark());
        }

        // 插入
        DeviceInformationDO information = BeanUtils.toBean(createReqVO, DeviceInformationDO.class);
        information.setDeviceStatus(1);
        informationMapper.insert(information);
        // 返回
        return information.getId();
    }

    @Override
    public void updateInformation(DeviceInformationSaveReqVO updateReqVO) {
        // 校验存在
        validateInformationExists(updateReqVO.getId());
        //默认图片
        if (PictureConfigEnum.DEFAULT.getType().equals(updateReqVO.getPictureConfig())) {
            List<DictDataDO> deviceType = dictDataService.getDictDataListByDictType("device_type");
            DictDataDO dictDataDO = deviceType.stream()
                    .filter(v -> Integer.valueOf(v.getValue()).equals(updateReqVO.getDeviceType()))
                    .findFirst()
                    .orElse(new DictDataDO());
            updateReqVO.setUrl(dictDataDO.getRemark());
        }
        // 更新
        DeviceInformationDO updateObj = BeanUtils.toBean(updateReqVO, DeviceInformationDO.class);
        informationMapper.updateById(updateObj);
    }

    @Override
    public void deleteInformation(Long id) {
        // 校验存在
        validateInformationExists(id);
        // 删除
        informationMapper.deleteById(id);
    }

    private void validateInformationExists(Long id) {
        if (informationMapper.selectById(id) == null) {
            throw exception(INFORMATION_NOT_EXISTS);
        }
    }

    @Override
    public DeviceInformationDO getInformation(Long id) {
        // 校验存在
        validateInformationExists(id);
        DeviceInformationDO deviceInformationDO = informationMapper.selectById(id);
        String deviceKey = DeviceChcheConstant.DEVICE_LAST_TIME + deviceInformationDO.getMacAddress();
        Object object = redisUtil.get(deviceKey);
        if (ObjectUtil.isEmpty(object)) {
            deviceInformationDO.setDeviceStatus(DeviceStatusEnum.OFF_LINE.getType());
            return deviceInformationDO;
        }
        try {
            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime curTimeLDT = LocalDateTime.parse((String) object, df);
            deviceInformationDO.setDeviceLastTime(curTimeLDT);
            deviceInformationDO.setDeviceStatus(DeviceStatusEnum.ON_LINE.getType());
        } catch (Exception e) {
            log.info("查询设备最后通讯时间异常 :{}", e.getMessage());
        }
        return deviceInformationDO;
    }

    @Override
    public PageResult<DeviceInformationDO> getInformationPage(DeviceInformationPageReqVO pageReqVO) {
        return informationMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchEditOrDel(List<List<DeviceInformationDO>> list) {
        //批量添加、修改、删除
        List<DeviceInformationDO> editList = new ArrayList<>();
        if (isNotEmpty(list.get(0)) || isNotEmpty(list.get(1))) {
            editList.addAll(list.get(0));
            editList.addAll(list.get(1));
        }
        if (isNotEmpty(list.get(2))) {
            list.get(2).forEach(a -> a.setPositionMapItemId(null));
            editList.addAll(list.get(2));
        }
        if (isNotEmpty(editList)){
            informationMapper.updateBatch(editList);
        }
    }

    @Override
    public List<DeviceInformationDO> getByMapId(Long positionMapId) {
        return informationMapper.selectList(new LambdaQueryWrapperX<DeviceInformationDO>()
                .eq(DeviceInformationDO::getPositionMapId, positionMapId));
    }

    @Override
    public List<DeviceInformationDO> getList(DeviceInformationDTO dto) {
        return informationMapper.selectList(new LambdaQueryWrapper<DeviceInformationDO>()
                .isNull(dto.getUnboundFlag() != null && dto.getUnboundFlag() == 1, DeviceInformationDO::getPositionMapId)
                .eq(dto.getPositionMapId() != null, DeviceInformationDO::getPositionMapId, dto.getPositionMapId())
                .eq(dto.getDeviceType() != null, DeviceInformationDO::getDeviceType, dto.getDeviceType())
                .eq(dto.getDeviceStatus() != null, DeviceInformationDO::getDeviceStatus, dto.getDeviceStatus())
                .eq(dto.getDeviceEnable() != null, DeviceInformationDO::getDeviceEnable, dto.getDeviceEnable())
        );
    }

    /**
     * 获得设备信息分类列表
     *
     * @return
     */
    @Override
    public Map<Integer, List<DeviceInformationDO>> classification() {
        List<DeviceInformationDO> deviceInformationDOS = informationMapper.selectList(new LambdaQueryWrapper<DeviceInformationDO>());
        if (ObjectUtil.isEmpty(deviceInformationDOS)) {
            return new HashMap<>();
        }

        // todo 需要设置设备通讯的时间
        for (DeviceInformationDO deviceInformationDO : deviceInformationDOS) {
            String deviceKey = DeviceChcheConstant.DEVICE_LAST_TIME + deviceInformationDO.getMacAddress();
            Object object = redisUtil.get(deviceKey);
            if (ObjectUtil.isEmpty(object)) {
                deviceInformationDO.setDeviceStatus(DeviceStatusEnum.OFF_LINE.getType());
                continue;
            }
            try {
                DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                LocalDateTime curTimeLDT = LocalDateTime.parse((String) object, df);
                deviceInformationDO.setDeviceLastTime(curTimeLDT);
                deviceInformationDO.setDeviceStatus(DeviceStatusEnum.ON_LINE.getType());
            } catch (Exception e) {
                log.info("查询设备最后通讯时间异常 :{}", e.getMessage());
            }
        }

        Map<Integer, List<DeviceInformationDO>> collect =
                deviceInformationDOS.stream()
                        .collect(Collectors.groupingBy(DeviceInformationDO::getDeviceType));

        ArrayList<Map.Entry<Integer, List<DeviceInformationDO>>> list = new ArrayList<Map.Entry<Integer, List<DeviceInformationDO>>>(collect.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<Integer, List<DeviceInformationDO>>>() {
            public int compare(Map.Entry<Integer, List<DeviceInformationDO>> o1, Map.Entry<Integer, List<DeviceInformationDO>> o2) {
                return o1.getKey().compareTo(o2.getKey()); // 按照值排序
            }
        });

        return collect;
    }

    /**
     * 统计设备
     *
     * @return
     */
    @Override
    public List<StatisticsInformationDTO> getDeviceNumber() {
        List<DeviceInformationDO> deviceInformationDOS = informationMapper.selectList(new LambdaQueryWrapper<DeviceInformationDO>());
        if (ObjectUtil.isEmpty(deviceInformationDOS)) {
            return new ArrayList<>();
        }

        List<StatisticsInformationDTO> list = deviceInformationDOS.stream()
                .filter(v -> ObjectUtil.isNotEmpty(v.getDeviceType()))
                .collect(Collectors.groupingBy(DeviceInformationDO::getDeviceType))
                .entrySet().stream()
                .map(v -> {
                    Integer key = v.getKey();
                    int number = v.getValue().size();
                    StatisticsInformationDTO statisticsInformationDTO = new StatisticsInformationDTO();
                    statisticsInformationDTO.setDeviceType(key);
                    statisticsInformationDTO.setNumber(number);
                    return statisticsInformationDTO;
                }).collect(Collectors.toList());

        return list;
    }

    @Override
    public List<DeviceInformationRespVO> getInformationList(DeviceInformationPageReqVO dto) {

        List<DeviceInformationDO> deviceInformationDOS = informationMapper.selectList(new LambdaQueryWrapper<DeviceInformationDO>()
                .like(dto.getDeviceNo() != null, DeviceInformationDO::getDeviceNo, dto.getDeviceNo())
                .eq(dto.getDeviceType() != null, DeviceInformationDO::getDeviceType, dto.getDeviceType())
        );

        if (ObjectUtil.isEmpty(deviceInformationDOS)) {
            return new ArrayList<>();
        }

        List<PositionMapDO> positionMapDOS = positionMapMapper.selectList(new LambdaQueryWrapperX<PositionMapDO>());
        Map<Long, PositionMapDO> positionMap =
                positionMapDOS.stream().collect(Collectors.toMap(v -> v.getId(), Function.identity()));

        List<DeviceInformationRespVO> beans = BeanUtils.toBean(deviceInformationDOS, DeviceInformationRespVO.class);
        // todo 需要设置设备通讯的时间
        for (DeviceInformationRespVO deviceInformationDO : beans) {

            if (ObjectUtil.isNotEmpty(deviceInformationDO.getPositionMapId())
                    && ObjectUtil.isNotEmpty(positionMap.get(deviceInformationDO.getPositionMapId()))) {
                deviceInformationDO.setFloor(positionMap.get(deviceInformationDO.getPositionMapId()).getFloor()+"");
                deviceInformationDO.setArea(positionMap.get(deviceInformationDO.getPositionMapId()).getArea());
            }

            String deviceKey = DeviceChcheConstant.DEVICE_LAST_TIME + deviceInformationDO.getMacAddress();
            Object object = redisUtil.get(deviceKey);
            if (ObjectUtil.isEmpty(object)) {
                deviceInformationDO.setDeviceStatus(DeviceStatusEnum.OFF_LINE.getType());
                continue;
            }
            try {
                DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                LocalDateTime curTimeLDT = LocalDateTime.parse((String) object, df);
                deviceInformationDO.setDeviceLastTime(curTimeLDT);
                deviceInformationDO.setDeviceStatus(DeviceStatusEnum.ON_LINE.getType());
            } catch (Exception e) {
                log.info("查询设备最后通讯时间异常 :{}", e.getMessage());
            }
        }

        return beans;
    }

    @Override
    public List<DeviceInformationDO> getDeviceInfoBindByMapId(Long positionMapId) {
        return informationMapper.selectList(new LambdaQueryWrapperX<DeviceInformationDO>()
                .eq(DeviceInformationDO::getPositionMapId, positionMapId)
                .isNotNull(DeviceInformationDO::getPositionMapItemId));
    }

    @Override
    public void mapBindDeviceInfo(MapBindDeviceInfoDTO dto) {
        DeviceInformationDO deviceInformationDO = informationMapper.selectById(dto.getDeviceInfoId());
        if (dto.getType() == 1) {
            deviceInformationDO.setPositionMapId(dto.getPositionMapId());
        } else {
            deviceInformationDO.setPositionMapId(null);
            deviceInformationDO.setPositionMapItemId(null);
        }
        informationMapper.updateById(deviceInformationDO);
    }
}
