package com.njcn.device.pq.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import com.njcn.device.biz.commApi.CommLineClient;
import com.njcn.device.biz.pojo.dto.LineALLInfoDTO;
import com.njcn.device.common.service.GeneralDeviceService;
import com.njcn.device.line.mapper.LineDetailMapper;
import com.njcn.device.line.mapper.LineMapper;
import com.njcn.device.line.service.LineService;
import com.njcn.device.pq.enums.RunFlagEnum;
import com.njcn.device.pq.mapper.RStatOnlinerateDMapper;
import com.njcn.device.pq.pojo.dto.GeneralDeviceDTO;
import com.njcn.device.pq.pojo.dto.LineDataQualityDTO;
import com.njcn.device.pq.pojo.dto.MonitorIntegrityDTO;
import com.njcn.device.pq.pojo.param.DeviceInfoParam;
import com.njcn.device.pq.pojo.param.LineBaseQueryParam;
import com.njcn.device.pq.pojo.param.LineDataQualityParam;
import com.njcn.device.pq.pojo.po.Line;
import com.njcn.device.pq.pojo.po.RStatIntegrityD;
import com.njcn.device.pq.pojo.po.RStatOnlinerateD;
import com.njcn.device.pq.pojo.vo.LineDetailDataVO;
import com.njcn.device.pq.pojo.vo.LineDetailVO;
import com.njcn.device.pq.pojo.vo.OnlineMonitorVo;
import com.njcn.device.pq.pojo.vo.RStatIntegrityVO;
import com.njcn.device.pq.pojo.vo.common.DeviceOnlineRate;
import com.njcn.device.pq.service.IRStatIntegrityDService;
import com.njcn.device.rstatintegrity.mapper.RStatIntegrityDMapper;
import com.njcn.device.userledger.service.UserLedgerService;
import com.njcn.supervision.pojo.param.user.UserReportParam;
import com.njcn.supervision.pojo.vo.user.UserLedgerVO;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 数据完整性日表 服务实现类
 * </p>
 *
 * @author hongawen
 * @since 2023-03-28
 */
@Service
@RequiredArgsConstructor
public class RStatIntegrityDServiceImpl extends MppServiceImpl<RStatIntegrityDMapper, RStatIntegrityD> implements IRStatIntegrityDService {

    private final RStatOnlinerateDMapper onlineRateMapper;
    private final RStatIntegrityDMapper rStatIntegrityDMapper;
    private final LineMapper lineMapper;
    private final LineDetailMapper lineDetailMapper;
    private final GeneralDeviceService deviceService;
    private final LineService lineService;

    @Override
    public Float getTotalIntegrityByLineIds(LineBaseQueryParam param) {
        return this.baseMapper.selectTotalIntegrityByLineIds(param);
    }


