package com.artfess.device.base.manager.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.artfess.base.enums.DeviceStatusEnum;
import com.artfess.base.enums.EnableStatusEnum;
import com.artfess.base.enums.ProductTypeEnum;
import com.artfess.base.manager.impl.BaseManagerImpl;
import com.artfess.base.query.PageBean;
import com.artfess.base.query.PageList;
import com.artfess.base.query.QueryFilter;
import com.artfess.base.util.BeanUtils;
import com.artfess.base.util.DateUtils;
import com.artfess.base.util.StringUtil;
import com.artfess.device.base.config.MqConstant;
import com.artfess.device.base.dao.DeviceBaseAreaDao;
import com.artfess.device.base.dao.DeviceBaseCompanyDao;
import com.artfess.device.base.dao.DeviceBaseUserDao;
import com.artfess.device.base.dao.DeviceInfoDao;
import com.artfess.device.base.dao.DeviceParamsValueDao;
import com.artfess.device.base.dao.DeviceProductInfoDao;
import com.artfess.device.base.dao.DeviceRelationContractDao;
import com.artfess.device.base.dao.DeviceStatusLogDao;
import com.artfess.device.base.manager.DeviceInfoManager;
import com.artfess.device.base.manager.DeviceVideoPointManager;
import com.artfess.device.base.manager.DeviceWarnInfoManager;
import com.artfess.device.base.model.DeviceBaseArea;
import com.artfess.device.base.model.DeviceBaseCompany;
import com.artfess.device.base.model.DeviceBaseUser;
import com.artfess.device.base.model.DeviceInfo;
import com.artfess.device.base.model.DeviceParamsValue;
import com.artfess.device.base.model.DeviceProductInfo;
import com.artfess.device.base.model.DeviceRelationContract;
import com.artfess.device.base.model.DeviceStatusLog;
import com.artfess.device.base.model.DeviceVideoPoint;
import com.artfess.device.base.utils.BizUtils;
import com.artfess.device.base.vo.CountVo;
import com.artfess.device.base.vo.DeviceCountVo;
import com.artfess.device.base.vo.FailureDeviceCountVo;
import com.artfess.device.base.vo.HomeRealTimeVo;
import com.artfess.device.base.vo.StatisticsVo;
import com.artfess.rocketmq.producer.RocketMQProducer;
import com.artfess.sysConfig.persistence.manager.SysDictionaryManager;
import com.artfess.sysConfig.persistence.param.DictModel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 设备信息 服务实现类
 *
 * @author min.wu
 * @company 阿特菲斯信息技术有限公司
 * @since 2022-07-15
 */
@Service
public class DeviceInfoManagerImpl extends BaseManagerImpl<DeviceInfoDao, DeviceInfo> implements DeviceInfoManager {

    @Resource
    private DeviceProductInfoDao deviceProductInfoDao;

    @Resource
    private DeviceRelationContractDao deviceRelationContractDao;

    @Resource
    private DeviceParamsValueDao paramsValueDao;

    @Resource
    private DeviceBaseCompanyDao companyDao;

    @Resource
    private DeviceBaseUserDao deviceBaseUserDao;

    @Resource
    private DeviceBaseAreaDao areaDao;

    @Resource
    DeviceStatusLogDao statusLogDao;

    @Autowired
    private DeviceWarnInfoManager warnInfoManager;

    @Autowired
    private RocketMQProducer rocketMQUtils;

    @Autowired
    private DeviceVideoPointManager videoPointManager;

    @Resource
    private SysDictionaryManager sdm;


    @Override
    public List<DeviceInfo> findAll(DeviceInfo entity) {
        entity.setFlag(1);
        return this.baseMapper.findByParams(entity);
    }

    @Override
    @Transactional
    public String createInfo(DeviceInfo t) {
        t.setIsLock("0");
        t.setFlag(1);
        BizUtils.checkLevel(t);

        int insert = this.baseMapper.insert(t);
        if (insert > 0) {
            List<DeviceRelationContract> contractList = t.getContractList();
            if (!CollectionUtils.isEmpty(contractList)) {
                contractList.forEach(contract -> {
                    contract.setDeviceId(t.getId());
                    deviceRelationContractDao.insert(contract);
                });
            }

            DeviceProductInfo productInfo = deviceProductInfoDao.selectById(t.getProductId());
            if (null != productInfo) {
                t.setFullName(productInfo.getFullName() + "/" + t.getName());
            }

            this.baseMapper.updateById(t);
            processParamsValue(t.getParamsValueList(), t.getId());
            return t.getId();
        }
        return null;
    }

