package com.tofly.xrtygis.custom.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tofly.common.core.util.DateUtil;
import com.tofly.common.oauth.exception.ToflyDeniedException;
import com.tofly.xrtygis.config.ScadaIndexBean;
import com.tofly.xrtygis.custom.entity.dto.StaitemDto;
import com.tofly.xrtygis.custom.entity.dto.StanderdDto;
import com.tofly.xrtygis.custom.entity.dto.StationDto;
import com.tofly.xrtygis.custom.entity.dto.TimeOutSectionDto;
import com.tofly.xrtygis.custom.mapper.CustomDisplayMapper;
import com.tofly.xrtygis.custom.service.CustomScadaAboutService;
import com.tofly.xrtygis.custom.util.CommonMethod;
import com.tofly.xrtygis.em.Constant;
import com.tofly.xrtygis.em.ExcelExportFileTypeEnum;
import com.tofly.xrtygis.entity.Scadahistory;
import com.tofly.xrtygis.entity.Scadastaitem;
import com.tofly.xrtygis.entity.Scadastation;
import com.tofly.xrtygis.service.ScadastaitemService;
import com.tofly.xrtygis.service.ScadastationService;
import com.tofly.xrtygis.strategy.ExcelStrategyContext;
import com.tofly.xrtygis.util.ExcelUtil;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * scada相关
 *
 * @author 陈浩
 */
@Service
@AllArgsConstructor
public class CustomScadaAboutServiceImpl implements CustomScadaAboutService {

    private static final Integer MAX_VALUE = 2 << 10;
    private static final Integer MIN_VALUE = -2 << 10;
    private final ScadastationService scadastationService;
    private final ScadastaitemService scadastaitemService;
    private final CustomDisplayMapper customDisplayMapper;
    private final ExcelStrategyContext excelStrategyContext;
    private final ScadaIndexBean scadaIndexBean;

    /**
     * 模糊查询站点名
     *
     * @param stnm 站点名称
     * @return list
     */
    @Override
    public List likeStnm(String stnm) {
        return scadastationService.list(Wrappers.<Scadastation>lambdaQuery()
                .like(stnm != null, Scadastation::getStnm, stnm)
                .select(Scadastation::getId, Scadastation::getStnm, Scadastation::getLgtd, Scadastation::getLttd, Scadastation::getIscraft)
                .orderByAsc(Scadastation::getSn));
    }

    /**
     * 查看趋势
     *
     * @param stid      站点名称
     * @param itnm      指标项
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return list
     */
    @Override
    public Page viewHistory(Page page, Long stid, String itnm, String startTime, String endTime) {

        //改动的话要动太多，将就用吧
        String str = itnm.replace("%23","#");

        Scadastaitem staitem = scadastaitemService.getOne(Wrappers.<Scadastaitem>lambdaQuery()
                .select(Scadastaitem::getItcd)
                .eq(Scadastaitem::getState, 1)
                .eq(Scadastaitem::getStationId, stid)
                .eq(Scadastaitem::getItnm, str));

        Scadastation station = scadastationService.getById(stid);

        if (staitem == null) {
            throw new ToflyDeniedException("测站<" + station.getStnm() + ">未绑定指标：" + itnm);
        }


        return customDisplayMapper.viewHistory(page, staitem.getItcd(), startTime, endTime, scadaIndexBean.getOutlier());
    }

    @Override
    public List<Map> viewHistory(Long stid, String itnm, String startTime, String endTime) {

        String str = itnm.replace("%23","#");
        Scadastaitem staitem = scadastaitemService.getOne(Wrappers.<Scadastaitem>lambdaQuery()
                .eq(Scadastaitem::getState, 1)
                .select(Scadastaitem::getItcd)
                .eq(Scadastaitem::getStationId, stid)
                .eq(Scadastaitem::getItnm, str));

        Scadastation station = scadastationService.getById(stid);

        if (staitem == null) {
            throw new ToflyDeniedException("测站<" + station.getStnm() + ">未绑定指标：" + itnm);
        }

        return customDisplayMapper.viewHistory2(staitem.getItcd(), startTime, endTime, scadaIndexBean.getOutlier());
    }


