package com.njcn.event.service.majornetwork.Impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.device.biz.enums.DeviceResponseEnum;
import com.njcn.device.pq.api.GeneralDeviceInfoClient;
import com.njcn.device.pq.api.LineFeignClient;
import com.njcn.device.pq.pojo.dto.GeneralDeviceDTO;
import com.njcn.device.biz.pojo.dto.PollutionLineDTO;
import com.njcn.device.pq.pojo.dto.PollutionParamDTO;
import com.njcn.device.pq.pojo.param.DeviceInfoParam;
import com.njcn.event.pojo.po.RmpEventDetailPO;
import com.njcn.event.pojo.vo.AreaCalculationVO;
import com.njcn.event.pojo.vo.AreaStatisticalVO;
import com.njcn.event.pojo.vo.AreaStatisticalVO.AreaStatisticsVO;
import com.njcn.event.pojo.vo.AreaStatisticalVO.MonthlyStatisticsVO;
import com.njcn.event.pojo.vo.AreaStatisticalVO.VoltageStatisticsVO;
import com.njcn.event.pojo.vo.MonthCalculationVO;
import com.njcn.event.pojo.vo.VoltageLevelCalculationVO;
import com.njcn.event.service.majornetwork.AreaStatisticalService;
import com.njcn.event.common.service.EventDetailService;
import com.njcn.event.pojo.vo.NodeVO;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.pojo.enums.StatisticsEnum;
import com.njcn.system.pojo.po.DictData;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.njcn.event.influxdb.PqsEventDetailQuery.*;

/**
 * 区域统计
 *
 * @author : zhaojun
 * @version : 1.0.0
 * @date : 2022年06月24日 11:02
 */
@Service
@RequiredArgsConstructor
public class AreaStatisticalServiceImpl implements AreaStatisticalService {
    private final GeneralDeviceInfoClient generalDeviceInfoClient;

    private final EventDetailService eventDetailService;

    private final DicDataFeignClient dicDataFeignClient;

    private final LineFeignClient lineFeignClient;

    /**
     * 获取暂降区域 电压等级 月份统计
     *
     * @param deviceInfoParam 查询参数
     * @return 暂降统计信息
     */
    @Override
    public AreaStatisticalVO getAreaCalculation(DeviceInfoParam.BusinessParam deviceInfoParam) {
        //获取录波字典信息
        DictData data = dicDataFeignClient.getDicDataByCode(DicDataEnum.VOLTAGE_DIP.getCode()).getData();
        DictData shortInterruptionsData = dicDataFeignClient.getDicDataByCode(DicDataEnum.SHORT_INTERRUPTIONS.getCode()).getData();

        if(ObjectUtil.isNull(data)||ObjectUtil.isNull(shortInterruptionsData)){
            throw new BusinessException(DeviceResponseEnum.DIC_GET_EMPTY);
        }
        // 获取指定部门下的监测点集合
        List<GeneralDeviceDTO> generalDeviceDTOList = generalDeviceInfoClient.getPracticalRunDeviceInfo(deviceInfoParam).getData();
        if (CollUtil.isEmpty(generalDeviceDTOList)) {
            return AreaStatisticalVO.empty();
        }
        List<String> lineIds = generalDeviceDTOList.stream().flatMap(dto -> dto.getLineIndexes().stream()).collect(Collectors.toList());
        if (CollUtil.isEmpty(lineIds)) {
            return AreaStatisticalVO.empty();
        }
        //数据暂降查询
        List<RmpEventDetailPO> info = eventDetailService.list(new LambdaQueryWrapper<RmpEventDetailPO>()
                .in(RmpEventDetailPO::getMeasurementPointId, lineIds)
                //todo 是否缺少录波
                .in(RmpEventDetailPO::getEventType, Stream.of(data.getId(),shortInterruptionsData.getId()).collect(Collectors.toList()))

                .ge(StrUtil.isNotBlank(deviceInfoParam.getSearchBeginTime()), RmpEventDetailPO::getStartTime,DateUtil.beginOfDay(DateUtil.parse(deviceInfoParam.getSearchBeginTime())))
                .le(StrUtil.isNotBlank(deviceInfoParam.getSearchEndTime()), RmpEventDetailPO::getStartTime, DateUtil.endOfDay(DateUtil.parse(deviceInfoParam.getSearchEndTime())))
        );
        if (CollUtil.isEmpty(info)) {
            return AreaStatisticalVO.empty();
        }

        AreaStatisticsVO areaStatisticsVO = statisticsAreaInfo(generalDeviceDTOList, info);

        MonthlyStatisticsVO monthCalculationVOS = statisticsMonthInfo(deviceInfoParam, info);


        deviceInfoParam.getStatisticalType().setCode(StatisticsEnum.VOLTAGE_LEVEL.getCode());
        List<GeneralDeviceDTO> voltageLevelList = generalDeviceInfoClient.getPracticalRunDeviceInfo(deviceInfoParam).getData();
        VoltageStatisticsVO voltageStatisticsVO = statisticalVoltageLevelInfo(voltageLevelList, info);

        return AreaStatisticalVO.buildVO(areaStatisticsVO, voltageStatisticsVO, monthCalculationVOS);
    }