    private void processParamsValue(List<DeviceParamsValue> paramsValueList, String deviceId) {
        QueryWrapper<DeviceParamsValue> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("device_id_", deviceId);
        paramsValueDao.delete(queryWrapper);
        if (CollectionUtils.isEmpty(paramsValueList)) {
            return;
        }
        paramsValueList.forEach(value -> {
            value.setDeviceId(deviceId);
            this.paramsValueDao.insert(value);
        });
    }

    @Override
    @Transactional
    public String updateInfo(DeviceInfo t) {
        DeviceProductInfo productInfo = deviceProductInfoDao.selectById(t.getProductId());
        if (null != productInfo) {
            t.setFullName(productInfo.getFullName() + "/" + t.getName());
        }

        BizUtils.checkLevel(t);

        this.baseMapper.updateById(t);
        processParamsValue(t.getParamsValueList(), t.getId());
        return t.getId();
    }

    @Override
    public DeviceCountVo getDeviceInfo(String productId) {

        List<DeviceCountVo> list =this.baseMapper.countNum(productId);
        if(list!=null && list.size()>0){
          return list.get(0);
        }
       /* JSONObject result = new JSONObject();
        result.p
        List<DeviceInfo> all = this.getBaseMapper().findByParams(entity);
        handleJsonResult(result, "0", all);

        Map<String, List<DeviceInfo>> deviceStatus = all.stream().collect(Collectors.groupingBy(DeviceInfo::getStatus));
        for (Map.Entry<String, List<DeviceInfo>> online:deviceStatus.entrySet()) {
            handleJsonResult(result, online.getKey(), online.getValue());
        }

        Map<Boolean, List<DeviceInfo>> banStatus = all.stream().collect(Collectors.partitioningBy((e) -> 0 == e.getFlag())); // true 禁用，false启用
        for (Map.Entry<Boolean, List<DeviceInfo>> ban:banStatus.entrySet()) {
            handleJsonResult(result, ban.getKey() ?  "10" : "11", ban.getValue());
        }*/

        return null;
    }

    private void handleJsonResult(JSONObject result, String key, List<DeviceInfo> detailData){
        JSONObject data = new JSONObject();
        data.put("count", detailData.size());
        JSONArray detail = new JSONArray();
        detail.add(detailData);
        data.put("detail", detail);
        result.put(key, data);
    }

    @Override
    public boolean modifyEnabled(DeviceInfo t) {

        DeviceInfo entity = getById(t.getId());
        if (null == entity) {
            return false;
        }
        
        BizUtils.checkLevel(entity);

        entity.setFlag(EnableStatusEnum.Y.getType().equals(entity.getFlag()) ? EnableStatusEnum.N.getType() : EnableStatusEnum.Y.getType());
        entity.setUpdateTime(LocalDateTime.now());
        boolean b = this.saveOrUpdate(entity);
        return b;
    }