    /**
     * 计算合格率
     *
     * @param itcd      指标编号
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return map
     */
    @Override
    public Map calItemValue(String itcd, String startTime, String endTime, Double duration) {
        //指标表
        Scadastaitem staitem = scadastaitemService.getOne(Wrappers.<Scadastaitem>lambdaQuery()
                .eq(Scadastaitem::getState, 1)
                .eq(Scadastaitem::getItcd, itcd));

        if (null == staitem) {
            throw new ToflyDeniedException("该指标不存在");
        }

        Map<String, Object> result = new HashMap<>(3);

        Map<String, Double> passRate = null;
        if (staitem.getDownLimit() != null || staitem.getUpLimit() != null) {

            double up = staitem.getUpLimit() == null ? MAX_VALUE : staitem.getUpLimit();
            double down = staitem.getDownLimit() == null ? MIN_VALUE : staitem.getDownLimit();

            passRate = calRate(startTime, endTime, staitem.getItcd(), up, down, Long.parseLong(duration.toString()));
        }

        Map<String, Double> qualityRate = null;
        if (null != staitem.getQualityStandard()) {

            String[] split = staitem.getQualityStandard().split(",");

            Double up;
            Double down;
            if (split.length != 0) {
                if (split.length != 2) {
                    if (staitem.getQualityStandard().trim().startsWith(",")) {
                        up = Double.parseDouble(split[1]);
                        down = Double.valueOf(MIN_VALUE);
                    } else {
                        down = Double.parseDouble(split[0]);
                        up = Double.valueOf(MAX_VALUE);
                    }
                } else {
                    up = Double.parseDouble(split[1]);
                    down = Double.parseDouble(split[0]);
                }
                qualityRate = calRate(startTime, endTime, staitem.getItcd(), up, down, Long.parseLong(duration.toString()));
            }
        }

        result.put("passRate", passRate);
        result.put("qualityRate", qualityRate);
        result.put("overStandardNum", getOverStandardNum(staitem, startTime, endTime, duration));

        return result;
    }


    /**
     * 添加测站信息
     *
     * @param scadastation 测站信息表
     * @return true or false
     */
    @Override
    public boolean insertStation(Scadastation scadastation) {

        if (scadastation == null) {
            throw new ToflyDeniedException("插入信息不能为空");
        }
        scadastationService.save(scadastation);

        return true;
    }

    /**
     * 修改测站信息
     *
     * @param scadastation 测站信息
     * @return true or false
     */
    @Override
    public boolean updateStation(Scadastation scadastation) {
        return scadastationService.updateById(scadastation);
    }

    /**
     * 删除测站信息
     *
     * @param ids 测站id
     * @return true or false
     */
    @Override
    public boolean deleteStation(String ids) {
        List<String> list = Arrays.asList(ids.split(","));
        scadastationService.removeByIds(list);
        scadastaitemService.remove(Wrappers.<Scadastaitem>lambdaQuery().in(Scadastaitem::getStationId, list));
        return true;
    }

    /**
     * 查看测站信息
     *
     * @param stnm 测站名称
     * @param stcd 测站编号
     * @return page
     */
    @Override
    public List selectStation(Long id, String stnm, String stcd, String categoryName) {

        return scadastationService.list(Wrappers.<Scadastation>lambdaQuery()
                .eq(id != null, Scadastation::getId, id)
                .like(!StringUtils.isEmpty(categoryName), Scadastation::getCategoryName, categoryName)
                .like(!StringUtils.isEmpty(stnm), Scadastation::getStnm, stnm)
                .like(!StringUtils.isEmpty(stcd), Scadastation::getStcd, stcd)
                .orderByAsc(Scadastation::getSn));
    }

    @Override
    public Page selectStation(Page page, Long id, String stnm, String stcd, String categoryName) {
        return scadastationService.page(page, Wrappers.<Scadastation>lambdaQuery()
                .eq(id != null, Scadastation::getId, id)
                .like(!StringUtils.isEmpty(categoryName), Scadastation::getCategoryName, categoryName)
                .like(!StringUtils.isEmpty(stnm), Scadastation::getStnm, stnm)
                .like(!StringUtils.isEmpty(stcd), Scadastation::getStcd, stcd)
                .orderByAsc(Scadastation::getSn));
    }

    /**
     * 添加指标信息
     *
     * @param item 指标配置信息
     * @return true or false
     */
    @Override
    public boolean insertIndex(Scadastaitem item) {
        Scadastaitem scadastaitem = scadastaitemService.getOne(Wrappers.<Scadastaitem>lambdaQuery()
                .eq(Scadastaitem::getItcd, item.getItcd()));

        //编号为空新增;不为空测站id为空修改
        if (scadastaitem == null) {
            scadastaitemService.save(item);
        } else if (scadastaitem.getStationId() == null) {
//            Long id = scadastaitemService.getOne(Wrappers.<Scadastaitem>lambdaQuery()
//                    .eq(Scadastaitem::getItcd, item.getItcd())).getId();
            item.setId(scadastaitem.getId());
            scadastaitemService.updateById(item);
        } else {
            throw new ToflyDeniedException("该指标已绑定测站");
        }
        return true;
    }