    @Override
    public List<NodeVO> getCalculation(DeviceInfoParam.BusinessParam deviceInfoParam) {
        List<NodeVO> nodeVOS=new ArrayList<>();
        //获取录波字典信息
        DictData data = dicDataFeignClient.getDicDataByCode(DicDataEnum.VOLTAGE_DIP.getCode()).getData();
        DictData shortInterruptionsData = dicDataFeignClient.getDicDataByCode(DicDataEnum.SHORT_INTERRUPTIONS.getCode()).getData();

        if(ObjectUtil.isNull(data)||ObjectUtil.isNull(shortInterruptionsData)){
            throw new BusinessException(DeviceResponseEnum.DIC_GET_EMPTY);
        }
        // 获取指定部门下的监测点集合
        List<GeneralDeviceDTO> generalDeviceDTOList = generalDeviceInfoClient.getPracticalRunDeviceInfo(deviceInfoParam).getData();
        if (CollUtil.isEmpty(generalDeviceDTOList)) {
            return nodeVOS;
        }
        List<String> lineIds = generalDeviceDTOList.stream().flatMap(dto -> dto.getLineIndexes().stream()).collect(Collectors.toList());
        if (CollUtil.isEmpty(lineIds)) {
            return nodeVOS;
        }
        //数据暂降查询
        List<RmpEventDetailPO> info = eventDetailService.list(new LambdaQueryWrapper<RmpEventDetailPO>()
                .in(RmpEventDetailPO::getMeasurementPointId, lineIds)
                //todo 是否缺少录波
                .in(RmpEventDetailPO::getEventType, Stream.of(data.getId(),shortInterruptionsData.getId()).collect(Collectors.toList()))
                .ge(StrUtil.isNotBlank(deviceInfoParam.getSearchBeginTime()), RmpEventDetailPO::getStartTime,DateUtil.beginOfDay(DateUtil.parse(deviceInfoParam.getSearchBeginTime())))
                .le(StrUtil.isNotBlank(deviceInfoParam.getSearchEndTime()), RmpEventDetailPO::getStartTime, DateUtil.endOfDay(DateUtil.parse(deviceInfoParam.getSearchEndTime())))
        );
        //获取变电站信息
        List<PollutionLineDTO> lineInfo = new ArrayList<>();
        if (!CollectionUtils.isEmpty(lineIds)){
            PollutionParamDTO paramDTO = new PollutionParamDTO();
            paramDTO.setLineList(lineIds);
            lineInfo = lineFeignClient.getLineInfo(paramDTO).getData();
        }
        //将将变电站信息组合
        Map<String,List<PollutionLineDTO>> map = lineInfo.stream().collect(Collectors.groupingBy(PollutionLineDTO::getSubstationId));
        generalDeviceDTOList.forEach(dto->{
            if (!CollectionUtils.isEmpty(dto.getSubIndexes())){
                List<NodeVO> childrenList = new ArrayList<>();
                NodeVO node = new NodeVO();
                node.setId(dto.getIndex());
                node.setName(dto.getName()+"\n("+dto.getLineIndexes().size()+")");
                dto.getSubIndexes().forEach(sub->{
                    List<PollutionLineDTO> l1 = map.get(sub);
                    NodeVO children = new NodeVO();
                    children.setId(sub);
                    children.setPid(dto.getIndex());
                    children.setName(l1.get(0).getSubstation());
                    //监测点id
                    List<String> lines = l1.stream().map(PollutionLineDTO::getId).collect(Collectors.toList());
                    //获取变电站暂降次数
                    List<RmpEventDetailPO> dipNum = info.stream().filter(x -> lines.contains(x.getLineId())).collect(Collectors.toList());
                    children.setData(dipNum.size());
                    childrenList.add(children);
                });
                if (!CollectionUtils.isEmpty(childrenList)){
                    node.setData(childrenList.stream().mapToInt(NodeVO::getData).sum());
                    node.setChildren(childrenList.stream().sorted(Comparator.comparing(NodeVO::getData).reversed().thenComparing(NodeVO::getName)).collect(Collectors.toList()));
                }
                nodeVOS.add(node);
            }
        });
        return nodeVOS;
    }