    @Override
    @Transactional
    public Boolean updateDeviceStatus(String deviceCode, String deviceType, String status) {
        Assert.hasText(deviceCode, "设备标识不能为空！");
        Assert.hasText(status, "设备状态不能为空！");
        Integer result = null;
        //修改设备状态
        QueryWrapper<DeviceInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("code_", deviceCode);
        DeviceInfo deviceInfo = this.baseMapper.selectOne(queryWrapper);
        //状态改变了就要修改状态和修改最后一条日志的结束时间和添加新状态的日志
        if (deviceInfo != null && !deviceInfo.getStatus().equals(status)) {
            deviceInfo.setStatus(status);
            deviceInfo.setLastTime(LocalDateTime.now());
            result = this.baseMapper.updateById(deviceInfo);
        }
        //先查询日志表中是否有日志数据
        QueryWrapper<DeviceStatusLog> statusWrapper = new QueryWrapper<>();
        statusWrapper.eq("device_code_", deviceCode);
        statusWrapper.eq("end_time_", "9999-12-31 23:59:59");
        DeviceStatusLog deviceStatusLog = statusLogDao.selectOne(statusWrapper);
        if (deviceStatusLog != null) { //有日志数据就判断状态是否改变
            //先修改最后一条状态日志的结束时间
            LocalDateTime times = LocalDateTime.now();
            if (!deviceStatusLog.getDeviceStatus().equals(status)) {
                Duration duration = Duration.between(deviceStatusLog.getStartTime(), times);
                deviceStatusLog.setTimeLength(duration.toMinutes());
                deviceStatusLog.setEndTime(times);
                deviceStatusLog.setLastTime(LocalDateTime.now());
                statusLogDao.updateById(deviceStatusLog);

                //新建一条当前状态日志数据
                DeviceStatusLog newDeviceStatusLog = new DeviceStatusLog();
                newDeviceStatusLog.setDeviceCode(deviceCode);
                newDeviceStatusLog.setDeviceStatus(status);
                newDeviceStatusLog.setDeviceType(deviceType);
                newDeviceStatusLog.setStartTime(times);
                newDeviceStatusLog.setEndTime(LocalDateTime.of(9999, 12, 31, 23, 59, 59));
                statusLogDao.insert(newDeviceStatusLog);
            }
            //发送设备的状态到MQ
            Map<String, Object> deviceStatus = new HashMap<>();
            deviceStatus.put("deviceCode", deviceCode);
            deviceStatus.put("proType", deviceType);
            deviceStatus.put("status", status);
            String deviceStatusString = JSON.toJSONString(deviceStatus);
            try {
                rocketMQUtils.send(MqConstant.SEND_STATUS_TOPIC, deviceStatusString);
            } catch (Exception e) {
                log.error("设备状态发送失败:{}", e);
            }
        } else {
            //没有日志数据就添加一条
            DeviceStatusLog newDeviceStatusLog = new DeviceStatusLog();
            newDeviceStatusLog.setDeviceCode(deviceCode);
            newDeviceStatusLog.setDeviceStatus(status);
            newDeviceStatusLog.setDeviceType(deviceType);
            newDeviceStatusLog.setStartTime(LocalDateTime.now());
            newDeviceStatusLog.setEndTime(LocalDateTime.of(9999, 12, 31, 23, 59, 59));
            statusLogDao.insert(newDeviceStatusLog);
            //发送设备的状态到MQ
            Map<String, Object> deviceStatus = new HashMap<>();
            deviceStatus.put("deviceCode", deviceCode);
            deviceStatus.put("proType", deviceType);
            deviceStatus.put("status", status);
            String deviceStatusString = JSON.toJSONString(deviceStatus);
            try {
                rocketMQUtils.send(MqConstant.SEND_STATUS_TOPIC, deviceStatusString);
            } catch (Exception e) {
                log.error("设备状态发送失败:{}", e);
            }
        }

        return null != result && result >= 1;
    }

    @Override
    public DeviceInfo findById(String id) {
        DeviceInfo deviceInfo = this.baseMapper.selectById(id);
        if (null == deviceInfo) {
            return null;
        }

        QueryWrapper<DeviceParamsValue> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("device_id_", id);
        List<DeviceParamsValue> deviceParamsValues = paramsValueDao.selectList(queryWrapper);
        deviceInfo.setParamsValueList(deviceParamsValues);
        return deviceInfo;
    }

    @Override
    public PageList<DeviceInfo> findByProductType(QueryFilter<DeviceInfo> queryFilter) {
        PageBean pageBean = queryFilter.getPageBean();
        Class<DeviceInfo> currentModelClass = currentModelClass();
        IPage<DeviceInfo> result = baseMapper.findByProductType(convert2IPage(pageBean), convert2Wrapper(queryFilter, currentModelClass));
        return new PageList<DeviceInfo>(result);
    }