    /**
     * 修改测站指标
     *
     * @param item 测站信息
     * @return true or false
     */
    @Override
    public boolean updateIndex(Scadastaitem item) {
        return scadastaitemService.updateById(item);
    }

    /**
     * 查询未关联指标
     *
     * @param itcd 指标编号
     * @return list
     */
    @Override
    public List viewIndex(String itcd) {

        return scadastaitemService.list(Wrappers.<Scadastaitem>lambdaQuery()
                .eq(Scadastaitem::getState, 1)
                .like(Scadastaitem::getItcd, itcd)
                .isNull(Scadastaitem::getStationId)
                .select(Scadastaitem::getItcd, Scadastaitem::getItnm));
    }

    /**
     * 删除测站指标表
     *
     * @param ids 指标id
     * @return true or false
     */
    @Override
    public boolean deleteIndex(String ids) {
        return scadastaitemService.removeByIds(Arrays.asList(ids.split(",")));
    }

    /**
     * 查看测站指标表
     *
     * @param ids 测站id
     * @return page
     */
    @Override
    public List selectIndex(String ids) {
        List<Long> stationId = scadastationService.list(Wrappers.<Scadastation>lambdaQuery()
                .in(Scadastation::getId, Arrays.asList(ids.split(",")))
                .select(Scadastation::getId))
                .stream().filter(Objects::nonNull)
                .map(Scadastation::getId)
                .collect(Collectors.toList());

        if (CollectionUtil.isEmpty(stationId)){
            throw new ToflyDeniedException("所选站点皆未启用");
        }
        return scadastaitemService.list(Wrappers.<Scadastaitem>lambdaQuery()
                .in(Scadastaitem::getStationId, Arrays.asList(ids.split(","))));
    }

    /**
     * 获取测站名称
     *
     * @return list
     */
    @Override
    public List getStnm() {
        return scadastationService.list();
    }

    /**
     * 获取指标名
     *
     * @param stnm 测站名
     * @return list
     */
    @Override
    public List getItnm(String stnm) {
        Long stationId = scadastationService.getOne(Wrappers.<Scadastation>lambdaQuery()
                .eq(Scadastation::getStnm, stnm)).getId();

        return scadastaitemService.list(Wrappers.<Scadastaitem>lambdaQuery()
                .eq(Scadastaitem::getState, 1)
                .eq(Scadastaitem::getStationId, stationId)
                .select(Scadastaitem::getItnm, Scadastaitem::getIscalcPass,
                        Scadastaitem::getPassStandard, Scadastaitem::getIscalcQuality,
                        Scadastaitem::getQualityStandard));
    }

    /**
     * 获取超标次数
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param time      超时时间
     * @return double
     */
    @Override
    public Double getOverStandardNum(Scadastaitem statiem, String startTime, String endTime, Double time) {

        double up = statiem.getUpLimit() == null ? MAX_VALUE : statiem.getUpLimit();
        double down = statiem.getDownLimit() == null ? MIN_VALUE : statiem.getDownLimit();

        //查询对应数据
        List<Scadahistory> historyList = customDisplayMapper.viewItValue(statiem.getItcd(), startTime, endTime, scadaIndexBean.getOutlier());

        //是否超标
        boolean flag = false;
        //超标开始时长
        Date start = null;
        //超标结束时长
        Date end = null;
        //超标次数
        double num = 0D;

        int size = historyList.size() - 1;

        for (int i = 0; i <= size; i++) {
            Scadahistory item = historyList.get(i);
            //指标值
            double itVal = Double.parseDouble(item.getItval());
            //没超标时
            if (!flag) {
                //当超标更改标记并设置开始时间，没超标更改结束时间
                if (itVal > up || itVal < down) {
                    flag = true;
                    start = item.getScadaTime();
                }

                //当标记为超标时
            } else {
                //数据未超标将标记改为未超标
                if (itVal < up && itVal > down) {
                    flag = false;
                    end = historyList.get(i - 1).getScadaTime();
                }
            }

            if (item.getScadaTime() == historyList.get(size).getScadaTime()) {
                end = item.getScadaTime();
            }

            //当开始时间和结束时间不为空计算
            if (start != null && end != null) {
                double floor = Math.floor(((end.getTime() - start.getTime()) / 1000 / 60) / (time * 60));
                if (floor >= 1) {
                    num += floor;
                }
                //重置开始时间和状态
                start = null;
                flag = false;
                end = null;
            }
            if (i == size) {
                if (!flag && end == null) {
                    num += 1;
                }
            }
        }
        return num;
    }