    /**
     * 统计月份信息
     *
     * @param deviceInfoParam 参数
     * @param eventDetailList influxdb数据
     * @return 统计月份信息
     */
    private MonthlyStatisticsVO statisticsMonthInfo(DeviceInfoParam.BusinessParam deviceInfoParam, List<RmpEventDetailPO> eventDetailList) {
        List<DateTime> timeLimit = calculateTheTimePeriodRange(deviceInfoParam);
        List<DateTime> monthStartAndEnd = new ArrayList<>();
        if(deviceInfoParam.getTimeFlag()==0){
            //月
            monthStartAndEnd = calculateMonthStartAndEnd(timeLimit,0);
        }else {
            //天
            monthStartAndEnd = calculateMonthStartAndEnd(timeLimit,1);
        }


        List<MonthCalculationVO> monthCalculationVOList = new ArrayList<>(monthStartAndEnd.size());
        MonthCalculationVO monthCalculationVO;
        for (int i = 0; i < monthStartAndEnd.size(); i += 2) {
            DateTime beginMon = monthStartAndEnd.get(i);
            DateTime endMon = monthStartAndEnd.get(i + 1);

            List<RmpEventDetailPO> eventDetails = getBetweenTimeEventDetailList(eventDetailList).apply(beginMon, endMon);

            // 时间段内总记录数
            Long count = (long) eventDetails.size();

            // 时间段内非空记录数,剔除等于空的
            Long linkedCount = countEventassIndexIsNoEmpty().apply(eventDetails);


            monthCalculationVO = new MonthCalculationVO();
            if(deviceInfoParam.getTimeFlag()==0){
                //月
                monthCalculationVO.setMonth(beginMon.toDateStr().substring(0, 7));
            }else {
                //日
                monthCalculationVO.setMonth(beginMon.toDateStr());
            }
            monthCalculationVO.setNotAssociated(count - linkedCount);
            monthCalculationVO.setLinked(linkedCount);

            monthCalculationVOList.add(monthCalculationVO);
        }
        Long notAssociatedSum = monthCalculationVOList.stream().mapToLong(MonthCalculationVO::getNotAssociated).sum();

        return MonthlyStatisticsVO.buildVO(notAssociatedSum, monthCalculationVOList);
    }

    /**
     * 统计电压等级信息
     *
     * @param generalDeviceDTOList 监测点数据
     * @param eventDetailList      influxdb数据
     * @return 统计电压等级信息
     */
    private VoltageStatisticsVO statisticalVoltageLevelInfo(List<GeneralDeviceDTO> generalDeviceDTOList, List<RmpEventDetailPO> eventDetailList) {
        List<VoltageLevelCalculationVO> voltageLevelCalculation = generalDeviceDTOList.parallelStream().map(t -> {
            VoltageLevelCalculationVO voltageLevelCalculationVO = new VoltageLevelCalculationVO();
            voltageLevelCalculationVO.setVoltageLevel(t.getName());
            voltageLevelCalculationVO.setMonitoringPoints(t.getLineIndexes().size());
            voltageLevelCalculationVO.setFrequency(countLineIndexes(eventDetailList).apply(t.getLineIndexes()));
            return voltageLevelCalculationVO;
        }).collect(Collectors.toCollection(() -> Collections.synchronizedList(new ArrayList<>())));

        // 求和暂降次数
        Long frequencySum = voltageLevelCalculation.stream().mapToLong(VoltageLevelCalculationVO::getFrequency).sum();
        // 求装置数量
        Integer monitoringPointSum = voltageLevelCalculation.stream().mapToInt(VoltageLevelCalculationVO::getMonitoringPoints).sum();
        return VoltageStatisticsVO.buildVO(frequencySum, monitoringPointSum, voltageLevelCalculation);
    }

