package cn.cecep.talroad.data.analyse.task.service;

import cn.cecep.talroad.common.core.utils.DateUtils;
import cn.cecep.talroad.domain.*;
import cn.cecep.talroad.enums.LabelFactoryEnum;
import cn.cecep.talroad.enums.LabelValueEnum;
import cn.cecep.talroad.enums.OutletTypesEnum;
import cn.cecep.talroad.mapper.*;
import cn.cecep.talroad.service.impl.SEnvGasMonRealServiceImpl;
import cn.cecep.talroad.util.NumberUtils;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

/**
 * @author hehongyu
 * @description: 废气排放量超标 标签任务
 * @date 2023/6/28 23:58
 * 逻辑描述：1.获取 s_env_gas_mon_hour 在线检测小时数据  根据时间区间： 大于当前时间减去一个月 的数据
 *         2.根据查询到的数据 获取排口ids 获取到当前数据的排口是一般 还是 主要
 *         3.根据 排口类型查询到 不同的偏口许可浓度表 将
 *         SO2 、NOx  、颗粒物（烟尘）、VOC 四个类型的污染物 小时许可浓度取到
 *         4.将当前数据的 对应 污染物种类 与 污染物许可浓度进行对比 判断 超标 or 超多少 or 未超标
 *         5.取出超标最大的 和 企业id 进行关联 打标 先根据标签id 和企业删除 原有关联关系
 *         6.将最新的企业和标签的关联关系 维护到表中
 */
@Slf4j
@Service
public class GasExceededLabelTaskService {
    @Autowired
    private BMainEmissionPollutionAirMapper mapper;

    @Autowired
    private PcLabelFactoryMapper labelFactoryMapper;

    @Autowired
    private PcLabelMapper labelMapper;

    @Autowired
    private SEnvGasMonHourMapper gasMonHourMapper;

    @Autowired
    private PcOutletMapper outletMapper;

    @Autowired
    private BMainPollDisGasMoutMapper mainPollDisGasMoutMapper;

    @Autowired
    private BMainPollDisGasOoutMapper mainPollDisGasOoutMapper;
    /**
     * 构建 企业id +排口code + 污染物种类的唯一key
     */
    public static final String KEY_FORMAT = "%s_%s_%s";

    public void invoke() {
        //定时任务 一月一次 直接减去一个月查询 > 数据即可
        Calendar instance = Calendar.getInstance();
        instance.set(Calendar.MONTH,instance.get(Calendar.MONTH) - 1);
        PageHelper.startPage(1,1000);
        List<SEnvGasMonHour> dataList = gasMonHourMapper.selectMaxProblemTypeByDateGroupFactoryIdAndOutletId(DateUtil.format(instance.getTime(), DateUtils.YYYY_MM_DD_HH_MM_SS));
        PageInfo<SEnvGasMonHour> dataListPage = new PageInfo<>(dataList);
        //查询标签id
        PcLabel label = labelMapper.selectOne(
                new QueryWrapper<PcLabel>().lambda().eq(PcLabel::getLabelCode, LabelFactoryEnum.PF018.getEnumId())
        );
        logicInvoke(dataListPage.getList(),label);
        int pages = dataListPage.getPages();
        if (pages <= 1){
            //如果只有一页 阻断
            return;
        }
        //因为已经查询过一页了 所以从2开始
        for (int i = 2; i <= pages; i++) {
            PageHelper.startPage(i,1000);
            List<SEnvGasMonHour> pageDataList = gasMonHourMapper.selectMaxProblemTypeByDateGroupFactoryIdAndOutletId(DateUtil.format(instance.getTime(), DateUtils.YYYY_MM_DD_HH_MM_SS));
            PageInfo<SEnvGasMonHour> pageInfo = new PageInfo<>(pageDataList);
            logicInvoke(pageInfo.getList(),label);
        }
    }