    /**
     * 测站指标上传
     *
     * @param dto dto
     * @return boolean
     */
    @Override
    public boolean stationExport(String dto) {
        if (dto.isEmpty()) {
            throw new ToflyDeniedException("dto is not null");
        }
        List<StationDto> station = JSONObject.parseArray(dto, StationDto.class).stream()
                .distinct().collect(Collectors.toList());

        Map<String, List<StaitemDto>> dtoMap = JSONObject.parseArray(dto, StaitemDto.class).stream().collect(Collectors.groupingBy(StaitemDto::getStcd));

        station.forEach(tion -> {
            Scadastation stion = scadastationService.getOne(Wrappers.<Scadastation>lambdaQuery()
                    .eq(Scadastation::getStcd, tion.getStcd()));

            Long stId = null;
            if (null == stion) {
                Scadastation scadastation = JSONObject.parseObject(JSONObject.toJSONString(tion), Scadastation.class);
                scadastation.setState("1");
                scadastationService.save(scadastation);
                stId = scadastation.getId();
            }
            if (dtoMap != null && null != dtoMap.get(tion.getStcd())) {
                List<StaitemDto> staitemDtos = dtoMap.get(tion.getStcd());

                if (null != staitemDtos && staitemDtos.size() > 0) {
                    Long finalStId = stId;
                    JSONObject.parseArray(JSONObject.toJSONString(staitemDtos), Scadastaitem.class)
                            .forEach(it -> {

                                Scadastaitem statiem = scadastaitemService.getOne(Wrappers.<Scadastaitem>lambdaQuery()
                                        .eq(Scadastaitem::getItcd, it.getItcd()));
                                if (statiem != null) {
                                    it.setId(statiem.getId());
                                    scadastaitemService.updateById(it);
                                } else {
                                    if (finalStId != null) {
                                        it.setStationId(finalStId);
                                    } else if (stion != null) {
                                        it.setStationId(stion.getId());
                                    }
                                    it.setState(1L);
                                    scadastaitemService.save(it);
                                }

                            });
                }
            }
        });
        return true;
    }

    @Override
    public List<Map> getIndexType() {
        return customDisplayMapper.getIndexType();
    }

    /**
     * 批量配置测站指标标准
     *
     * @param dto dto
     * @return boolean
     */
    @Override
    public boolean insertStandard(StanderdDto dto) {
        if (dto == null) {
            throw new ToflyDeniedException("数据为空");
        }

        return scadastaitemService.update(Wrappers.<Scadastaitem>lambdaUpdate()
                .eq(Scadastaitem::getStationId, dto.getStationId())
                .set(dto.getUpLimit() != null, Scadastaitem::getUpLimit, dto.getUpLimit())
                .set(dto.getDownLimit() != null, Scadastaitem::getDownLimit, dto.getDownLimit())
                .set(dto.getUpLimit() != null || dto.getDownLimit() != null, Scadastaitem::getIscalcPass, 1)
                .set(dto.getQualityStandard() != null, Scadastaitem::getQualityStandard, dto.getQualityStandard()));
    }

    /**
     * 获取已配置合格率/优质率的测站与指标信息
     *
     * @return map
     */
    @Override
    public Map<Object, List<Map>> getStationItemInfo() {
        List<Map> stationItemInfo = customDisplayMapper.getStationItemInfo();
        if (!stationItemInfo.isEmpty()) {
            return stationItemInfo.stream().collect(Collectors.groupingBy(m -> m.get("stnm")));
        } else {
            return null;
        }

    }

