package cn.cecep.talroad.service.impl;

import cn.cecep.talroad.common.core.utils.DateUtils;
import cn.cecep.talroad.common.core.utils.StringUtils;
import cn.cecep.talroad.domain.BaseRegionalControlObjectives;
import cn.cecep.talroad.domain.PcPollutant;
import cn.cecep.talroad.mapper.BaseRegionalControlObjectivesMapper;
import cn.cecep.talroad.mapper.PcOutletMapper;
import cn.cecep.talroad.mapper.PcPollutantMapper;
import cn.cecep.talroad.mapper.SEnvGasMonRealMapper;
import cn.cecep.talroad.service.EmissionsStatService;
import cn.cecep.talroad.vo.EChartsVO;
import cn.cecep.talroad.vo.EmissionsStatVo;
import cn.cecep.talroad.vo.SEnvGasMonRealVo;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class EmissionsStatServiceImpl implements EmissionsStatService {
    @Autowired
    private PcPollutantMapper pollutantMapper;

    @Autowired
    private PcOutletMapper outletMapper;

    @Autowired
    private SEnvGasMonRealMapper gasMonRealMapper;

    @Autowired
    private BaseRegionalControlObjectivesMapper regionalControlObjectivesMapper;

    @Override
    public List<EmissionsStatVo.PointRow> getPointList(IPage<EmissionsStatVo.PointRow> page, EmissionsStatVo.PointSearch search) {

        if (StringUtils.isNotEmpty(search.getStartTime()) &&StringUtils.isNotEmpty(search.getEndTime())) {
            int days = DateUtils.daysBetween(search.getStartTime(), search.getEndTime());
            if (days > 31) {
                throw new RuntimeException("查询时间范围大于一个月");
            }
        }

        /*
            查询出所有纳入计算的监测点管控浓度标准
         */
        List<EmissionsStatVo.PointRow> rows = outletMapper.selectEmissionsStatPointList(search);

        if (rows.isEmpty()) {
            return rows;
        }

        // 查询大气监管污染物，并且设置了gas_mon_real_property字段的
        List<PcPollutant> pollutants = pollutantMapper.selectList(new QueryWrapper<PcPollutant>()
                .eq("is_gas_type", "1")
                .isNotNull("gas_mon_real_property"));
        Map<String, PcPollutant> pollutantMap = pollutants.stream().collect(Collectors.toMap(o -> o.getPollCode(), o -> o));

        // >> 计算指定时间范围内监测点排放量相关字段
        if (StringUtils.isNotEmpty(search.getStartTime()) && StringUtils.isNotEmpty(search.getEndTime())) {

            // 汇总监测点位id
            Set<String> outletIds = rows.stream().map(o -> o.getPointId()).collect(Collectors.toSet());

            // 查询指定时间范围内的监测点监测数据
            List<SEnvGasMonRealVo.EmissionSumVo> gasMonReals1 = gasMonRealMapper.selectEmissionStat(search.getStartTime(), search.getEndTime(), outletIds);
            Map<String, SEnvGasMonRealVo.EmissionSumVo> outletEmissionMap1 = gasMonReals1.stream().collect(Collectors.toMap(o -> o.getOutletId(), o -> o));

            for (EmissionsStatVo.PointRow row : rows) {
                SEnvGasMonRealVo.EmissionSumVo emissionVo = outletEmissionMap1.get(row.getPointId());
                PcPollutant pcPollutant = pollutantMap.get(row.getPollutantCode());

                // 当前监测点的监测数据存在
                if (emissionVo != null) {
                    /*
                        设置实际排放量，需求如下：
                        实际排放量 = 在线监测系统对应小时排放量数据
                        TODO 当排放量大于上限值时用前720小时最大排放量进行替换，该功能待确定如何实现
                     */
                    if (pcPollutant != null) {
                        // 获取污染物的排放量
                        BigDecimal sum = BeanUtil.getProperty(emissionVo, pcPollutant.getGasMonRealProperty() + "Sum");
                        row.setRealEmissions(sum);
                    }

                    /*
                        设置管控浓度排放量，需求如下：
                        管控浓度排放量=管控浓度设置模块设置的浓度乘以废气小时排放量除以1000000
                        TODO 当排放量大于上限值时用前720小时管控浓度排放量的最大排放量进行替换，该功能待确定如何实现
                     */
                    if (row.getControlStandard() != null && emissionVo.getOxygenEmissionSum() != null) {
                        BigDecimal manageEmissions = row.getControlStandard().multiply(emissionVo.getOxygenEmissionSum())
                                .divide(BigDecimal.valueOf(1000000), 6, BigDecimal.ROUND_HALF_UP);
                        row.setManageEmissions(manageEmissions);
                    }

                    row.setHourDataCount(emissionVo.getHourDataCount());
                }

                /*
                    管控浓度排放量与实际排放量差值 = 管控浓度排放量-实际排放量
                 */
                if (row.getManageEmissions() != null && row.getRealEmissions() != null) {
                    row.setManageRealDiff(row.getManageEmissions().subtract(row.getRealEmissions()));
                }

                /*
                    管控浓度排放量与实际排放量差值小于0时，是超标
                 */
                if (row.getManageRealDiff() != null) {
                    row.setOver(row.getManageRealDiff().compareTo(BigDecimal.valueOf(0)) < 0 ? "是" : "否");
                }
            }
        }

        /*
            由于是否超标只有计算后才能得到，因此页面上要根据是否超标进行查询，只能查询出所有的数据计算完成后才能过滤、分页
         */
        // 根据是否超标过滤
        if (StringUtils.isNotEmpty(search.getOver())) {
            String over = search.getOver().equals("1") ? "是" : "否";
            rows = rows.stream().filter(o -> over.equals(o.getOver())).collect(Collectors.toList());
        }

        // 分页
        if (page != null) {
            page.setTotal(rows.size());
            rows = rows.stream()
                    .skip((page.getCurrent() - 1) * page.getSize())
                    .limit(page.getSize())
                    .collect(Collectors.toList());
        }
        return rows;
    }

    @Override
    public List<EmissionsStatVo.FactoryRow> getFactoryList(IPage<EmissionsStatVo.FactoryRow> page, EmissionsStatVo.FactorySearch search) {
        EmissionsStatVo.PointSearch pointSearch = new EmissionsStatVo.PointSearch();
        BeanUtils.copyProperties(search, pointSearch, "over"); // 不拷贝是否超标，企业的是否超标需要根据监测点的数据重新计算

        // 不分页获取所有监测点数据
        List<EmissionsStatVo.PointRow> pointList = getPointList(null, pointSearch);

        if (pointList.isEmpty()) {
            return new ArrayList<>();
        }

        // 按照企业和污染物分组，计算排放量相关字段
        Map<String, List<EmissionsStatVo.PointRow>> collect = pointList.stream()
                .collect(Collectors.groupingBy(o -> o.getFactoryId() + "_" + o.getPollutantCode(),
                        // 用LinkedHashMap，保证分组顺序和查询顺序一样
                        LinkedHashMap::new, Collectors.toList()));

        List<EmissionsStatVo.FactoryRow> rows = collect.entrySet().stream().map(entry -> {
            EmissionsStatVo.PointRow pointRow = entry.getValue().get(0);

            EmissionsStatVo.FactoryRow row = new EmissionsStatVo.FactoryRow();
            row.setFactoryName(pointRow.getFactoryName());
            row.setRegionName(pointRow.getRegionName());
            row.setIndustryName(pointRow.getIndustryName());
            row.setPollutantName(pointRow.getPollutantName());
            row.setRegionCode(pointRow.getRegionCode());
            row.setFactoryId(pointRow.getFactoryId());

            BigDecimal manageEmissions = entry.getValue().stream().filter(o -> o.getManageEmissions() != null)
                    .map(EmissionsStatVo.BaseRow::getManageEmissions)
                    .reduce(BigDecimal::add).orElse(null);
            row.setManageEmissions(manageEmissions);

            BigDecimal realEmissions = entry.getValue().stream().filter(o -> o.getRealEmissions() != null)
                    .map(EmissionsStatVo.BaseRow::getRealEmissions)
                    .reduce(BigDecimal::add).orElse(null);
            row.setRealEmissions(realEmissions);

            BigDecimal nonKeyRealEmissions = entry.getValue().stream()
                    // 找到非重点的并且实际排放量不为null的
                    .filter(o -> o.getRealEmissions() != null && "0".equals(o.getControlPoints()))
                    .map(EmissionsStatVo.BaseRow::getRealEmissions)
                    .reduce(BigDecimal::add).orElse(null);
            row.setNonKeyRealEmissions(nonKeyRealEmissions);

            /*
                基准排放量=管控浓度排放量+非重点管控点位实际排放量
             */
            if (row.getManageEmissions() != null || row.getNonKeyRealEmissions() != null) {
                BigDecimal basisEmissions = new BigDecimal(0);
                if (row.getManageEmissions() != null) {
                    basisEmissions = basisEmissions.add(row.getManageEmissions());
                }
                if (row.getNonKeyRealEmissions() != null) {
                    basisEmissions = basisEmissions.add(row.getNonKeyRealEmissions());
                }
                row.setBasisEmissions(basisEmissions);
            }

            /*
                基准排放量与实际排放量差值
             */
            if (row.getBasisEmissions() != null && row.getRealEmissions() != null) {
                row.setBasisRealDiff(row.getBasisEmissions().subtract(row.getRealEmissions()));
            }

            /*
                基准排放量与实际排放量差值小于0时，是超标
             */
            if (row.getBasisRealDiff() != null) {
                row.setOver(row.getBasisRealDiff().compareTo(BigDecimal.valueOf(0)) < 0 ? "是" : "否");
            }
            return row;
        }).collect(Collectors.toList());

        // 根据是否超标过滤
        if (StringUtils.isNotEmpty(search.getOver())) {
            String over = search.getOver().equals("1") ? "是" : "否";
            rows = rows.stream().filter(o -> over.equals(o.getOver())).collect(Collectors.toList());
        }

        // 分页
        if (page != null) {
            page.setTotal(rows.size());
            rows = rows.stream()
                    .skip((page.getCurrent() - 1) * page.getSize())
                    .limit(page.getSize())
                    .collect(Collectors.toList());
        }
        return rows;
    }

    @Override
    public EmissionsStatVo.Region getRegionStat(EmissionsStatVo.RegionSearch search) {
        if (StringUtils.isEmpty(search.getPollutantCode())) {
            throw new RuntimeException("请指定一个污染物种类");
        }

        EChartsVO.SeriesObj manageTargetSer = new EChartsVO.SeriesObj();
        manageTargetSer.setName("管控目标排放量");
        manageTargetSer.setValues(new ArrayList<>());

        EChartsVO.SeriesObj basisSer = new EChartsVO.SeriesObj();
        basisSer.setName("基准排放量");
        basisSer.setValues(new ArrayList<>());

        EChartsVO.SeriesObj realSer = new EChartsVO.SeriesObj();
        realSer.setName("实际排放量");
        realSer.setValues(new ArrayList<>());

        EChartsVO chartsVO = new EChartsVO();
        chartsVO.setXData(new ArrayList<>());
        chartsVO.setSeries(Arrays.asList(manageTargetSer, basisSer, realSer));

        EmissionsStatVo.Region responseData = new EmissionsStatVo.Region();
        responseData.setRows(new ArrayList<>());
        responseData.setChartsVO(chartsVO);

        EmissionsStatVo.PointSearch pointSearch = new EmissionsStatVo.PointSearch();
        BeanUtils.copyProperties(search, pointSearch, "over"); // 不拷贝是否超标，区域的是否超标需要根据监测点的数据重新计算

        // 不分页获取所有监测点数据
        List<EmissionsStatVo.PointRow> pointList = getPointList(null, pointSearch);
        if (pointList.isEmpty()) {
            return responseData;
        }

        // 查询区县管控目标设置
        List<BaseRegionalControlObjectives> regionalControlObjectives = regionalControlObjectivesMapper.selectList(new QueryWrapper<>());

        /*
            准备表格数据
         */
        // 若未选择行业，则将行业名称属性改完全部
        if (StringUtils.isEmpty(search.getIndustryCode())) {
            pointList.forEach(o -> o.setIndustryName("全部"));
        }

        // 按照区域、行业和污染物种类分组，计算排放量相关字段
        Map<String, List<EmissionsStatVo.PointRow>> collect = pointList.stream()
                .collect(Collectors.groupingBy(o -> o.getRegionCode() + "_" + o.getIndustryName() + "_" + o.getPollutantCode(),
                        // 用LinkedHashMap，保证分组顺序和查询顺序一样
                        LinkedHashMap::new, Collectors.toList()));

        List<EmissionsStatVo.RegionRow> rows = collect.entrySet().stream().map(entry -> {
            EmissionsStatVo.PointRow pointRow = entry.getValue().get(0);

            EmissionsStatVo.RegionRow row = new EmissionsStatVo.RegionRow();
            row.setRegionName(pointRow.getRegionName());
            row.setIndustryName(pointRow.getIndustryName());
            row.setPollutantName(pointRow.getPollutantName());
            row.setRegionCode(pointRow.getRegionCode());
            row.setPollutantCode(pointRow.getPollutantCode());

            BigDecimal manageEmissions = entry.getValue().stream().filter(o -> o.getManageEmissions() != null)
                    .map(EmissionsStatVo.BaseRow::getManageEmissions)
                    .reduce(BigDecimal::add).orElse(null);
            if (manageEmissions != null) {
                row.setManageEmissions(manageEmissions.setScale(0, BigDecimal.ROUND_HALF_UP));
            }

            BigDecimal realEmissions = entry.getValue().stream().filter(o -> o.getRealEmissions() != null)
                    .map(EmissionsStatVo.BaseRow::getRealEmissions)
                    .reduce(BigDecimal::add).orElse(null);
            if (realEmissions != null) {
                row.setRealEmissions(realEmissions.setScale(0, BigDecimal.ROUND_HALF_UP));
            }

            BigDecimal nonKeyRealEmissions = entry.getValue().stream()
                    // 找到非重点的并且实际排放量不为null的
                    .filter(o -> o.getRealEmissions() != null && "0".equals(o.getControlPoints()))
                    .map(EmissionsStatVo.BaseRow::getRealEmissions)
                    .reduce(BigDecimal::add).orElse(null);
            if (nonKeyRealEmissions != null) {
                row.setNonKeyRealEmissions(nonKeyRealEmissions.setScale(0, BigDecimal.ROUND_HALF_UP));
            }

            /*
                基准排放量=管控浓度排放量+非重点管控点位实际排放量
             */
            if (row.getManageEmissions() != null || row.getNonKeyRealEmissions() != null) {
                BigDecimal basisEmissions = new BigDecimal(0);
                if (row.getManageEmissions() != null) {
                    basisEmissions = basisEmissions.add(row.getManageEmissions());
                }
                if (row.getNonKeyRealEmissions() != null) {
                    basisEmissions = basisEmissions.add(row.getNonKeyRealEmissions());
                }
                row.setBasisEmissions(basisEmissions);
            }

            /*
                基准排放量与实际排放量差值
             */
            if (row.getBasisEmissions() != null && row.getRealEmissions() != null) {
                row.setBasisRealDiff(row.getBasisEmissions().subtract(row.getRealEmissions()));
            }

            /*
                设置管控目标排放量，需求：
                1、若选择了行业，直接取基准排放量；
                2、若未选择行业，从区域管控目标那里取值，要是空的话默认基准排放量
             */
            if (StringUtils.isEmpty(search.getIndustryCode())) {
                // 区域污染物的管控浓度配置
                BaseRegionalControlObjectives regionPollutantConfig = regionalControlObjectives.stream()
                        .filter(o -> o.getCountryCode().equals(pointRow.getRegionCode()) && o.getPollutantType().equals(pointRow.getPollutantCode()))
                        .findFirst().orElse(null);
                if (regionPollutantConfig != null && regionPollutantConfig.getControlEmissions() != null) {
                    // 获取监测点小时数据数量最多的
                    OptionalLong optionalLong = entry.getValue().stream().filter(o -> o.getHourDataCount() != null)
                            .mapToLong(EmissionsStatVo.PointRow::getHourDataCount).max();
                    if (optionalLong.isPresent()) {
                        row.setCtrlTargetEmissions(regionPollutantConfig.getControlEmissions().multiply(BigDecimal.valueOf(optionalLong.getAsLong())));
                    }
                } else {
                    row.setCtrlTargetEmissions(row.getBasisEmissions());
                }
            } else {
                row.setCtrlTargetEmissions(row.getBasisEmissions());
            }

            // 管控目标排放量取整
            if (row.getCtrlTargetEmissions() != null) {
                row.setCtrlTargetEmissions(row.getCtrlTargetEmissions().setScale(0, BigDecimal.ROUND_HALF_UP));
            }


            /*
                管控目标与实际排放量差值 = 管控目标排放量-实际排放量
             */
            if (row.getCtrlTargetEmissions() != null && row.getRealEmissions() != null) {
                row.setCtrlTargetRealDiff(row.getCtrlTargetEmissions().subtract(row.getRealEmissions()));
            }

            /*
                管控目标与实际排放量差值小于0时，是超标
             */
            if (row.getCtrlTargetRealDiff() != null) {
                row.setOver(row.getCtrlTargetRealDiff().compareTo(BigDecimal.valueOf(0)) < 0 ? "是" : "否");
            }

            return row;
        }).collect(Collectors.toList());

        // 根据是否超标过滤
        if (StringUtils.isNotEmpty(search.getOver())) {
            String over = search.getOver().equals("1") ? "是" : "否";
            rows = rows.stream().filter(o -> over.equals(o.getOver())).collect(Collectors.toList());
        }

        // 表格数据
        responseData.setRows(rows);

        // ========================================

        /*
            准备图表数据
         */
        // 按照区域分组，计算每个区域排放量相关字段
        Map<String, List<EmissionsStatVo.PointRow>> regionMap = pointList.stream()
                .collect(Collectors.groupingBy(o -> o.getRegionCode(),
                        // 用LinkedHashMap，保证分组顺序和查询顺序一样
                        LinkedHashMap::new, Collectors.toList()));

        List<EmissionsStatVo.RegionRow> regionRows = regionMap.entrySet().stream().map(entry -> {
            EmissionsStatVo.PointRow pointRow = entry.getValue().get(0);

            EmissionsStatVo.RegionRow row = new EmissionsStatVo.RegionRow();
            row.setRegionName(pointRow.getRegionName());
            row.setRegionCode(pointRow.getRegionCode());

            BigDecimal manageEmissions = entry.getValue().stream().filter(o -> o.getManageEmissions() != null)
                    .map(EmissionsStatVo.BaseRow::getManageEmissions)
                    .reduce(BigDecimal::add).orElse(null);
            if (manageEmissions != null) {
                row.setManageEmissions(manageEmissions.setScale(0, BigDecimal.ROUND_HALF_UP));
            }

            BigDecimal realEmissions = entry.getValue().stream().filter(o -> o.getRealEmissions() != null)
                    .map(EmissionsStatVo.BaseRow::getRealEmissions)
                    .reduce(BigDecimal::add).orElse(null);
            if (realEmissions != null) {
                row.setRealEmissions(realEmissions.setScale(0, BigDecimal.ROUND_HALF_UP));
            }

            BigDecimal nonKeyRealEmissions = entry.getValue().stream()
                    // 找到非重点的并且实际排放量不为null的
                    .filter(o -> o.getRealEmissions() != null && "0".equals(o.getControlPoints()))
                    .map(EmissionsStatVo.BaseRow::getRealEmissions)
                    .reduce(BigDecimal::add).orElse(null);
            if (nonKeyRealEmissions != null) {
                row.setNonKeyRealEmissions(nonKeyRealEmissions.setScale(0, BigDecimal.ROUND_HALF_UP));
            }

            /*
                基准排放量=管控浓度排放量+非重点管控点位实际排放量
             */
            if (row.getManageEmissions() != null || row.getNonKeyRealEmissions() != null) {
                BigDecimal basisEmissions = new BigDecimal(0);
                if (row.getManageEmissions() != null) {
                    basisEmissions = basisEmissions.add(row.getManageEmissions());
                }
                if (row.getNonKeyRealEmissions() != null) {
                    basisEmissions = basisEmissions.add(row.getNonKeyRealEmissions());
                }
                row.setBasisEmissions(basisEmissions);
            }

            /*
                基准排放量与实际排放量差值
             */
            if (row.getBasisEmissions() != null && row.getRealEmissions() != null) {
                row.setBasisRealDiff(row.getBasisEmissions().subtract(row.getRealEmissions()));
            }

            /*
                设置管控目标排放量，需求：从区域管控目标那里取值，要是空的话默认基准排放量
             */
            // 区域污染物的管控浓度配置
            BaseRegionalControlObjectives regionPollutantConfig = regionalControlObjectives.stream()
                    .filter(o -> o.getCountryCode().equals(pointRow.getRegionCode()) && o.getPollutantType().equals(search.getPollutantCode()))
                    .findFirst().orElse(null);
            if (regionPollutantConfig != null && regionPollutantConfig.getControlEmissions() != null) {
                OptionalLong optionalLong = entry.getValue().stream().filter(o -> o.getHourDataCount() != null)
                        .mapToLong(EmissionsStatVo.PointRow::getHourDataCount).max();
                if (optionalLong.isPresent()) {
                    row.setCtrlTargetEmissions(regionPollutantConfig.getControlEmissions().multiply(BigDecimal.valueOf(optionalLong.getAsLong())));
                }
            } else {
                row.setCtrlTargetEmissions(row.getBasisEmissions());
            }
            // 管控目标排放量取整
            if (row.getCtrlTargetEmissions() != null) {
                row.setCtrlTargetEmissions(row.getCtrlTargetEmissions().setScale(0, BigDecimal.ROUND_HALF_UP));
            }

            /*
                管控目标与实际排放量差值 = 管控目标排放量-实际排放量
             */
            if (row.getCtrlTargetEmissions() != null && row.getRealEmissions() != null) {
                row.setCtrlTargetRealDiff(row.getCtrlTargetEmissions().subtract(row.getRealEmissions()));
            }

            /*
                管控目标与实际排放量差值小于0时，是超标
             */
            if (row.getCtrlTargetRealDiff() != null) {
                row.setOver(row.getCtrlTargetRealDiff().compareTo(BigDecimal.valueOf(0)) < 0 ? "是" : "否");
            }

            return row;
        }).collect(Collectors.toList());

        // 根据是否超标过滤
        if (StringUtils.isNotEmpty(search.getOver())) {
            String over = search.getOver().equals("1") ? "是" : "否";
            regionRows = regionRows.stream().filter(o -> over.equals(o.getOver())).collect(Collectors.toList());
        }

        chartsVO.setXData(regionRows.stream().map(o -> o.getRegionName()).collect(Collectors.toList()));
        manageTargetSer.setValues(regionRows.stream().map(o -> o.getCtrlTargetEmissions() == null ? null : o.getCtrlTargetEmissions().stripTrailingZeros().toPlainString()).collect(Collectors.toList()));
        basisSer.setValues(regionRows.stream().map(o -> o.getBasisEmissions() == null ? null : o.getBasisEmissions().stripTrailingZeros().toPlainString()).collect(Collectors.toList()));
        realSer.setValues(regionRows.stream().map(o -> o.getRealEmissions() == null ? null : o.getRealEmissions().stripTrailingZeros().toPlainString()).collect(Collectors.toList()));
        return responseData;
    }

}
