package com.njcn.prepare.harmonic.service.mysql.dim.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
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.common.pojo.exception.BusinessException;
import com.njcn.common.utils.ReflectCommonUtil;
import com.njcn.device.biz.pojo.dto.DeptGetBusBarDTO;
import com.njcn.device.pms.pojo.po.GeneratrixWire;
import com.njcn.device.pms.pojo.po.Monitor;
import com.njcn.device.pq.pojo.po.RStatIntegrityD;
import com.njcn.harmonic.pojo.po.dim.RDimVoltageD;
import com.njcn.influx.utils.ReflectUitl;
import com.njcn.prepare.harmonic.mapper.mysql.area.PmsMonitorPOMapper;
import com.njcn.prepare.harmonic.mapper.mysql.day.RStatIntegrityDMapper;
import com.njcn.prepare.harmonic.mapper.mysql.dim.GeneratrixWireMapper;
import com.njcn.prepare.harmonic.mapper.mysql.dim.RDimVoltageDMapper;
import com.njcn.prepare.harmonic.pojo.bo.CalculatedParam;
import com.njcn.harmonic.pojo.dto.LineDataDto;
import com.njcn.prepare.harmonic.service.mysql.dim.IRDimVoltageDService;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataTypeEnum;
import com.njcn.system.pojo.po.DictData;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.C;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * <p>
 * 母线不同电压等级指标数据-日表 服务实现类
 * </p>
 *
 * @author xuyang
 * @since 2023-11-27
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class RDimVoltageDServiceImpl extends MppServiceImpl<RDimVoltageDMapper, RDimVoltageD> implements IRDimVoltageDService {

    private final PmsMonitorPOMapper pmsMonitorPOMapper;

    private final GeneratrixWireMapper generatrixWireMapper;

    private final RStatIntegrityDMapper rStatIntegrityDMapper;

    private final DicDataFeignClient dicDataFeignClient;

    @Override
    public void busbarVoltageD(CalculatedParam<DeptGetBusBarDTO> calculatedParam) {
        List<RDimVoltageD> result = new ArrayList<>();
        List<DeptGetBusBarDTO> deptList = calculatedParam.getIdList();
        String beginDay = LocalDateTimeUtil.format(LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.parse(calculatedParam.getDataDate(), DatePattern.NORM_DATE_PATTERN)), DatePattern.NORM_DATETIME_PATTERN);
        String endDay = LocalDateTimeUtil.format(LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.parse(calculatedParam.getDataDate(), DatePattern.NORM_DATE_PATTERN)), DatePattern.NORM_DATETIME_PATTERN);
        Set<String> lineList = new HashSet<>();
        Set<String> busBarList = new HashSet<>();

        //接线方式
        List<DictData> dictData = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.DEV_CONNECT.getCode()).getData();
        Map<String, DictData> dictDataMap = dictData.stream().collect(Collectors.toMap(DictData::getId, Function.identity()));

        //获取监测点集合
        deptList.forEach(item -> {
            lineList.addAll(item.getBusBarAndMonitorIDs());
            busBarList.addAll(item.getBusBarIds());
        });
        if (CollectionUtil.isNotEmpty(lineList)) {
            //获取监测点信息
            List<Monitor> list1 = getMonitorInfo(lineList);
            Map<String, Monitor> monitorMap = list1.stream().collect(Collectors.toMap(Monitor::getId, Function.identity()));

            //获取监测点数据
            List<LineDataDto> list2 = getLineData(beginDay, endDay, lineList);

            for (LineDataDto lineDataDto : list2) {
                Monitor monitor = monitorMap.get(lineDataDto.getLineId());
                Double lv;
                //不同接线类型的线电压反推公式不同
                if (dictDataMap.get(monitor.getTerminalWiringMethod()).getCode().equals("star")) {
                    lv = Math.sqrt(3);
                } else {
                    lv = 1.00;
                }
                lineDataDto.setRms(lineDataDto.getRms() * lv);
            }


            //获取数据完整性
            QueryWrapper<RStatIntegrityD> queryWrapper = new QueryWrapper<>();
            queryWrapper.select("time_id", "line_index", "real_time/due_time as integrityData", "real_time", "due_time").
                    eq("time_id", beginDay);
            List<RStatIntegrityD> rStatIntegrityDList = rStatIntegrityDMapper.selectList(queryWrapper);


            //获取母线信息
            List<GeneratrixWire> list3 = getBusBarInfo(busBarList);
            //循环区域信息,筛选符合条件的数据,做统计
            deptList.forEach(item -> {

                if(item.getUnitId().equals("13B9B47F251C3324E05338297A0A0595")){

                    System.out.println(55);
                }

                List<String> list = item.getBusBarIds();
                if (CollUtil.isNotEmpty(list)) {
                    RDimVoltageD rDimVoltageD = new RDimVoltageD();
                    rDimVoltageD.setOrgId(item.getUnitId());
                    rDimVoltageD.setStatisDate(LocalDate.parse(calculatedParam.getDataDate(), DatePattern.NORM_DATE_FORMATTER));
                    //取母线并集
                    List<GeneratrixWire> list4 = list3.stream().filter(obj -> list.contains(obj.getId())).collect(Collectors.toList());
                    //根据母线电压等级分组
                    Map<String, List<GeneratrixWire>> map1 = list4.stream().collect(Collectors.groupingBy(GeneratrixWire::getScale));
                    //循环分组数据，获取母线下监测点数据
                    map1.forEach((k, v) -> {
                       assignment(k, rDimVoltageD, v, list1, list2, rStatIntegrityDList,result);
                    });
                }
            });
        }
        if (CollectionUtil.isNotEmpty(result)) {
            this.saveOrUpdateBatchByMultiId(result);
        }
    }

    /**
     * 获取监测点信息
     *
     * @param lineList 监测点集合
     * @return 监测点信息
     */
    public List<Monitor> getMonitorInfo(Set<String> lineList) {
        LambdaQueryWrapper<Monitor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(Monitor::getId, lineList).eq(Monitor::getStatus, 1);
        return pmsMonitorPOMapper.selectList(lambdaQueryWrapper);
    }

    /**
     * 获取母线信息
     *
     * @param busBarList 监测点集合
     * @return 母线信息
     */
    public List<GeneratrixWire> getBusBarInfo(Set<String> busBarList) {
        LambdaQueryWrapper<GeneratrixWire> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(GeneratrixWire::getId, busBarList).eq(GeneratrixWire::getStatus, 1);
        return generatrixWireMapper.selectList(lambdaQueryWrapper);
    }

    /**
     * 获取监测点数据
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param lineList  监测点集合
     * @return 母线信息
     */
    public List<LineDataDto> getLineData(String startTime, String endTime, Set<String> lineList) {
        List<LineDataDto> list2 = this.baseMapper.getLineData(startTime, endTime, lineList);
        return list2.stream().filter(i -> i.getRms() > 0).collect(Collectors.toList());
    }

    /**
     * 给实体赋值
     *
     * @param rDimVoltageD 对象
     */
    public void assignment(String voltage, RDimVoltageD rDimVoltageD, List<GeneratrixWire> list1, List<Monitor> list2, List<LineDataDto> list3, List<RStatIntegrityD> rStatIntegrityDList,List<RDimVoltageD> resultList) {
        RDimVoltageD result = new RDimVoltageD();
        RDimVoltageD resultSd = new RDimVoltageD();
        BeanUtils.copyProperties(rDimVoltageD, result);
        result.setDataType("avg");
        BeanUtils.copyProperties(rDimVoltageD, resultSd);
        resultSd.setDataType("sd");
        result.setVoltageType(voltage);
        resultSd.setVoltageType(voltage);
        //不同电压等级下，母线的id
        List<String> l1 = list1.stream().map(GeneratrixWire::getId).collect(Collectors.toList());
        //获取这些母线下的监测点
        List<Monitor> l2 = list2.stream().filter(obj -> l1.contains(obj.getLineId())).collect(Collectors.toList());

        List<RStatIntegrityD> rStatIntegrityDS = rStatIntegrityDList.stream().filter(it -> l2.stream().map(Monitor::getId).collect(Collectors.toList()).contains(it.getLineIndex())).collect(Collectors.toList());

        Map<String, List<Monitor>> mapMonitor = l2.stream().collect(Collectors.groupingBy(Monitor::getLineId));

        List<LineDataDto> l3 = new ArrayList<>();
        mapMonitor.forEach((busId, list) -> {
            RStatIntegrityD rStatIntegrityD = rStatIntegrityDS.stream().filter(i -> list.stream().map(Monitor::getId).collect(Collectors.toList()).contains(i.getLineIndex())).max(Comparator.comparing(RStatIntegrityD::getIntegrityData)).orElse(null);
            if (Objects.nonNull(rStatIntegrityD)) {
                //获取监测点数据
                List<LineDataDto> pointList = list3.stream().filter(vo -> vo.getLineId().equals(rStatIntegrityD.getLineIndex())).collect(Collectors.toList());
                l3.addAll(pointList);
            }
        });

        if (CollectionUtil.isNotEmpty(l3)) {
            double v_avg = l3.stream().mapToDouble(LineDataDto::getRms).average().orElse(0d);
            result.setV(v_avg);
            double v_sdf = 0.0;
            for (LineDataDto lineDataDto : l3) {
                try {
                    v_sdf += (lineDataDto.getRms() - v_avg) * (lineDataDto.getRms() - v_avg);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            }
            resultSd.setV(Math.sqrt(v_sdf / l3.size()));


            double v_unbalance = l3.stream().mapToDouble(LineDataDto::getVUnbalance).average().orElse(0d);
            result.setVUnbalance(v_unbalance);
            double v_unbalance_df = 0.0;
            for (LineDataDto lineDataDto : l3) {
                try {
                    v_unbalance_df += (lineDataDto.getVUnbalance() - v_unbalance) * (lineDataDto.getVUnbalance() - v_unbalance);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            }
            resultSd.setVUnbalance(Math.sqrt(v_unbalance_df / l3.size()));


            double v_pst = l3.stream().mapToDouble(LineDataDto::getPst).average().orElse(0d);
            result.setPst(v_pst);
            double v_pst_df = 0.0;
            for (LineDataDto lineDataDto : l3) {
                try {
                    v_pst_df += (lineDataDto.getPst() - v_pst) * (lineDataDto.getPst() - v_pst);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            }
            resultSd.setPst(Math.sqrt(v_pst_df / l3.size()));


            double v_thd = l3.stream().mapToDouble(LineDataDto::getVThd).average().orElse(0d);
            result.setVThd(v_thd);
            double v_thd_df = 0.0;
            for (LineDataDto lineDataDto : l3) {
                try {
                    v_thd_df += (lineDataDto.getVThd() - v_thd) * (lineDataDto.getVThd() - v_thd);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            }
            resultSd.setVThd(Math.sqrt(v_thd_df / l3.size()));

            double v_ineg = l3.stream().mapToDouble(LineDataDto::getINeg).average().orElse(0d);
            result.setINeg(v_ineg);
            double v_ineg_df = 0.0;
            for (LineDataDto lineDataDto : l3) {
                try {
                    v_ineg_df += (lineDataDto.getINeg() - v_ineg) * (lineDataDto.getINeg() - v_ineg);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            }
            resultSd.setINeg(Math.sqrt(v_ineg_df / l3.size()));

            result.setINeg(l3.stream().mapToDouble(LineDataDto::getINeg).average().orElse(0d));
            calculateAveragesAndSet(l3,result,resultSd);
            resultList.add(result);
            resultList.add(resultSd);
        }

    }


    public void calculateAveragesAndSet(List<LineDataDto> l3, RDimVoltageD result,RDimVoltageD resultSd) {

            // 从V1到V50，可以使用IntStream来迭代这些索引
            IntStream.rangeClosed(1, 50)
                    .forEach(i -> {
                        // 构建getter和setter方法的名字
                        String getterName = "getV" + i;
                        String setterName = "setV" + i;

                        // 获取getter和setter的Method对象
                        try {
                            Method getter = LineDataDto.class.getMethod(getterName);
                            Method setter = result.getClass().getMethod(setterName, Double.class);
                            Method setterSd = resultSd.getClass().getMethod(setterName, Double.class);

                            // 计算平均值
                            OptionalDouble average = l3.stream()
                                    .mapToDouble(lineDataDto -> {
                                        try {
                                            return (double) getter.invoke(lineDataDto);
                                        } catch (Exception e) {
                                            throw new RuntimeException(e);
                                        }
                                    })
                                    .average();

                            // 设置结果
                            if (average.isPresent()) {
                                double tem = average.getAsDouble();
                                setter.invoke(result,tem);

                                double sd = getTemParam(l3, tem, i);
                                setterSd.invoke(resultSd,sd);
                            } else {
                                // 如果列表为空，可以设置默认值或其他逻辑
                                setter.invoke(result, 0d);
                                setterSd.invoke(resultSd,0d);
                            }
                        } catch (Exception e) {
                            log.error(e.getMessage());
                        }
                    });


    }

    private Double getTemParam(List<LineDataDto> l3, Double avg, Integer count) {
        double tem = 0.0;
        for (LineDataDto lineDataDto : l3) {
            try {
                double vvv = (double) LineDataDto.class.getMethod("getV" + count).invoke(lineDataDto);
                tem += (vvv - avg) * (vvv - avg);
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
        return Math.sqrt(tem/l3.size());
    }

}