    /**
     * 趋势分析
     *
     * @param itcd  指标编号
     * @param start 开始时间
     * @param end   结束时间
     * @param time  超时时间
     * @param itid  需排除id
     * @return map
     */
    @Override
    public Map<String, Map<String, Object>> trendAnalys(String itcd, String start, String end, Double time, String itid) {
        if (StringUtils.isEmpty(itcd)) {
            throw new ToflyDeniedException("指标编号为空");
        }

        List<String> itcdList = Arrays.asList(itcd.split(","));

        Map<String, Map<String, Object>> result = new HashMap<>();

        //需排除的指标值id
        List<String> itidList = null;
        if (!StringUtils.isEmpty(itid)) {
            itidList = Arrays.asList(itid.split(","));
        }
        //查询所有指标信息
        List<Map<String, Object>> itvalHistoryAll = customDisplayMapper.getItvalHistoryAll(itcdList, start, end, itidList);

        if (!itvalHistoryAll.isEmpty()) {
            //总时长
            Map<String, Double> timeMap = calAbnormalTime(itvalHistoryAll);
            Double sumTime = timeMap.get("sumTime");
            Double anomaly = timeMap.get("anomaly");
            //根据指标编号进行分组
            Map<String, List<Map<String, Object>>> itcdGroupMap = itvalHistoryAll.stream().collect(Collectors.groupingBy(m -> m.get("itcd").toString()));

            Map<String, Object> map;
            for (String index : itcdList) {
                //查询指标的合格率/优质率
                Scadastaitem scadastaitem = scadastaitemService.getOne(Wrappers.<Scadastaitem>lambdaQuery().eq(Scadastaitem::getItcd, index));
                map = new HashMap();
                if (scadastaitem != null) {
                    //合格上下限
                    Double qualifiedUp = null;
                    Double qualifiedDown = null;
                    //设置合格率上下限
                    if (scadastaitem.getUpLimit() != null && scadastaitem.getDownLimit() != null) {
                        qualifiedUp = scadastaitem.getUpLimit();
                        qualifiedDown = scadastaitem.getDownLimit();
                    } else if (scadastaitem.getUpLimit() != null && scadastaitem.getDownLimit() == null) {
                        qualifiedUp = scadastaitem.getUpLimit();
                        qualifiedDown = Double.MIN_VALUE;
                    } else if (scadastaitem.getUpLimit() == null && scadastaitem.getDownLimit() != null) {
                        qualifiedUp = Double.MAX_VALUE;
                        qualifiedDown = scadastaitem.getDownLimit();
                    }

                    Map qualifiedMap = this.commonCode(index, qualifiedUp, qualifiedDown, time, sumTime, itcdGroupMap,anomaly);
                    map.put("sumTime", sumTime);
                    map.put("limitRete", (qualifiedMap != null && !qualifiedMap.isEmpty()) ? Double.valueOf(qualifiedMap.get("rate").toString()) : null);
                    map.put("limitNum", (qualifiedMap != null && !qualifiedMap.isEmpty()) ? Double.valueOf(qualifiedMap.get("outNum").toString()) : null);
                    map.put("limitTimeOut", (qualifiedMap != null && !qualifiedMap.isEmpty()) ? Double.valueOf(qualifiedMap.get("timeOut").toString()) : null);
                    map.put("limitSection", (qualifiedMap != null && !qualifiedMap.isEmpty()) ? qualifiedMap.get("section") : null);

                    //优质上下限
                    Double highGradeUp = null;
                    Double highGradeDown = null;

                    String qualityStandard = scadastaitem.getQualityStandard();
                    if (!StringUtils.isEmpty(qualityStandard) && !",".equals(qualityStandard)) {
                        String[] split = qualityStandard.trim().split(",");
                        if (split.length == 1 && !StringUtils.isEmpty(split[0])) {
                            highGradeDown = Double.valueOf(split[0]);
                            highGradeUp = Double.MAX_VALUE;
                        } else if (split.length == 2 && StringUtils.isEmpty(split[0]) && !StringUtils.isEmpty(split[1])) {
                            highGradeUp = Double.valueOf(split[1]);
                            highGradeDown = Double.MIN_VALUE;
                        } else {
                            highGradeDown = Double.valueOf(split[0]);
                            highGradeUp = Double.valueOf(split[1]);
                        }
                    }
                    Map highGradeMap = this.commonCode(index, highGradeUp, highGradeDown, time, sumTime, itcdGroupMap,anomaly);
                    map.put("qualityRete", (highGradeMap != null && !highGradeMap.isEmpty()) ? Double.valueOf(highGradeMap.get("rate").toString()) : null);
                    map.put("qualityNum", (highGradeMap != null && !highGradeMap.isEmpty()) ? Double.valueOf(highGradeMap.get("outNum").toString()) : null);
                    map.put("qualityTimeOut", (highGradeMap != null && !highGradeMap.isEmpty()) ? Double.valueOf(highGradeMap.get("timeOut").toString()) : null);
                    map.put("qualitySection", (qualifiedMap != null && !qualifiedMap.isEmpty()) ? qualifiedMap.get("section") : null);
                }
                result.put(index, map);
            }

        }
        return result;
    }