    @Override
    public DeviceInfo detail(String id) {
        DeviceInfo deviceInfo = this.baseMapper.selectById(id);
        if (null == deviceInfo) {
            return null;
        }

        DeviceProductInfo productInfo = deviceProductInfoDao.selectById(deviceInfo.getProductId());
        if (null != productInfo) {
            deviceInfo.setProductName(productInfo.getName());
        }

        DeviceBaseCompany deviceBaseCompany = companyDao.selectById(deviceInfo.getSupplierCompanyId());
        if (null != deviceBaseCompany) {
            deviceInfo.setCompanyName(deviceBaseCompany.getCompanyName());
        }


        DeviceBaseUser deviceBaseUser = deviceBaseUserDao.selectById(deviceInfo.getManagerUserId());
        if (null != deviceBaseUser) {
            deviceInfo.setManagerUserName(deviceBaseUser.getName());
        }

        deviceBaseUser = deviceBaseUserDao.selectById(deviceInfo.getRepairUserId());
        if (null != deviceBaseUser) {
            deviceInfo.setRepairUserName(deviceBaseUser.getName());
        }

        DeviceBaseArea deviceBaseArea = areaDao.selectById(deviceInfo.getAreaId());

        if (null != deviceBaseArea) {
            deviceInfo.setAreaName(deviceBaseArea.getName());
        }

        String addressName = baseMapper.getAreaName(deviceInfo.getAddvcd());
        deviceInfo.setAddressName(addressName);
        return deviceInfo;
    }

    @Override
    public List<CountVo> statistics(String productType) {
        List<DeviceInfo> deviceInfos = this.baseMapper.findDeviceList(productType);

        Map<String, List<DeviceInfo>> deviceMap = deviceInfos.stream().filter(base -> !StringUtils.isEmpty(base.getStatus())).collect(Collectors.groupingBy(DeviceInfo::getStatus));

        List<CountVo> data = Lists.newArrayList();
        for (DeviceStatusEnum type : DeviceStatusEnum.values()) {
            CountVo countVo = new CountVo();
            if (deviceMap.containsKey(type.getType())) {
                countVo.setCount(deviceMap.get(type.getType()).size());
            } else {
                countVo.setCount(0);
            }
            countVo.setStatusName(type.getDesc());
            data.add(countVo);
        }


        Integer warnCount = warnInfoManager.findByDeviceType(ProductTypeEnum.getType(productType));

        CountVo countVo = new CountVo();
        countVo.setCount(warnCount);
        countVo.setStatusName("告警数");
        data.add(countVo);

        return data;
    }

    @Override
    public PageList<FailureDeviceCountVo> failureStatistics(QueryFilter<DeviceInfo> queryFilter) {

        PageBean pageBean = queryFilter.getPageBean();
        Class<DeviceInfo> currentModelClass = currentModelClass();
        IPage<FailureDeviceCountVo> result = baseMapper.failureStatistics(convert2IPage(pageBean), convert2Wrapper(queryFilter, currentModelClass));
        return new PageList<FailureDeviceCountVo>(result);
    }

    @Override
    public PageList<DeviceInfo> findByPage(QueryFilter<DeviceInfo> queryFilter) {
        PageBean pageBean = queryFilter.getPageBean();
        Class<DeviceInfo> currentModelClass = currentModelClass();
        IPage<DeviceInfo> result = baseMapper.findByPage(convert2IPage(pageBean), convert2Wrapper(queryFilter, currentModelClass));
        return new PageList<DeviceInfo>(result);
    }

    @Override
    public PageList<DeviceInfo> failureEquipment(QueryFilter<DeviceInfo> queryFilter) {
        PageBean pageBean = queryFilter.getPageBean();
        QueryWrapper<DeviceInfo> wrapper = convert2Wrapper(queryFilter, currentModelClass());
        wrapper.eq("dwi.WARN_TYPE_", "1");
        IPage<DeviceInfo> result = baseMapper.failureEquipment(convert2IPage(pageBean), wrapper);
        return new PageList<DeviceInfo>(result);
    }