    public void logicInvoke(List<SEnvGasMonHour> dataList,PcLabel label){
        if (CollectionUtil.isEmpty(dataList)){
            return;
        }
        //根据排口ids 查询到排口编号
        List<String> outletIds = dataList.stream().map(SEnvGasMonHour::getOutletId).distinct().collect(Collectors.toList());
        List<String> factoryIds = dataList.stream().map(SEnvGasMonHour::getFactoryId).distinct().collect(Collectors.toList());
        LambdaQueryWrapper<PcOutlet> outletWrapper = new QueryWrapper<PcOutlet>().lambda()
                .select(PcOutlet::getId, PcOutlet::getOutletCode, PcOutlet::getOutletTypes)
                .in(PcOutlet::getId, outletIds);
        List<PcOutlet> pcOutlets = outletMapper.selectList(outletWrapper);
        if (CollectionUtil.isEmpty(pcOutlets)){
            //查询不到排口 直接退出
            return;
        }
        List<PcLabelFactory> pcLabelFactoryList = outletMout(pcOutlets,factoryIds,dataList,label);
        //查询对应企业 和 标签 是否存在 存在修改 不存在新增
        LambdaQueryWrapper<PcLabelFactory> labelWrapper = Wrappers.<PcLabelFactory>lambdaQuery()
                .in(PcLabelFactory::getFactoryId, pcLabelFactoryList.stream().map(PcLabelFactory::getFactoryId).collect(Collectors.toList()))
                .eq(PcLabelFactory::getLabelId, label.getId());
        //删除原本的对应标签 和 企业的 关联关系
        labelFactoryMapper.delete(labelWrapper);
        //重新添加新的企业 标签 关联关系
        if (CollectionUtil.isNotEmpty(pcLabelFactoryList)){
            labelFactoryMapper.batchSave(pcLabelFactoryList);
        }
    }