    /**
     * 统计区域信息
     *
     * @param generalDeviceDTOList 监测点数据
     * @param eventDetailList      influxdb数据
     * @return 统计区域信息
     */
    private AreaStatisticsVO statisticsAreaInfo(List<GeneralDeviceDTO> generalDeviceDTOList, List<RmpEventDetailPO> eventDetailList) {
        List<AreaCalculationVO> areaCalculation = generalDeviceDTOList.parallelStream().map(t -> {
            AreaCalculationVO calculationVO = new AreaCalculationVO();
            calculationVO.setAreaName(t.getName()+"\n("+t.getLineIndexes().size()+")");
            calculationVO.setMonitoringPoints(t.getLineIndexes().size());

            calculationVO.setFrequency(countLineIndexes(eventDetailList).apply(t.getLineIndexes()));

            calculationVO.computeSarfi90();
            return calculationVO;
        }).collect(Collectors.toCollection(() -> Collections.synchronizedList(new ArrayList<>())));

        // 求和暂降次数
        Long frequencySum = areaCalculation.stream().mapToLong(AreaCalculationVO::getFrequency).sum();
        // 求装置数量
        Integer monitoringPointSum = areaCalculation.stream().mapToInt(AreaCalculationVO::getMonitoringPoints).sum();

        return AreaStatisticsVO.buildVO(frequencySum, monitoringPointSum, areaCalculation);
    }

    /**
     * 计算出月的开始时间和结束时间
     *
     * @param timeLimit 查询参数
     * @param timeFlag 0.月 1.天
     * @return 月的开始时间和结束时间
     */
    private List<DateTime> calculateMonthStartAndEnd(List<DateTime> timeLimit,Integer timeFlag) {

        if(timeFlag == 0){
            return timeLimit.stream().flatMap(dateTime -> {
                DateTime dateTime1 = DateUtil.beginOfMonth(dateTime);
                DateTime dateTime2 = DateUtil.endOfMonth(dateTime);
                return Stream.of(dateTime1, dateTime2);
            }).collect(Collectors.toList());
        }else {
            return timeLimit.stream().flatMap(dateTime -> {
                DateTime dateTime1 = DateUtil.beginOfDay(dateTime);
                DateTime dateTime2 = DateUtil.endOfDay(dateTime);
                return Stream.of(dateTime1, dateTime2);
            }).collect(Collectors.toList());

        }

    }

    /**
     * 根据开始时间结束时间, 获取时间年月范围
     *
     * @param deviceInfoParam 查询参数
     * @return 获取时间年月范围
     */
    private List<DateTime> calculateTheTimePeriodRange(DeviceInfoParam.BusinessParam deviceInfoParam) {
        if(deviceInfoParam.getTimeFlag() == 0){
            //月
            DateTime beginTime = DateUtil.parse(deviceInfoParam.getSearchBeginTime(), DatePattern.NORM_DATE_PATTERN);
            DateTime endTime = DateUtil.parse(deviceInfoParam.getSearchEndTime(), DatePattern.NORM_DATE_PATTERN);
            return DateUtil.rangeToList(beginTime, endTime, DateField.MONTH);
        }else {
            DateTime beginTime = DateUtil.parse(deviceInfoParam.getSearchBeginTime(), DatePattern.NORM_DATE_PATTERN);
            DateTime endTime = DateUtil.parse(deviceInfoParam.getSearchEndTime(), DatePattern.NORM_DATE_PATTERN);
            return DateUtil.rangeToList(beginTime, endTime, DateField.DAY_OF_MONTH);

        }

    }

}