    @Override
    public List<StatisticsVo> homeStatistics() {
        List<StatisticsVo> result = Lists.newArrayList();

        List<DictModel> deviceStatus = sdm.queryDictListItemsByCode("sbzt");

        Map<String, List<DeviceInfo>> deviceInfos = list().stream().collect(Collectors.groupingBy(DeviceInfo::getStatus));
        List<DeviceVideoPoint> v = videoPointManager.list();
        Map<String, List<DeviceVideoPoint>> videoPoints = v.stream().collect(Collectors.groupingBy(DeviceVideoPoint::getStatus));

        Integer total = v.isEmpty() ? 0 : v.size();
        for (Map.Entry<String, List<DeviceInfo>> d : deviceInfos.entrySet()) {
            StatisticsVo dTemp = new StatisticsVo();
            dTemp.setStatus(BizUtils.getDicValueByCode(deviceStatus, d.getKey()));
            dTemp.setCount(d.getValue().size());
            result.add(dTemp);
            total += d.getValue().size();
        }

        result.forEach(r -> {
            if ("正常".equals(r.getStatus()) && BeanUtils.isNotEmpty(videoPoints.get("1"))) {
                r.setCount(r.getCount() + videoPoints.get("1").size());
            }
            if ("故障".equals(r.getStatus()) && BeanUtils.isNotEmpty(videoPoints.get("0"))) {
                r.setCount(r.getCount() + videoPoints.get("0").size());
            }
        });

        result.add(new StatisticsVo("总数", total));

        return result;
    }

    @Override
    public List<StatisticsVo> currentStatusAnalyze() {
        List<StatisticsVo> result = Lists.newArrayList();

        List<DictModel> deviceStatus = sdm.queryDictListItemsByCode("sbzt");
        List<DictModel> videostatus = sdm.queryDictListItemsByCode("spdwzt");

        Map<String, Map<String, List<DeviceInfo>>> data = baseMapper.currentStatusAnalyze().stream().collect(Collectors.groupingBy(DeviceInfo::getName, Collectors.groupingBy(DeviceInfo::getStatus)));

        for (Map.Entry<String, Map<String, List<DeviceInfo>>> d : data.entrySet()) {
            StatisticsVo dTemp = new StatisticsVo(d.getKey());
            List<StatisticsVo> subData = Lists.newArrayList();
            boolean isVideo = "摄像头".equals(d.getKey());
            if (isVideo) {
                d.setValue(BizUtils.sortByKey(d.getValue(), true));
            }
            for (Map.Entry<String, List<DeviceInfo>> s : d.getValue().entrySet()) {
                String status = BizUtils.getDicValueByCode(isVideo ? videostatus : deviceStatus, s.getKey());
                subData.add(new StatisticsVo(status, s.getValue().size()));
            }
            dTemp.setData(subData);
            result.add(dTemp);
        }

        return result;
    }

    @Override
    public List<StatisticsVo> monthlyAnalyze(String code) {
        Assert.isTrue(StringUtil.isNotEmpty(code), "参数不能为空！");

        List<DictModel> deviceStatus = sdm.queryDictListItemsByCode("sbzt");
        List<DictModel> videostatus = sdm.queryDictListItemsByCode("spdwzt");

        List<StatisticsVo> result = Lists.newArrayList();
        Map<String, Map<String, List<StatisticsVo>>> data = ("video".equals(code) ? baseMapper.monthlyVideoAnalyze() : baseMapper.monthlyAnalyze(code)).
                stream().collect(Collectors.groupingBy(StatisticsVo::getTime, Collectors.groupingBy(StatisticsVo::getStatus)));

        for (Map.Entry<String, Map<String, List<StatisticsVo>>> d : data.entrySet()) {
            List<StatisticsVo> statusData = Lists.newArrayList();
            for (Map.Entry<String, List<StatisticsVo>> s : d.getValue().entrySet()) {
                String status = BizUtils.getDicValueByCode("video".equals(code) ? videostatus : deviceStatus, s.getKey());
                statusData.add(new StatisticsVo(status, s.getValue().size()));
            }
            result.add(new StatisticsVo(d.getKey(), statusData));
        }

        for (int i = 0; i <= 31; i++) {
            String date = DateUtils.formatSubDayDate(i);
            if(!result.stream().filter(r->r.getTime().equals(date)).findAny().isPresent()){
                result.add(new StatisticsVo(date, Lists.newArrayList()));
            }
        }

        result = result.stream().sorted(Comparator.comparing(StatisticsVo::getTime)).collect(Collectors.toList());

        return result;
    }

    @Override
    public List<HomeRealTimeVo> homeRealTime(String code) {
        String table = ProductTypeEnum.getTable(code);
        Assert.isTrue(StringUtil.isNotEmpty(table), "非法 code！");
        return baseMapper.homeRealTime(table, code);
    }
}