    //判断排口是什么类型的排口用来判断污染物超标多少
    public List<PcLabelFactory> outletMout(List<PcOutlet> pcOutlets, List<String> factoryIds, List<SEnvGasMonHour> dataList, PcLabel label){
        //需要过滤掉 没有类型的排口
        pcOutlets = pcOutlets.stream().filter(outlet -> StringUtils.isNotBlank(outlet.getOutletTypes())).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(pcOutlets)){
            return new ArrayList<>();
        }
        //一般排口
        Map<String, PcOutlet> ordinaryOutletsMap = pcOutlets.stream()
                .filter(outlet -> OutletTypesEnum.GENERAL_OUTLETS.getCode().equals(outlet.getOutletTypes()))
                .collect(Collectors.toMap(PcOutlet::getId, Function.identity()));
        //主要排口
        Map<String, PcOutlet> mainOutletsMap = pcOutlets.stream()
                .filter(outlet -> OutletTypesEnum.MAIN_OUTLETS.getCode().equals(outlet.getOutletTypes()))
                .collect(Collectors.toMap(PcOutlet::getId, Function.identity()));
        //主要排口的污染物许可
        List<BMainPollDisGasMout> pollDisGasMouts = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(mainOutletsMap)){
            LambdaQueryWrapper<BMainPollDisGasMout> moutWrapper = new QueryWrapper<BMainPollDisGasMout>().lambda()
                    .in(BMainPollDisGasMout::getFactoryId, factoryIds)
                    .in(BMainPollDisGasMout::getOutletCode, mainOutletsMap.values().stream().map(PcOutlet::getOutletCode).collect(Collectors.toList()));
            pollDisGasMouts = mainPollDisGasMoutMapper.selectList(moutWrapper);
        }
        //一般排口的污染物许可
        List<BMainPollDisGasOout> pollDisGasOouts = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(ordinaryOutletsMap)){
            LambdaQueryWrapper<BMainPollDisGasOout> ooutWrapper = new QueryWrapper<BMainPollDisGasOout>().lambda()
                    .in(BMainPollDisGasOout::getFactoryId, factoryIds)
                    .in(BMainPollDisGasOout::getOutletCode, ordinaryOutletsMap.values().stream().map(PcOutlet::getOutletCode).collect(Collectors.toList()));
            pollDisGasOouts = mainPollDisGasOoutMapper.selectList(ooutWrapper);
        }
        List<PcLabelFactory> pcLabelFactoryList = new ArrayList<>();
        //企业id + 排口code + 污染物种类code ： 小时许可浓度   主要排口
        Map<String, BigDecimal> mainPermissionValueMap = new HashMap<>();
        for (BMainPollDisGasMout pollDisGasMout : pollDisGasMouts) {
            //需要 企业id  + 排口code + 污染物 组成唯一key ： 许可排放小时浓度限值
            String uqeKey = createUqeKey(pollDisGasMout.getFactoryId(), pollDisGasMout.getOutletCode(), pollDisGasMout.getPollutantCode());
            mainPermissionValueMap.put(uqeKey, NumberUtils.getBigDecimalDefaultZero(pollDisGasMout.getPollutantsMgHour()));
        }
        //企业id + 排口code + 污染物种类code ： 小时许可浓度   一般排口
        Map<String, BigDecimal> ordinaryPermissionValueMap = new HashMap<>();
        for (BMainPollDisGasOout pollDisGasMout : pollDisGasOouts) {
            //需要 企业id  + 排口code + 污染物 组成唯一key ： 许可排放小时浓度限值
            String uqeKey = createUqeKey(pollDisGasMout.getFactoryId(), pollDisGasMout.getOutletCode(), pollDisGasMout.getPollutantCode());
            ordinaryPermissionValueMap.put(uqeKey, NumberUtils.getBigDecimalDefaultZero(pollDisGasMout.getPollutantsMgHour()));
        }
        for (SEnvGasMonHour sEnvGasMonHour : dataList) {
            PcLabelFactory pcLabelFactory = null;
            PcOutlet pcOutlet = mainOutletsMap.get(sEnvGasMonHour.getOutletId());
            String outletCode = pcOutlet.getOutletCode();
            if (ordinaryOutletsMap.containsKey(sEnvGasMonHour.getOutletId())){
                //一般排口
                pcLabelFactory = outletComputeExceeded(outletCode,ordinaryPermissionValueMap,sEnvGasMonHour,label);
            }else{
                //主要排口
                pcLabelFactory = outletComputeExceeded(outletCode,mainPermissionValueMap,sEnvGasMonHour,label);
            }
            pcLabelFactoryList.add(pcLabelFactory);
        }
        //企业可能分析多来 多条 此处进行分组 只取没个企业最大的标签进行维护
        if (CollectionUtil.isEmpty(pcLabelFactoryList)){
            return new ArrayList<>();
        }
        Map<String, List<PcLabelFactory>> collect = pcLabelFactoryList.stream().collect(Collectors.groupingBy(PcLabelFactory::getFactoryId));
        List<PcLabelFactory> resultList = new ArrayList<>();

        for (Map.Entry<String, List<PcLabelFactory>> labelFactoryEntry : collect.entrySet()) {
            List<PcLabelFactory> value = labelFactoryEntry.getValue();
            List<PcLabelFactory> sortList = value.stream().sorted(Comparator.comparing(PcLabelFactory::getLabelValue)).collect(Collectors.toList());
            //经过按照标签排序后 升序 只需要取最后一条即可
            resultList.add(sortList.get(sortList.size()-1));
        }
        return resultList;
    }

    //排口计算污染物超标
    public PcLabelFactory outletComputeExceeded(String outletCode,Map<String, BigDecimal> permissionValueMap,SEnvGasMonHour sEnvGasMonHour,PcLabel label){
        //废气 污染物 空气：SO2 、NOx  、颗粒物（烟尘）、VOC
        String factoryId = sEnvGasMonHour.getFactoryId();
        //四个污染物浓度的枚举列表
        ArrayList<LabelValueEnum> labelValueEnums = new ArrayList<>();
        //实际浓度
        BigDecimal so2 = NumberUtils.getBigDecimalDefaultNull(sEnvGasMonHour.getSo2());
        //许可每小时浓度
        BigDecimal so2PValue = permissionValueMap.get(createUqeKey(factoryId, outletCode, SEnvGasMonRealServiceImpl.PollutantEnum.so2.getCode()));
        //计算实际 与 许可 超标枚举
        labelValueEnums.add(judgementCb(so2,so2PValue));
        BigDecimal nox = NumberUtils.getBigDecimalDefaultNull(sEnvGasMonHour.getNox());
        BigDecimal noxPValue = permissionValueMap.get(createUqeKey(factoryId, outletCode, SEnvGasMonRealServiceImpl.PollutantEnum.nox.getCode()));
        labelValueEnums.add(judgementCb(nox,noxPValue));
        BigDecimal pm = NumberUtils.getBigDecimalDefaultNull(sEnvGasMonHour.getPm());
        BigDecimal pmPValue = permissionValueMap.get(createUqeKey(factoryId, outletCode, SEnvGasMonRealServiceImpl.PollutantEnum.pm.getCode()));
        labelValueEnums.add(judgementCb(pm,pmPValue));
        BigDecimal vocs = NumberUtils.getBigDecimalDefaultNull(sEnvGasMonHour.getVocs());
        BigDecimal vocsPValue = permissionValueMap.get(createUqeKey(factoryId, outletCode, SEnvGasMonRealServiceImpl.PollutantEnum.vocs.getCode()));
        labelValueEnums.add(judgementCb(vocs,vocsPValue));
        return buildPcLabelFactory(factoryId,label.getId(),labelValueEnums);
    }

    /**
     * 构建企业标签关联关系实体
     */
    private PcLabelFactory buildPcLabelFactory(String factoryId, String labelId, ArrayList<LabelValueEnum> labelValue){
        PcLabelFactory pcLabelFactory = new PcLabelFactory();
        pcLabelFactory.setId(UUID.randomUUID().toString());
        pcLabelFactory.setFactoryId(factoryId);
        pcLabelFactory.setLabelId(labelId);
        LabelValueEnum labelValueEnum = labelValue.stream()
                .sorted(
                        Comparator.comparing(valueEnum -> Integer.parseInt(valueEnum.getArgumentsCode()))
                )
                .collect(Collectors.toList()).get(labelValue.size() - 1);
        pcLabelFactory.setLabelValue(labelValueEnum.getArgumentsCode());
        pcLabelFactory.setLableUseing("0");
        return pcLabelFactory;
    }

    public LabelValueEnum judgementCb(BigDecimal xk, BigDecimal sj){
        if (sj == null || xk == null){
            //没有实际浓度 则 直接 返回未超标
            return LabelValueEnum.CODE_1044;
        }
        return judgingTheIndexOfExcess(xk, sj);
    }

    /**
     * 判断超标倍数
     */
    public static LabelValueEnum judgingTheIndexOfExcess(BigDecimal xk, BigDecimal sj){
        //超标值 - 实际值 > 0 代表无超标
        if (xk.subtract(sj).compareTo(BigDecimal.ZERO) > 0){
            return LabelValueEnum.CODE_1044;
        }
        //实际值 - 超标值 > 超标值 超标一倍以内
        if (sj.subtract(xk).compareTo(xk) <= 0){
            return LabelValueEnum.CODE_1045;
        }
        //实际值 - 超标值 < 超标值 * 2 超标两倍以内
        if (sj.subtract(xk).compareTo(xk.multiply(new BigDecimal(2))) < 0){
            return LabelValueEnum.CODE_1046;
        }
        //否则就是超标两倍以上
        return LabelValueEnum.CODE_1047;
    }

    /**
     * 构建 企业id +排口code + 污染物种类的唯一key
     */
    public String createUqeKey(String factoryId,String outletCode,String pollutantsType){
        return String.format(KEY_FORMAT,factoryId,outletCode,pollutantsType);
    }
}