    @Override
    public Map commonCode(String index, Double up, Double down, Double time, Double sumTime, Map<String, List<Map<String, Object>>> itcdGroupMap, Double anomaly) {
        Map<String, Object> map = new HashMap<>();
        if (up != null && down != null) {
            if (itcdGroupMap.containsKey(index)) {
                List<Map<String, Object>> itValueList = itcdGroupMap.get(index);

                //超标开始时间
                Date startDate = null;
                //超标结束时间
                Date endDate = null;
                //超标标志
                boolean flag = false;
                //超标时长
                Double timeOut = 0D;
                //超标次数
                Double outNum = 0D;

                List<TimeOutSectionDto> timeOutSectionList = new ArrayList<>(10);

                TimeOutSectionDto timeOutSectionDto;

                int size = itValueList.size();

                for (int i = 0; i < size; i++) {
                    Map<String, Object> itValmap = itValueList.get(i);

                    //特殊值跳过不参与计算
/*`1                    if (itValmap.get("itval").toString().contains("9999")) {
                        continue;
                    }*/
                    //指标值
                    double itval = Double.parseDouble(itValmap.get("itval").toString());
                    //时间
                    Date dateTime = DateUtil.formatDate(itValmap.get("scadaTime").toString(), "yyyy-MM-dd HH:mm:ss");

                    if (itval > up || itval < down) {
                        //超标后变更
                        if (!flag && !String.valueOf(itval).contains("9999")) {
                            flag = true;
                            startDate = dateTime;
                        }else if (flag && String.valueOf(itval).contains("9999")){
                            flag = false;
                            endDate = DateUtil.formatDate(itValueList.get(i-1).get("scadaTime").toString(), "yyyy-MM-dd HH:mm:ss");
                            timeOut += calTime(startDate,dateTime);
                        }
                    } else {
                        //未超标变更
                        if (flag && startDate != null) {
                            flag = false;
                            endDate = dateTime;
                            if (!itValueList.get(i - 1).get("itval").toString().contains("9999")) {
                                //计算超时时间
                                timeOut += calTime(startDate,dateTime);
                            }
                        }
                    }

                    if (!flag && startDate != null && endDate != null) {
                        double outTime = calTime(startDate,dateTime);
                        //大于超时时间超标次数加1
                        if (outTime >= time) {
                            if (!itValueList.get(i - 1).get("itval").toString().contains("9999")) {
                                //保存超时区间
                                timeOutSectionDto = new TimeOutSectionDto();
                                timeOutSectionDto.setStart(startDate);
                                timeOutSectionDto.setEnd(endDate);
                                timeOutSectionList.add(timeOutSectionDto);
                                outNum += 1;
                            }
                        }
                        startDate = null;
                        endDate = null;
                    }
                    //到最后一个值还是超标且没有结束时间，最后一条的时间作为结束时间然后计算
                    if (i == size - 1 && flag && startDate != null && endDate == null && !itValueList.get(i - 1).get("itval").toString().contains("9999")) {
                        endDate = dateTime;
                        double outTime = calTime(startDate,dateTime);
                        timeOut += outTime;

                        if (outTime > time) {
                            //保存超时区间
                            timeOutSectionDto = new TimeOutSectionDto();
                            timeOutSectionDto.setStart(startDate);
                            timeOutSectionDto.setEnd(endDate);
                            timeOutSectionList.add(timeOutSectionDto);
                            outNum += 1;
                            startDate = null;
                            endDate = null;
                            flag = false;
                        }
                    }
                }
                map.put("rate", timeOut == 0 ? 100 : Double.valueOf(String.format("%.2f", 100D - ((timeOut / sumTime) * 100))));
                map.put("outNum", outNum);
                map.put("timeOut", Double.valueOf(String.format("%.2f", timeOut)));
                map.put("section", timeOutSectionList);
            }
        }
        return map;
    }

    /**
     * 获取指标历史
     *
     * @param itcd  指标编号
     * @param start 开始时间
     * @param end   结束时间
     * @return map
     */
    @Override
    public Map<Object, List<Map<String, Object>>> getValHistory(String itcd, String start, String end) {
        return customDisplayMapper.getItvalHistory(Arrays.asList(itcd.split(",")), start, end, null, scadaIndexBean.getOutlier())
                .stream().collect(Collectors.groupingBy(map -> map.get("itcd")));
    }

