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

import cn.cecep.talroad.domain.*;
import cn.cecep.talroad.enums.DeviceStatusEnum;
import cn.cecep.talroad.enums.EnIndustry;
import cn.cecep.talroad.mapper.*;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

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

/**
 * 石灰石基类
 */
@Slf4j
public abstract class BaseAnomalyLimestoneService<T> {

    @Autowired
    private BasDeviceMapper basDeviceMapper;

    @Autowired
    private MonDevRealMapper monDevRealMapper;

    @Autowired
    private BasDeviceSalAnalysisMapper basDeviceSalAnalysisMapper;

    public static final int pageSize = 10;

    @Autowired
    private BasThresholdConfigMapper basThresholdConfigMapper;

    @Autowired
    private SEnvGasMonRealMapper envGasMonRealMapper;

    @Autowired
    private AmProblemFiMapper amProblemFiMapper;

    @Autowired
    private BMainProductionOrganizGasMapper bMainProductionOrganizGasMapper;

    @Autowired
    private PcOutletMapper pcOutletMapper;


    /**
     * 主方法执行
     */
    public boolean mainProcess(){

        try {
            // 获取所有可用设备id
            List<BasDevice> basDevices = queryUsableDeviceIds();

            if (!CollectionUtils.isEmpty(basDevices)){

                // 获取计算得开始时间  跟  结束时间
                Date startTime = getBeforeDayStart();
                Date endTime = getBeforeDayEnd();

                // 设备分页 默认十个一分页
                List<List<BasDevice>> baseDevicePage = Lists.partition(basDevices, BaseAnomalyLimestoneService.pageSize);

                for (List<BasDevice> basDeviceList : baseDevicePage) {

                    List<String> deviceIds = basDeviceList.stream().map(BasDevice::getDeviceId).collect(Collectors.toList());

                    // 获取石灰消耗量
                    Map<String, BigDecimal> deviceGas = getDeviceGas(deviceIds, startTime, endTime);

                    // 获取 异常信息
                    List<AmProblemFi> amProblemFis = getAmProblemFis(basDeviceList, deviceGas,startTime,endTime);

                    if (!CollectionUtils.isEmpty(amProblemFis)){
                        // 添加异常数据
                        amProblemFiMapper.insertAmProblemFis(amProblemFis);
                    }

                }

            }
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取石灰石纯度配置
     */
    public BigDecimal getLimestoneConfig(){
        BasThresholdConfig config = basThresholdConfigMapper.selectByParamCode("T01");
        if (config==null){
            return new BigDecimal(85.00);
        }
        return config.getThresholdValue().divide(new BigDecimal(100),2, RoundingMode.HALF_UP);
    }

    /**
     * 获取 消耗量 配置
     */
    public BigDecimal getConsumeConfig(){
        BasThresholdConfig config = basThresholdConfigMapper.selectByParamCode("T02");
        if (config==null){
            return new BigDecimal(50.00);
        }
        return config.getThresholdValue().divide(new BigDecimal(100),2, RoundingMode.HALF_UP);
    }

    /**
     * 判断是否 小于 比例值
     */
    public boolean calculate(BigDecimal desulfuration , BigDecimal limestone , BigDecimal proportion){
        int equals = desulfuration.compareTo(limestone.multiply(proportion));
        if (equals < 0) {
            return false;
        }
        return true;
    }

    /**
     * 计算 石灰石消耗量
     * @return
     */
    public BigDecimal getLimestone(List<BigDecimal> so2Ins,List<BigDecimal> so2Outs,BigDecimal emissionValue,BigDecimal thresholdValue){

        // 计算 出口入口 日均值
        BigDecimal so2In = realAvg(so2Ins);
        BigDecimal so2Out = realAvg(so2Outs);

        //（入口SO2日均浓度-出口SO2日均浓度）/入口SO2日均浓度
        BigDecimal div = so2In.subtract(so2Out).divide(so2In, 6, RoundingMode.HALF_UP);

        //日废气排放量*入口SO2日均浓度/1000000*（（入口SO2日均浓度-出口SO2日均浓度）/入口SO2日均浓度）*100/64/石灰石纯度（一般为85%-90%）*1.03
        BigDecimal limestone = emissionValue
                .multiply(so2In)
                .divide(new BigDecimal("1000000"), 6, RoundingMode.HALF_UP)
                .multiply(div)
                .multiply(new BigDecimal(100))
                .divide(new BigDecimal(64), 6, RoundingMode.HALF_UP)
                .divide(thresholdValue, 6, RoundingMode.HALF_UP)
                .multiply(new BigDecimal("1.03"));
        return limestone;
    }

    /**
     * 获取设备日废气排放量
     */
    public Map<String,BigDecimal> getDeviceGas(List<String> deviceIds,Date startTime,Date endTime){

//        List<GasDeviceVo> gasDeviceVos = envGasMonRealMapper.selectGasByDeviceIds(deviceIds,startTime,endTime);
//        return gasDeviceVos.stream().collect(Collectors.toMap(GasDeviceVo::getDeviceId,GasDeviceVo::getAvgGas));

        HashMap<String, BigDecimal> gasData = new HashMap<>();

        // 查询 设备对应的站点Code
        // 查询 站点id
        // 查询站点对应的数据 并且按照设备计算平均值
        // 每一步返回为空值 或者 null 不继续计算
        for (String deviceId : deviceIds) {
            List<BMainProductionOrganizGas> mainProduct = getMainProduct(deviceId);
            if (CollectionUtils.isNotEmpty(mainProduct)){
                List<PcOutlet> outLet = getOutLet(mainProduct);
                if (CollectionUtils.isNotEmpty(outLet)){
                    BigDecimal gasData1 = getGasData(outLet, startTime, endTime);
                    if (ObjectUtils.isNotEmpty(gasData1)){
                        gasData.put(deviceId,gasData1);
                    }
                }
            }
        }
        return gasData;
    }


    /**
     * 获取设备对应的  站点Code
     */
    public List<BMainProductionOrganizGas> getMainProduct(String deviceId){

        LambdaQueryWrapper<BMainProductionOrganizGas> bMainProductionOrganizGasLambdaQueryWrapper = new QueryWrapper<BMainProductionOrganizGas>().lambda()
                .select(BMainProductionOrganizGas::getFactoryId,BMainProductionOrganizGas::getOutletCode)
                .isNotNull(BMainProductionOrganizGas::getProFacilityId)
                .like(BMainProductionOrganizGas::getProFacilityId, deviceId)
                .eq(BMainProductionOrganizGas::getCepFacilityCode, "TA091");

        return bMainProductionOrganizGasMapper.selectList(bMainProductionOrganizGasLambdaQueryWrapper);
    }

    /**
     *  获取站点id
     * @param bMainProductionOrganizGases
     * @return
     */
    public List<PcOutlet> getOutLet(List<BMainProductionOrganizGas> bMainProductionOrganizGases){
       return bMainProductionOrganizGases.stream().map((bMainProductionOrganizGas)->{
            LambdaQueryWrapper<PcOutlet> pcOutletLambdaQueryWrapper = new QueryWrapper<PcOutlet>().lambda()
                    .select(PcOutlet::getId,PcOutlet::getFactoryId)
                    .eq(PcOutlet::getOutletCode, bMainProductionOrganizGas.getOutletCode())
                    .eq(PcOutlet::getFactoryId, bMainProductionOrganizGas.getFactoryId())
                    .eq(PcOutlet::getPollutantType, "4");
            return pcOutletMapper.selectOne(pcOutletLambdaQueryWrapper);
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 获取 该设备的所有站点的 数据的平均值
     */
    public BigDecimal getGasData(List<PcOutlet> pcOutlets,Date startTime,Date endTime){

        List<BigDecimal> bigDecimals = pcOutlets.stream().map((pcOutlet) ->
                envGasMonRealMapper.selectByOutLetId(pcOutlet.getFactoryId(), pcOutlet.getId(), startTime, endTime)
        ).filter(Objects::isNull).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(bigDecimals)){
            return null;
        }

        return bigDecimals.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(bigDecimals.size()), 2, BigDecimal.ROUND_HALF_UP);
    }

    public BigDecimal realAvg(List<BigDecimal> bigDecimals){
        Double collect = bigDecimals.stream().map(BigDecimal::doubleValue).collect(Collectors.averagingDouble(Double::doubleValue));
        if (collect!=null){
            return new BigDecimal(collect);
        }
        return new BigDecimal(0);
    }

    /**
     * 将每个设备分组
     * @param monDevReals
     * @return
     */
    protected Map<String,List<MonDevReal>> groupMonDevReal(List<MonDevReal> monDevReals){
        return monDevReals.stream().collect(Collectors.groupingBy(MonDevReal::getDeviceId));
    }


    /**
     * 获取所有可用设备
     */
    protected List<BasDevice> queryUsableDeviceIds() {
        List<BasDevice> basDevices = queryDevice();
        if (CollectionUtil.isEmpty(basDevices)){
            return new ArrayList<>();
        }
        return filterOpenDeviceIds(basDevices);
    }

    /**
     * 获取设备
     * @param enIndustry
     * @return
     */
    protected List<BasDevice> queryDevice(EnIndustry enIndustry,String... types){
        if (enIndustry == null){
            throw new RuntimeException("queryDeviceIds is null!");
        }
        LambdaQueryWrapper<BasDevice> wrapper = Wrappers.<BasDevice>lambdaQuery()
                .select(BasDevice::getDeviceId,BasDevice::getName,BasDevice::getFactoryId)
                .eq(BasDevice::getIndustry, enIndustry.id())
                .eq(BasDevice::getIsStatus,"1");
        if (types!=null && types.length>0){
            wrapper.in(BasDevice::getType,types);
        }
        return basDeviceMapper.selectList(wrapper);
    }

    /**
     * 获取设备
     * @return
     */
    protected abstract List<BasDevice> queryDevice();


    /**
     * 过滤停产设备
     */
    protected List<BasDevice> filterOpenDeviceIds(List<BasDevice> devices) {
        if (CollectionUtil.isEmpty(devices)) {
            return new ArrayList<>();
        }
        List<String> deviceIds = devices.stream().map(BasDevice::getDeviceId).collect(Collectors.toList());
        List<BasDeviceSalAnalysis> basDeviceSalAnalyses = basDeviceSalAnalysisMapper.selectList(Wrappers.<BasDeviceSalAnalysis>lambdaQuery().in(BasDeviceSalAnalysis::getDeviceId, deviceIds));
        if (CollectionUtil.isEmpty(basDeviceSalAnalyses)){
            return new ArrayList<>();
        }
        //过滤出所有 ！= 停产的 设备
        List<String> openDeviceIds = getOpenDeviceIds(deviceIds, basDeviceSalAnalyses);

        List<BasDevice> basDevices = devices.stream().filter((device) -> {
            for (String openDeviceId : openDeviceIds) {
                if (openDeviceId.equals(device.getDeviceId())) {
                    return true;
                }
            }
            return false;
        }).collect(Collectors.toList());

        return basDevices;
    }
    protected List<String> getOpenDeviceIds(List<String> deviceIds,List<BasDeviceSalAnalysis> basDeviceSalAnalyses){
        return basDeviceSalAnalyses
                .stream()
                .filter(deviceSalAnalysis -> !DeviceStatusEnum.STOP.getValue().equals(deviceSalAnalysis.getMonStatus()))
                .map(BasDeviceSalAnalysis::getDeviceId)
                .collect(Collectors.toList());
    }

    /**
     * @Description 获取当前时期前 i 天 日期的零点 当前日期 2019-7-8 13:13:13 --> 获取日期 2019-07-07
     * @Author: szj
     * @param:  [i]  输入int值 代表天数 00:00:00  UNIX 时间
     * @Return: int
     */
    public Date getBeforeDayStart() {
        Calendar zero = Calendar.getInstance();
        zero.add(Calendar.DATE, -1);
        zero.set(zero.get(Calendar.YEAR), zero.get(Calendar.MONTH), zero.get(Calendar.DATE), 0, 0, 0);
        return zero.getTime();
    }
    /**
     * @Description 获取当前时期前 i 天 日期的23:59:59 当前日期 2019-7-8 13:13:13 --> 获取日期 2019-7-7 23:59:59 UNIX 时间
     * @Author: szj
     * @param:  [i]  输入int值 代表天数
     * @Return: int
     */
    public Date getBeforeDayEnd() {
        Calendar zero = Calendar.getInstance();
        zero.add(Calendar.DATE, -1);
        zero.set(zero.get(Calendar.YEAR), zero.get(Calendar.MONTH), zero.get(Calendar.DATE), 23, 59, 59);
        return zero.getTime();
    }


    /**
     *  获取 实时数据 并计算废弃 与  脱硫剂比例
     */
    protected List<AmProblemFi> getAmProblemFis(List<BasDevice> basDevices , Map<String,BigDecimal> gasData , Date startTime , Date endTime){

        List<String> deviceIds = basDevices.stream().map(BasDevice::getDeviceId).collect(Collectors.toList());

        Map<String, BasDevice> deviceMap = basDevices.stream().collect(Collectors.toMap(BasDevice::getDeviceId, Function.identity()));

        List<T> realData = queryRealData(deviceIds,startTime,endTime);
        if (CollectionUtils.isEmpty(realData)){
            return new ArrayList<>();
        }
        Map<String, List<T>> groupRealData = groupRealData(realData);

        BigDecimal limestoneConfig = getLimestoneConfig();
        BigDecimal consumeConfig = getConsumeConfig();

        List<AmProblemFi> amProblemFis = groupRealData.entrySet().stream().filter((entry)->{
            BigDecimal gas = gasData.get(entry.getKey());
            return gas!=null;
        }).map(entry -> {
            BigDecimal gas = gasData.get(entry.getKey());
            BasDevice basDevice = deviceMap.get(entry.getKey());
            return getRealAmProblemFi(basDevice, entry.getValue(), gas , limestoneConfig , consumeConfig );
        }).filter(amProblemFi -> amProblemFi != null).collect(Collectors.toList());
        return amProblemFis;
    }

    /**
     * 通过 设备id 查询实时数据
     */
    protected abstract List<T> queryRealData(List<String> deviceIds , Date startTime , Date endTime);


    /**
     * 实时数据分组
     */
    protected abstract Map<String,List<T>> groupRealData(List<T> realData);


    /**
     * 分组的实时数据  计算，并且得出异常信息
     * @param deviceRealData
     * @return
     */
    protected abstract AmProblemFi getRealAmProblemFi(BasDevice basDevice, List<T> deviceRealData, BigDecimal gasData , BigDecimal limestoneConfig , BigDecimal consumeConfig);


    protected AmProblemFi addProblem(String name,BigDecimal value,BigDecimal minValue,BigDecimal maxValue,String factoryId,String problemType) {
        if (value!=null){
            value = value.setScale(2,BigDecimal.ROUND_HALF_UP);
        }
        if (minValue!=null){
            minValue = value.setScale(2,BigDecimal.ROUND_HALF_UP);
        }
        if (maxValue!=null){
            maxValue = value.setScale(2,BigDecimal.ROUND_HALF_UP);
        }
        //出现报警
        AmProblemFi amProblemFi = new AmProblemFi();
        amProblemFi.setFactoryId(factoryId);
        String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
        amProblemFi.setId(uuid);
        // 问题来源 1:关联分析系统, 2:分表计电系统, 3:动态管控系统
        amProblemFi.setProblemSources("1");
        // 新增一条，代表企业生产设施运行分析
        amProblemFi.setProblemType(problemType);
        amProblemFi.setProblemDetails(getMessage(name, value, minValue, maxValue));
        amProblemFi.setType(1);
        return amProblemFi;
    }

    protected String getMessage(String name,BigDecimal value,BigDecimal minValue,BigDecimal maxValue){
        String date = new SimpleDateFormat("yyyy年MM月dd日").format(getBeforeDayStart());
        return new StringBuilder()
                .append(name).append(",")
                .append(date)
                .append("实际使用脱硫剂").append(value).append("kg")
                .append("理论药剂投加量应为").append(minValue).append("-").append(maxValue).append("，")
                .append("实际药剂使用量过低，请注意排查。").toString();
    }
}
