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


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.dto.SimpleDTO;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.param.StatisticsBizBaseParam;
import com.njcn.device.pms.enums.PmsDeviceResponseEnum;
import com.njcn.device.pms.mapper.SubstationExpendMapper;
import com.njcn.device.pms.mapper.majornetwork.DistributionMonitorMapper;
import com.njcn.device.pms.mapper.majornetwork.MonitorMapper;
import com.njcn.device.pms.pojo.dto.PmsGeneralDeviceDTO;
import com.njcn.device.pms.pojo.dto.PmsLedgerStaticDTO;
import com.njcn.device.pms.pojo.dto.PmsSimpleDTO;
import com.njcn.device.pms.pojo.param.PmsDeviceInfoParam;
import com.njcn.device.pms.pojo.param.SubstationExpendParam;
import com.njcn.device.pms.pojo.po.DistributionMonitor;
import com.njcn.device.pms.pojo.po.Monitor;
import com.njcn.device.pms.pojo.vo.StationExpendMonitorVO;
import com.njcn.device.pms.pojo.vo.SubstationExpendVO;
import com.njcn.device.pms.pojo.po.PmsTerminal;
import com.njcn.device.pms.pojo.po.SubstationExpend;
import com.njcn.device.pms.service.ISubstationExpendService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.device.pms.service.majornetwork.IPmsGeneralDeviceService;
import com.njcn.device.pms.service.majornetwork.ITerminalService;
import com.njcn.device.pq.pojo.param.CommunicateParam;
import com.njcn.device.pq.pojo.vo.CommunicateVO;
import com.njcn.influx.imapper.PqsCommunicateMapper;
import com.njcn.influx.pojo.constant.InfluxDBTableConstant;
import com.njcn.influx.pojo.po.PqsCommunicate;
import com.njcn.influx.query.InfluxQueryWrapper;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataTypeEnum;
import com.njcn.system.pojo.po.DictData;
import com.njcn.user.api.DeptFeignClient;
import com.njcn.user.pojo.dto.DeptDTO;
import com.njcn.user.pojo.po.Dept;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zhuxinyu
 * @since 2023-05-08
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SubstationExpendServiceImpl extends ServiceImpl<SubstationExpendMapper, SubstationExpend> implements ISubstationExpendService {

    private final DeptFeignClient deptFeignClient;

    private final SubstationExpendMapper substationExpendMapper;

    private final IPmsGeneralDeviceService pmsGeneralDeviceService;

    private final DistributionMonitorMapper distributionMonitorMapper;

    private final DicDataFeignClient dicDataFeignClient;

    private final ITerminalService iTerminalService;

    private final PqsCommunicateMapper pqsCommunicateMapper;

    private final MonitorMapper monitorMapper;

    @Override
    public Map<String, List<SubstationExpendVO>> getSubstationExpendInfo(StatisticsBizBaseParam param) {
        Map<String, List<SubstationExpendVO>> map = new HashMap<>();
        List<DeptDTO> deptInfos = deptFeignClient.getDeptDescendantIndexes(param.getId(), Stream.of(0, 1).collect(Collectors.toList())).getData();
        Map<String, List<DeptDTO>> deptMap = deptInfos.stream().collect(Collectors.groupingBy(DeptDTO::getCode));
        if (CollectionUtil.isNotEmpty(deptInfos)) {
            List<String> list = deptInfos.stream()
                    .map(DeptDTO::getCode)
                    .collect(Collectors.toList());
            // 获取部门所属变电站
            List<SubstationExpendVO> subList=substationExpendMapper.getSubList(list, param);
            if (CollUtil.isNotEmpty(subList)) {
                subList.forEach (
                        t -> {
                            if (t.getDeptId()!=null){
                                t.setArea(deptMap.get(t.getDeptId()).get(0).getArea());
                            }
                            if (t.getBreakSite()!=null) {
                                List<PmsSimpleDTO> simpleDTOS = new ArrayList<>();
                                String[][] arr = JSON.parseObject(t.getBreakSite(), String[][].class);
                                List<String[]> sites = Arrays.asList(arr);
                                for (String[] site : sites) {
                                    PmsSimpleDTO simpleDTO = new PmsSimpleDTO();
                                    simpleDTO.setName("");
                                    Double[] ds=new Double[site.length];
                                    for(int i=0;i<site.length;i++){
                                        ds[i]=Double.valueOf(site[i]);
                                    }
                                    simpleDTO.setValue(ds);
                                    simpleDTOS.add(simpleDTO);
                                }
                                t.setSites(simpleDTOS);
                            }
                        }
                );
                map = subList.stream().collect(Collectors.groupingBy(SubstationExpendVO::getArea));
            }
        }
        return map;
    }

    @Override
    public PmsLedgerStaticDTO getLedgerStatistics(StatisticsBizBaseParam param) {
        PmsLedgerStaticDTO ledgerStaticDTO = new PmsLedgerStaticDTO();
        PmsDeviceInfoParam pmsDeviceInfoParam = new PmsDeviceInfoParam();
        pmsDeviceInfoParam.setDeptIndex(param.getId());
        pmsDeviceInfoParam.setStatisticalType(new SimpleDTO());
        List<PmsGeneralDeviceDTO> generalDeviceDTOS = pmsGeneralDeviceService.getPmsDeviceInfoWithInOrg(pmsDeviceInfoParam);
        List<DictData> data = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.DEV_VOLTAGE.getCode()).getData();
        Map<String, DictData> collect = data.stream().collect(Collectors.toMap(DictData::getId, Function.identity()));

        // 统计监测点
        List<String> monitorIds = new ArrayList<>();
        generalDeviceDTOS.stream().forEach(e -> monitorIds.addAll(e.getMonitorIdList()));
        List<PmsSimpleDTO> monitorStatistic = distributionMonitorMapper.statisticMonitor(monitorIds);
        Map<String, Long> collect1 = monitorStatistic.stream().collect(Collectors.groupingBy(PmsSimpleDTO::getName, Collectors.counting()));
        List<PmsSimpleDTO> monitor=new ArrayList<>();
        PmsSimpleDTO pDto;
        for (Map.Entry<String, Long> stringLongEntry : collect1.entrySet()) {
            pDto=new PmsSimpleDTO();
            pDto.setName(stringLongEntry.getKey());
            pDto.setStatistics(Math.toIntExact(stringLongEntry.getValue()));
            monitor.add(pDto);
        }
        ledgerStaticDTO.setMonitorStatistics(monitor);


        //获取所有监测点信息
        List<PmsSimpleDTO> pmsSimpleDTOS = monitorStatistic;

        //根据电压等级进行分组
        Map<String, List<PmsSimpleDTO>> voltageMap = pmsSimpleDTOS.stream().collect(Collectors.groupingBy(
              x-> {if (collect.containsKey(x.getName())){
                  DictData dictData = collect.get(x.getName());
                  return dictData.getName()+"_"+dictData.getSort();
              }
                  return null;
              }
               ));
        // 根据单位统计监测点数量
        List<List<PmsSimpleDTO.PmsSimpleOrg>> list=new ArrayList<>();
        for (PmsGeneralDeviceDTO dto : generalDeviceDTOS) {
            List<PmsSimpleDTO.PmsSimpleOrg> dtoList=new ArrayList<>();
            for (Map.Entry<String, List<PmsSimpleDTO>> stringListEntry : voltageMap.entrySet()) {
                if(StrUtil.isNotBlank(stringListEntry.getKey())){
                    List<PmsSimpleDTO> monitorList = stringListEntry.getValue().stream().filter(x -> dto.getMonitorIdList().contains(x.getId())).collect(Collectors.toList());
                    PmsSimpleDTO.PmsSimpleOrg simpleDTO=new PmsSimpleDTO.PmsSimpleOrg();
                    if(CollUtil.isNotEmpty(monitorList)){
                        simpleDTO.setStatistics((int) monitorList.stream().mapToDouble(PmsSimpleDTO::getStatistics).sum());
                    }else{
                        simpleDTO.setStatistics(0);
                    }
                    String key = stringListEntry.getKey();
                    String[] split = key.split("_");
                    simpleDTO.setName(split[0]);
                    simpleDTO.setSort(Integer.valueOf(split[1]));
                    simpleDTO.setOrgName(dto.getName());
                    dtoList.add(simpleDTO);
                }

            }
            dtoList.sort(Comparator.comparing(r -> r.getSort()));
            list.add(dtoList);
        }
        ledgerStaticDTO.setMonitorStatisticsByDept(list);
        // 统计终端
        List<String> terminalIds = new ArrayList<>();
        List<PmsGeneralDeviceDTO> filterList = generalDeviceDTOS.stream().filter(e -> e.getTerminalIdList().size() != 0).collect(Collectors.toList());
        filterList.stream().forEach(e -> terminalIds.addAll(e.getTerminalIdList()));
        terminalIds.removeAll(Collections.singleton(null));
        List<PmsSimpleDTO> terminalNumStatistic = distributionMonitorMapper.statisticTerminal(terminalIds);
        ledgerStaticDTO.setTerminalStatistics(terminalNumStatistic);

        //获取所有终端信息
        List<String> terminalList = generalDeviceDTOS.stream().flatMap(dto -> dto.getTerminalIdList().stream()).collect(Collectors.toList());
        List<PmsSimpleDTO> dtoList = distributionMonitorMapper.statisticTerminal(terminalList);
        //根据终端电压等级进行分组
        Map<String, List<PmsSimpleDTO>> terminalMap = dtoList.stream().collect(Collectors.groupingBy(
                x-> {if (collect.containsKey(x.getName())){
                    DictData dictData = collect.get(x.getName());
                    return dictData.getName()+"_"+dictData.getSort();
                }
                    return null;
                }
        ));
        // 根据单位统计终端数量
        List<List<PmsSimpleDTO.PmsSimpleOrg>> terminalInfo=new ArrayList<>();
        for (PmsGeneralDeviceDTO dto : generalDeviceDTOS) {

            List<PmsSimpleDTO.PmsSimpleOrg> terminal=new ArrayList<>();
            for (Map.Entry<String, List<PmsSimpleDTO>> stringListEntry : terminalMap.entrySet()) {
                if(StrUtil.isNotBlank(stringListEntry.getKey())){
                    List<PmsSimpleDTO> monitorList = stringListEntry.getValue().stream().filter(x -> dto.getTerminalIdList().contains(x.getId())).collect(Collectors.toList());
                    PmsSimpleDTO.PmsSimpleOrg simpleDTO=new PmsSimpleDTO.PmsSimpleOrg();
                    if(CollUtil.isNotEmpty(monitorList)){
                        simpleDTO.setStatistics((int) monitorList.stream().mapToDouble(PmsSimpleDTO::getStatistics).sum());
                    }else{
                        simpleDTO.setStatistics(0);
                    }
                    String key = stringListEntry.getKey();
                    String[] split = key.split("_");
                    simpleDTO.setName(split[0]);
                    simpleDTO.setSort(Integer.valueOf(split[1]));
                    simpleDTO.setOrgName(dto.getName());
                    terminal.add(simpleDTO);
                }

            }
            terminal.sort(Comparator.comparing(r -> r.getSort()));
            terminalInfo.add(terminal);
        }
        ledgerStaticDTO.setTerminalStatisticsByDept(list);


        // 统计在线率
        List<PmsSimpleDTO> onlineRateStatistic =  distributionMonitorMapper.getOnlineRate(terminalIds,param);
        double sum = onlineRateStatistic.stream().mapToDouble(PmsSimpleDTO::getDoubleValue).sum()!=0?onlineRateStatistic.stream().mapToDouble(PmsSimpleDTO::getDoubleValue).sum():3.14159;
        onlineRateStatistic.forEach(dto -> dto.setDoubleValue(new BigDecimal(dto.getDoubleValue() * 100 / sum).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue()));
        ledgerStaticDTO.setOnlineRateStatistics(onlineRateStatistic);

        // 完整性
        List<PmsSimpleDTO> integrityStatistic =  distributionMonitorMapper.getIntegrity(monitorIds,param);
        double s = integrityStatistic.stream().mapToDouble(PmsSimpleDTO::getDoubleValue).sum()!=0?integrityStatistic.stream().mapToDouble(PmsSimpleDTO::getDoubleValue).sum():3.14159;
        integrityStatistic.forEach(dto -> dto.setDoubleValue(new BigDecimal(dto.getDoubleValue() * 100 / s).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue()));
        ledgerStaticDTO.setIntegrityStatistics(integrityStatistic);
        return ledgerStaticDTO;
    }

    @Override
    public List<CommunicateVO.PVCommunicateVO> getCommunicationStatus(CommunicateParam param) {
        PmsDeviceInfoParam pmsDeviceInfoParam = new PmsDeviceInfoParam();
        pmsDeviceInfoParam.setDeptIndex(param.getId());
        pmsDeviceInfoParam.setStatisticalType(new SimpleDTO());
        List<PmsGeneralDeviceDTO> generalDeviceDTOS = pmsGeneralDeviceService.getPmsDeviceInfoWithInOrg(pmsDeviceInfoParam);
        //获取所有终端id
        List<String> terminalIds = generalDeviceDTOS.stream().flatMap(dto -> dto.getTerminalIdList().stream()).collect(Collectors.toList());

        List<CommunicateVO.PVCommunicateVO> info =new ArrayList<>();

        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(PqsCommunicate.class);
        influxQueryWrapper.between(InfluxDBTableConstant.TIME,
                DateUtil.beginOfDay(DateUtil.parse(param.getStartTime())).toString(),
                DateUtil.beginOfDay(DateUtil.parse(param.getEndTime()))).toString();
        if(CollUtil.isNotEmpty(terminalIds)){
            influxQueryWrapper.or(PqsCommunicate::getDevId,terminalIds);
        }
        influxQueryWrapper.limit(param.getLimit());
        List<PqsCommunicate> communicates = pqsCommunicateMapper.selectByQueryWrapper(influxQueryWrapper);
        List<String> ids = communicates.stream().map(PqsCommunicate::getDevId).distinct().collect(Collectors.toList());
        if(CollUtil.isNotEmpty(ids)){
            List<PmsTerminal> terminalSelectByIds = iTerminalService.getTerminalSelectByIds(ids);
            Map<String, String> terminalNameIp = terminalSelectByIds.stream().collect(Collectors.toMap(PmsTerminal::getId, x -> x.getName() + "_" + x.getIp()));
            CommunicateVO.PVCommunicateVO communicate;
            for (PqsCommunicate cate : communicates) {
                communicate=new CommunicateVO.PVCommunicateVO();
                BeanUtil.copyProperties(cate, communicate);
                communicate.setTime(cate.getTime());
                if(terminalNameIp.containsKey(cate.getDevId())){
                    communicate.setDevIp(terminalNameIp.get(cate.getDevId()));
                }
                info.add(communicate);
            }
        }
        return info;
    }

    @Override
    public Page<SubstationExpend> getSubstationExpend(SubstationExpendParam param) {
        List<DeptDTO> deptInfos = deptFeignClient.getDeptDescendantIndexes(param.getOrgId(), Stream.of(0, 1).collect(Collectors.toList())).getData();
        Map<String, DeptDTO> deptMap = deptInfos.stream().collect(Collectors.toMap(DeptDTO::getCode, Function.identity()));
        List<String> deptIDs = deptInfos.stream().map(DeptDTO::getCode).collect(Collectors.toList());
        if(CollUtil.isNotEmpty(deptIDs)){
            Page<SubstationExpend> page = this.page(new Page<>(param.getPageNum(), param.getPageSize()), new LambdaQueryWrapper<SubstationExpend>()
                    .in(SubstationExpend::getDeptId, deptIDs)
                    .like(StrUtil.isNotBlank(param.getSearchValue()),SubstationExpend::getName,param.getSearchValue())

            );
            List<SubstationExpend> records = page.getRecords();
            if(CollUtil.isNotEmpty(records)){
                for (SubstationExpend record : records) {
                    if(deptMap.containsKey(record.getDeptId())){
                        record.setDeptName(deptMap.get(record.getDeptId()).getName());
                        record.setDeptId(deptMap.get(record.getDeptId()).getId());

                    }
                }
            }
            return page;
        }
        return new Page<>();
    }

    @Override
    public Boolean addSubstation(SubstationExpend param) {
        checkName(param,true);
        Dept data = deptFeignClient.getDeptById(param.getDeptId()).getData();
        if(ObjectUtil.isNull(data)){
            throw new BusinessException(PmsDeviceResponseEnum.ORG_ITEM_EMPTY);
        }
        param.setDeptId(data.getCode());

        //判断此电站是否绑定pms电站表里的电站
        if(StrUtil.isNotBlank(param.getSubstationId())){
            LambdaQueryWrapper<SubstationExpend> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(SubstationExpend::getSubstationId,param.getSubstationId());
            int count = this.count(lambdaQueryWrapper);
            if(count>0){
                throw new BusinessException("关联电站已被绑定！");
            }
        }
        return this.save(param);
    }

    @Override
    public Boolean updateSubstation(SubstationExpend param) {
        checkName(param,false);
        Dept data = deptFeignClient.getDeptById(param.getDeptId()).getData();
        if(ObjectUtil.isNull(data)){
            throw new BusinessException(PmsDeviceResponseEnum.ORG_ITEM_EMPTY);
        }
        param.setDeptId(data.getCode());
        //判断此电站是否绑定pms电站表里的电站
        if(StrUtil.isNotBlank(param.getSubstationId())){
            LambdaQueryWrapper<SubstationExpend> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(SubstationExpend::getSubstationId,param.getSubstationId())
                    .ne(SubstationExpend::getId,param.getId());
            int count = this.count(lambdaQueryWrapper);
            if(count>0){
                throw new BusinessException("关联电站已被绑定！");
            }
        }
        boolean b = this.updateById(param);
        return b;
    }

    @Override
    public List<StationExpendMonitorVO> getSubStationMonitor(String stationId) {
        List<StationExpendMonitorVO> substationExpendVOList = new ArrayList<>();
        SubstationExpend substationExpend = this.getById(stationId);
        if(Objects.nonNull(substationExpend)) {
            if(StrUtil.isNotBlank(substationExpend.getSubstationId())){
                LambdaQueryWrapper<Monitor> monitorLambdaQueryWrapper = new LambdaQueryWrapper<>();
                monitorLambdaQueryWrapper.eq(Monitor::getPowerrId, substationExpend.getSubstationId());
                List<Monitor> monitorList = monitorMapper.selectList(monitorLambdaQueryWrapper);
                monitorList.forEach(item -> {
                    StationExpendMonitorVO stationExpendMonitorVO = new StationExpendMonitorVO();
                    stationExpendMonitorVO.setStationId(stationId);
                    stationExpendMonitorVO.setMonitorId(item.getId());
                    stationExpendMonitorVO.setMonitorName(item.getName());
                    stationExpendMonitorVO.setObjType(item.getMonitorTag());
                    substationExpendVOList.add(stationExpendMonitorVO);
                });
            }
        }
        return substationExpendVOList;
    }

    /**
     * 名称重复校验
     */
    private void checkName(SubstationExpend param, boolean flag){
        LambdaQueryWrapper<SubstationExpend> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SubstationExpend::getName,param.getName());
        //修改
        if(!flag){
            lambdaQueryWrapper.ne(SubstationExpend::getId,param.getId());
        }
        int result = this.count(lambdaQueryWrapper);
        //大于等于1个则表示重复
        if (result >= 1) {
            throw new BusinessException(PmsDeviceResponseEnum.MODEL_NAME_REPEAT);
        }

    }
}