    @Override
    public List<LineDataQualityDTO> getLineDataQuality(LineDataQualityParam lineDataQualityParam) {
        List<String> lineIds = lineDataQualityParam.getLineIds();
        List<Line> lineList = lineMapper.selectList(new LambdaQueryWrapper<Line>().in(Line::getId, lineIds));

        lineList = lineList.stream().peek(item -> {
            String devId = item.getPids().split(StrUtil.COMMA)[4];
            item.setPid(devId);
        }).collect(Collectors.toList());

        List<String> devIds = lineList.stream().map(Line::getPid).distinct().collect(Collectors.toList());

        QueryWrapper<RStatOnlinerateD> onlineQuery = new QueryWrapper<>();
        onlineQuery.select("dev_index,avg(online_min/offline_min+online_min) as onlineRate")
                .lambda()
                .in(RStatOnlinerateD::getDevIndex, devIds)
                .between(RStatOnlinerateD::getTimeId, DateUtil.parse(lineDataQualityParam.getBeginTime(), DatePattern.NORM_DATE_PATTERN), DateUtil.parse(lineDataQualityParam.getEndTime(), DatePattern.NORM_DATE_PATTERN))
                .groupBy(RStatOnlinerateD::getDevIndex);
        List<RStatOnlinerateD> rStatOnlinerateDList = onlineRateMapper.selectList(onlineQuery);
        Map<String, RStatOnlinerateD> onlineMap = rStatOnlinerateDList.stream().collect(Collectors.toMap(RStatOnlinerateD::getDevIndex, Function.identity()));


        QueryWrapper<RStatIntegrityD> integrityQuery = new QueryWrapper<>();
        integrityQuery.select("line_index,avg(real_time/due_time) as integrityData")
                .lambda()
                .in(RStatIntegrityD::getLineIndex, lineIds)
                .between(RStatIntegrityD::getTimeId, DateUtil.parse(lineDataQualityParam.getBeginTime(), DatePattern.NORM_DATE_PATTERN), DateUtil.parse(lineDataQualityParam.getEndTime(), DatePattern.NORM_DATE_PATTERN))
                .groupBy(RStatIntegrityD::getLineIndex);
        List<RStatIntegrityD> rStatIntegrityDList = rStatIntegrityDMapper.selectList(integrityQuery);
        Map<String, RStatIntegrityD> integrityMap = rStatIntegrityDList.stream().collect(Collectors.toMap(RStatIntegrityD::getLineIndex, Function.identity()));

        List<LineDataQualityDTO> result = new ArrayList<>();
        for (Line item : lineList) {
            LineDataQualityDTO lineDataQualityDTO = new LineDataQualityDTO();
            lineDataQualityDTO.setLineId(item.getId());
            lineDataQualityDTO.setLineName(item.getName());
            if (onlineMap.containsKey(item.getPid())) {
                lineDataQualityDTO.setOnlineRate(Double.valueOf(onlineMap.get(item.getPid()).getOnlineRate()));
            } else {
                lineDataQualityDTO.setOnlineRate(0.00);
            }
            if (integrityMap.containsKey(item.getId())) {
                lineDataQualityDTO.setIntegrityRate(Double.valueOf(integrityMap.get(item.getId()).getIntegrityData()));
            } else {
                lineDataQualityDTO.setIntegrityRate(0.00);
            }
            result.add(lineDataQualityDTO);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Boolean saveOrUpdateData(List<MonitorIntegrityDTO> monitorIntegrityDTOList) {
        List<String> lineIds = monitorIntegrityDTOList.stream().map(MonitorIntegrityDTO::getId).distinct().collect(Collectors.toList());
        List<LineDetailDataVO> lineDetailInfo = lineDetailMapper.getLineDetailInfo(lineIds);
        Map<String, Integer> collect = lineDetailInfo.stream().collect(Collectors.toMap(LineDetailDataVO::getLineId, LineDetailDataVO::getTimeInterval));
        List<RStatIntegrityD> collect1 = monitorIntegrityDTOList.stream().map(temp -> {
            RStatIntegrityD rStatIntegrityD = new RStatIntegrityD();
            rStatIntegrityD.setTimeId(temp.getDate());
            rStatIntegrityD.setLineIndex(temp.getId());
            rStatIntegrityD.setDueTime(24 * 60 / collect.get(temp.getId()));
            rStatIntegrityD.setRealTime(temp.getTimes());

            return rStatIntegrityD;
        }).collect(Collectors.toList());
        this.saveOrUpdateBatchByMultiId(collect1, 200);
        return true;
    }

    @Override
    public DeviceOnlineRate getData(DeviceInfoParam.BusinessParam param) {
        DeviceOnlineRate rate = new DeviceOnlineRate();
        //获取终端台账类信息
        List<GeneralDeviceDTO> deviceInfo = deviceService.getDeviceInfo(param, null, Collections.singletonList(1));
        if (CollUtil.isNotEmpty(deviceInfo)) {
            List<String> lineIds = deviceInfo.stream()
                    .flatMap(x -> x.getLineIndexes().stream()).collect(Collectors.toList())
                    .stream()
                    .distinct()
                    .collect(Collectors.toList());
            //获取所有监测点的数据完整性
            List<RStatIntegrityVO> lineIntegrityRateInfo = rStatIntegrityDMapper.getLineIntegrityRateInfo(lineIds, param.getSearchBeginTime(), param.getSearchEndTime());
            //获取所有监测点信息信息
            List<LineDetailVO.Detail> LineInfoByIds = lineService.getLineDetailByIds(lineIds);

            rate.setTotalNum(lineIds.size());
            rate.setBelowNum(CollUtil.isNotEmpty(lineIntegrityRateInfo) ? calculateIntegrityRate(lineIntegrityRateInfo, 90, lineIds.size()) : lineIds.size());
            rate.setTotalOnlineRate(calculateIntegrityRate(lineIntegrityRateInfo, lineIds).doubleValue() > 100.0 ? BigDecimal.valueOf(100.0) : calculateIntegrityRate(lineIntegrityRateInfo, lineIds));
            List<DeviceOnlineRate.CitDetail> citDetailList = new ArrayList<>();
            DeviceOnlineRate.CitDetail citDetail;
            DeviceOnlineRate.LineDetail detail;
            for (GeneralDeviceDTO dto : deviceInfo) {
                //获取部门终端集合
                List<RStatIntegrityVO> citDevOnRate = lineIntegrityRateInfo.stream().filter(x -> dto.getLineIndexes().contains(x.getLineIndex())).collect(Collectors.toList());
                Map<String, BigDecimal> onlineRateByDevMap = citDevOnRate.stream()
                        .collect(Collectors.toMap(RStatIntegrityVO::getLineIndex, RStatIntegrityVO::getIntegrityRate));
                citDetail = new DeviceOnlineRate.CitDetail();
                List<LineDetailVO.Detail> lineDetail = LineInfoByIds.stream().filter(x -> dto.getLineIndexes().contains(x.getLineId())).collect(Collectors.toList());
                citDetail.setCitName(dto.getName());
                citDetail.setCitTotalNum(dto.getLineIndexes().size());
                citDetail.setCitBelowNum(CollUtil.isNotEmpty(citDevOnRate) ? calculateIntegrityRate(citDevOnRate, 90, dto.getLineIndexes().size()) : dto.getLineIndexes().size());
                citDetail.setCitTotalOnlineRate(calculateIntegrityRate(lineIntegrityRateInfo, dto.getLineIndexes()).doubleValue() > 100.0 ? BigDecimal.valueOf(100.0) : calculateIntegrityRate(lineIntegrityRateInfo, dto.getLineIndexes()));
                List<DeviceOnlineRate.LineDetail> detailList = new ArrayList<>();
                for (LineDetailVO.Detail line : lineDetail) {
                    detail = new DeviceOnlineRate.LineDetail();
                    detail.setCit(line.getDeptName());
                    detail.setCompany(line.getGdName());
                    detail.setSubStation(line.getSubName());
                    detail.setDeviceId(line.getDevId());
                    detail.setDeviceName(line.getDevName());
                    detail.setManufacturer(line.getManufacturer());
                    detail.setIp(line.getIp());
                    detail.setRunFlag(RunFlagEnum.getRunFlagRemarkByStatus(Integer.valueOf(line.getLineRunType())));
                    detail.setLineId(line.getLineId());
                    detail.setLineName(line.getLineName());
                    //用户侧监测点 监测对象
                    detail.setObjName(StringUtils.isBlank(line.getObjName()) ? "/" : line.getObjName());
                    detail.setLatestTime(line.getTimeID());
                    detail.setIntegrity(onlineRateByDevMap.getOrDefault(line.getLineId(), BigDecimal.valueOf(0)).doubleValue() > 100.0 ? BigDecimal.valueOf(100.0) : onlineRateByDevMap.getOrDefault(line.getLineId(), BigDecimal.valueOf(0)));
                    detailList.add(detail);
                }
                citDetail.setDetailList(detailList);
                citDetailList.add(citDetail);
            }
            rate.setCitDetailList(citDetailList);
        }
        return rate;
    }

    @Override
    public List<OnlineMonitorVo> getNoData(LineBaseQueryParam param) {
        return this.baseMapper.getNoData(param.getLineIds(), param.getStartTime(), param.getEndTime());
    }

    private BigDecimal calculateIntegrityRate(List<RStatIntegrityVO> lineIntegrityRateInfo, List<String> deviceIds) {
        List<RStatIntegrityVO> list = lineIntegrityRateInfo.stream().filter(x -> deviceIds.contains(x.getLineIndex())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(list)) {
            BigDecimal dueTime = BigDecimal.valueOf(list.stream().mapToLong(RStatIntegrityVO::getDueTime).sum());
            BigDecimal realTime = BigDecimal.valueOf(list.stream().mapToLong(RStatIntegrityVO::getRealTime).sum());
            if (BigDecimal.ZERO.compareTo(dueTime) == 0) {
                BigDecimal.valueOf(0);
            }
            return realTime.multiply(BigDecimal.valueOf(100.0)).divide(dueTime, 2, RoundingMode.HALF_UP);
        }
        return BigDecimal.valueOf(0);
    }

    private Integer calculateIntegrityRate(List<RStatIntegrityVO> onlineRateByDev, Integer limit, Integer count) {
        return count - onlineRateByDev.stream().filter(x -> x.getIntegrityRate().doubleValue() >= limit).collect(Collectors.toList()).size();
    }


}