    /**
     * 获取指标历史
     *
     * @param page  page
     * @param itcd  指标编号
     * @param start 开始时间
     * @param end   结束时间
     * @return map
     */
    @Override
    public Map<Object, Page> getValHistory(Page page, String itcd, String start, String end) {
        Map<Object, Page> result = new HashMap<>();
        Map<Object, List<Map<String, Object>>> valHistory = this.getValHistory(itcd, start, end);
        for (String cd : itcd.split(",")) {
            if (!valHistory.isEmpty()) {
                result.put(cd, CommonMethod.listToPage(page, valHistory.get(cd)));
            } else {
                result.put(cd, page);
            }
        }
        return result;
    }

    /**
     * 单个指标历史分页查询
     */
    @Override
    public Page<Map> itvalPageQuery(Page page, String itcd, String start, String end) {
        return customDisplayMapper.viewHistory(page, itcd, start, end, scadaIndexBean.getOutlier());
    }

    /**
     * 指标历史导出
     *
     * @param itcd  指标编号
     * @param start 开始时间
     * @param end   结束时间
     */
    @Override
    public Object itemValExport(HttpServletResponse response, String itcd, String start, String end) {
        List<Map<String, Object>> itvalHistory = customDisplayMapper.itemValInfo(Arrays.asList(itcd.split(",")), start, end, scadaIndexBean.getOutlier());
        // 根据templateServiceType,templateFlag获取对应模板数据
        ExcelExportFileTypeEnum excelExportFileTypeEnum = ExcelExportFileTypeEnum.getExcelExportFileTypeEnum("itemItvalExportService", "1");
        if (null == excelExportFileTypeEnum) {
            throw new ToflyDeniedException("导出失败");
        }
        //将查询的结果转json字符串的格式，因为导出的时候只能传一个字符串
        String resultListToString = JSONArray.toJSONStringWithDateFormat(itvalHistory, "yyyy-MM-dd HH:mm:ss");
        // 从模型类型枚举根据templateServiceType获取策略对应模板数据
        List<Map<String, Object>> dataList = excelStrategyContext.getExportData("itemItvalExportService", "1", resultListToString);
        ExcelUtil.export(dataList.get(0), excelExportFileTypeEnum, Constant.FILE_PATH_TEST_EXPORT_EXCEL, response);
        return null;
    }

    /**
     * 批量修改归类名称
     *
     * @param stid 测站id
     * @param name 归类名称
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean setCategoryName(String stid, String name) {
        if (StringUtils.isEmpty(stid)) {
            throw new ToflyDeniedException("未选择测站信息");
        }
        if (StringUtils.isEmpty(name)) {
            throw new ToflyDeniedException("未输入归类名称");
        }
        return scadastationService.update(Wrappers.<Scadastation>lambdaUpdate()
                .in(Scadastation::getId, Arrays.asList(stid.split(","))).set(Scadastation::getCategoryName, name));
    }

    @Override
    public Map<String, List<Scadastaitem>> getIndexTypeInfo(Long typeId) {
        if (typeId == null) {
            throw new ToflyDeniedException("查询类型id不能为空");
        }
        return scadastaitemService.list(Wrappers.<Scadastaitem>lambdaQuery()
                .eq(Scadastaitem::getIndexType, typeId))
                .stream()
                .collect(Collectors.groupingBy(Scadastaitem::getStationName));
    }

    private Map<String, Double> calRate(String startTime, String endTime, String itcd, Double up, Double down, Long duration) {
        Date startDate = DateUtil.formatDate(startTime, "yyyy-MM-dd HH:mm:ss");
        Date endDate = DateUtil.formatDate(endTime, "yyyy-MM-dd HH:mm:ss");

        //总时长
        long sumTime = (endDate.getTime() - startDate.getTime()) / 1000 / 60 / (duration * 60);
        //超标标识
        boolean flag = false;
        //超标时长
        double time = 0D;
        //开始时长
        Date start = null;
        //结束时长
        Date end = null;

        Map<String, Double> map = new HashMap<>();

        List<Scadahistory> valueList = customDisplayMapper.viewItValue(itcd, startTime, endTime, scadaIndexBean.getOutlier());

        if (valueList.size() >= 1) {
            int size = valueList.size() - 1;

            for (int i = 0; i <= size; i++) {
                Scadahistory item = valueList.get(i);
                //指标值
                double itVal = Double.parseDouble(item.getItval());
                //没超标时
                if (!flag) {
                    //当超标更改标记并设置开始时间，没超标更改结束时间
                    if (itVal > up || itVal < down) {
                        flag = true;
                        start = item.getScadaTime();
                    }

                    //当标记为超标时
                } else {
                    //数据未超标将标记改为未超标
                    if (itVal < up && itVal > down) {
                        flag = false;
                        end = valueList.get(i - 1).getScadaTime();
                    }
                }

                if (item.getScadaTime() == valueList.get(size).getScadaTime()) {
                    end = item.getScadaTime();
                }

                //当开始时间和结束时间不为空计算
                if (start != null && end != null) {

                    time += Math.floor((double) ((end.getTime() - start.getTime()) / 1000 / 60 / (duration * 60)));
                    //重置开始时间和状态
                    start = null;
                    flag = false;
                    end = null;
                }
                if (i == size) {
                    if (!flag && end == null) {
                        if (start == null) {
                            continue;
                        }
                        time += Math.floor((double) ((valueList.get(i).getScadaTime().getTime() - start.getTime()) / 1000 / 60 / (duration * 60)));
                    }
                }
            }
            map.put("rate", Double.parseDouble(new DecimalFormat("0.00").format(100 - (time / sumTime) * 100)));
            map.put("sumTime", time);
        } else {
            map.put("rate", null);
            map.put("sumTime", null);
        }


        return map;
    }

    private Map<String, Double> calAbnormalTime(List<Map<String, Object>> historyList) {
        //异常开始结束时间
        Date start = null;
        Date end = null;
        //是否异常
        boolean flag = false;

        String outlier = "9999";

        double sumTime = 0D;

        int size = historyList.size() - 1;

        for (int i = 0; i <= size; i++) {
            Map<String, Object> map = historyList.get(i);
            //当前值
            String itval = String.valueOf(map.get("itval"));
            //当前时间
            Date scadaTime = cn.hutool.core.date.DateUtil.parse(map.get("scadaTime").toString(), "yyyy-MM-dd HH:mm:ss");
            if (i == 0) {
                if (itval.contains(outlier)) {
                    start = scadaTime;
                    flag = true;
                }
            } else if (i < size) {
                if (itval.contains(outlier)) {
                    if (!flag) {
                        start = scadaTime;
                        flag = true;
                    }
                } else {
                    if (flag) {
                        end = cn.hutool.core.date.DateUtil.parse(historyList.get(i - 1).get("scadaTime").toString(), "yyyy-MM-dd HH:mm:ss");
                    }
                }
            } else {
                if (flag) {
                    end = scadaTime;
                }
            }
            if (flag && end != null) {
                double time = calTime (start,end);
                sumTime += time;
                flag = false;
                start = null;
                end = null;
            }
        }
        DateTime scadaTime1 = cn.hutool.core.date.DateUtil.parse(historyList.get(0).get("scadaTime").toString(), "yyyy-MM-dd HH:mm:ss");
        DateTime scadaTime2 = cn.hutool.core.date.DateUtil.parse(historyList.get(size).get("scadaTime").toString(), "yyyy-MM-dd HH:mm:ss");

        double sum = calTime(scadaTime1,scadaTime2);
        Map<String, Double> result = new HashMap<>();
        result.put("sumTime", sum - sumTime);
        result.put("anomaly", sumTime);
        return result;
    }

    private double calTime(Date date1,Date date2){
       return BigDecimal.valueOf(cn.hutool.core.date.DateUtil.between(date1, date2, DateUnit.MINUTE)).divide(BigDecimal.valueOf(60),8,BigDecimal.ROUND_DOWN).doubleValue();
    }

//    public static void main(String[] args) {
//        DateTime parse1 = cn.hutool.core.date.DateUtil.parse("2022-12-31 15:27:33", "yyyy-MM-dd HH:mm:ss");
//        DateTime parse2 = cn.hutool.core.date.DateUtil.parse("2022-12-31 15:00:05", "yyyy-MM-dd HH:mm:ss");
//        long between = cn.hutool.core.date.DateUtil.between(parse1, parse2, DateUnit.MINUTE);
//        double v = BigDecimal.valueOf(between).divide(BigDecimal.valueOf(60)).doubleValue();
//
//
//        System.out.println(v);
//    }
}
