package cn.cecep.talroad.data.analyse.task.anomaly.analysis;

import cn.cecep.talroad.domain.*;
import cn.cecep.talroad.domain.analysis.BasDeviceParamAnalyse;
import cn.cecep.talroad.enums.DataTypeEnum;
import cn.cecep.talroad.enums.DeviceStatusEnum;
import cn.cecep.talroad.enums.EnIndustry;
import cn.cecep.talroad.mapper.*;
import cn.cecep.talroad.mapper.analysis.BasDeviceParamAnalyseMapper;
import cn.cecep.talroad.service.IAmProblemFiService;
import cn.cecep.talroad.vo.BasDeviceParamListVo;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 数据异常处理基类
 *
 * @param <T> 实时数据实体
 */
@Slf4j
public abstract class DataAnomalyAbstractService<T> {

    @Autowired
    private BasParamMapper basParamMapper;

    @Autowired
    private BasDeviceParamMapper basDeviceParamMapper;

    @Autowired
    private BasDeviceParamAnalyseMapper basDeviceParamAnalyseMapper;

    @Autowired
    private BasDeviceSalAnalysisMapper basDeviceSalAnalysisMapper;

    @Autowired
    private IAmProblemFiService amProblemFiService;

    @Autowired
    private BasDeviceMapper basDeviceMapper;

    public boolean mainProcess() {
        try {
            // 获取所有可用设备id
            List<String> deviceIds = queryUsableDeviceIds();
            if (CollectionUtil.isEmpty(deviceIds)) {
                //当前工业 不存在 在线设备
                return true;
            }
            Integer pageSize = devicePageSize();
            List<List<String>> idsPage = Lists.partition(deviceIds, pageSize);
            for (List<String> currentDeviceIds : idsPage) {
                // 获取每个设备绑定的 在线参数
                Map<String, List<BasDeviceParamListVo>> currentDeviceParams = queryDeviceOnline(currentDeviceIds);
                if (currentDeviceParams.size() == 0){
                    continue;
                }
                Map<String, List<T>> groupDeviceReal = groupDeviceRealList(queryReal(currentDeviceIds,getQueryDataStartDateTime(),getQueryDataEndDateTime()),currentDeviceIds);
                if (CollectionUtil.isEmpty(groupDeviceReal)){
                    continue;
                }

                List<AmProblemFi> amProblemFis = verifyAnomaly(groupDeviceReal, currentDeviceParams);
                if (CollectionUtil.isEmpty(amProblemFis)){
                    continue;
                }
                if(amProblemFis!=null && amProblemFis.size()>0) {
                    amProblemFiService.saveBatch(amProblemFis);
                }
            }
            return true;
        }catch (Exception e){
            log.error("任务执行失败!",e);
            return false;
        }
    }


    /**
     * 获取该设备绑定参数
     */
    protected Map<String, List<BasDeviceParamListVo>> queryDeviceOnline(List<String> deviceIds) {
        List<String> deviceTargetIds = deviceIds.stream().map(this::getDeviceId).collect(Collectors.toList());
        //此处看情况 是否要用批处理
        List<BasDeviceParamListVo> basDeviceParamListVos = basDeviceParamMapper.queryByDeviceIdListAndIndustry(deviceTargetIds, null, null);
        if (CollectionUtil.isEmpty(basDeviceParamListVos)){
            return new HashMap<>();
        }

        return basDeviceParamListVos.stream().collect(Collectors.groupingBy(BasDeviceParamListVo::getDeviceId));
    }

    /**
     * 获取单个实体类的 参数内容
     */
    protected Map<String, Object> getParamValue(T entity, List<String> params) {
        Map<String, Object> result = new HashMap<>();
        for (String param : params) {
            Class<?> entityClass = entity.getClass();
            try {
                Field field = ReflectionUtils.findField(entityClass, param);
                if (field == null) {
                    continue;
                }
                field.setAccessible(true);
                Object value = ReflectionUtils.getField(field, entity);
                result.put(param, value);
            } catch (Exception e) {
                log.warn("获取字段value失败！字段名称：{} , 类：{}", param, entity.getClass().getName());
            }
        }
        return result;
    }

    /**
     * 获取每个实体类
     */
    public static<T> Map<String, List<Object>> getColumnParamValue(List<T> realDataList, List<String> params) {
        Map<String, List<Object>> paramValues = new HashMap<>();
        if (CollectionUtil.isEmpty(realDataList)) {
            return paramValues;
        }
        Class<?> realClass = realDataList.get(0).getClass();
        for (String param : params) {
            List<Object> classParamValue = getClassParamValue(realDataList, param, realClass);
            paramValues.put(param, classParamValue);
        }
        return paramValues;
    }

    /**
     * 获取单个列属性
     */
    protected static<T> List<Object> getClassParamValue(List<T> reals, String param, Class<?> realClass) {
        List<Object> paramValues = new ArrayList<>();
        try {
            Field declaredField = realClass.getDeclaredField(param);
            declaredField.setAccessible(true);
            for (T real : reals) {
                try {
                    Object o = declaredField.get(real);
                    paramValues.add(o);
                } catch (Exception e) {
                    log.warn("获取字段value失败！字段名称：{} , 类：{}", param, realClass.getName());
                    //即使是异常状态也应该有一个值来站位 因为reals数据有多少条 就应该根据该参数取到多少值
                    //参数获取到的值 必须和 传入数据list 长度一致 便于后面利用异常参数下标 获取到 对应下标的数据
                    //如果 参数值异常 可根据此下标获取逻辑 获取到数据异常上报时间 和 截止时间
                    paramValues.add(null);
                }
            }
        } catch (Exception e) {
            log.warn("获取字段value失败！字段名称：{} , 类：{}", param, realClass.getName());
        }
        return paramValues;
    }

    /**
     * 过滤停产设备id
     */
    protected List<String> filterOpenDeviceIds(List<String> deviceIds) {
        if (CollectionUtil.isEmpty(deviceIds)) {
            return new ArrayList<>();
        }
        List<String> deviceTargetIds = deviceIds.stream().map(this::getDeviceId).collect(Collectors.toList());
        List<List<String>> idsPage = Lists.partition(deviceTargetIds, 200);
        List<BasDeviceSalAnalysis> basDeviceSalAnalyses = new ArrayList<>();
        idsPage.forEach(i->{
            List<BasDeviceSalAnalysis> list = basDeviceSalAnalysisMapper.selectList(Wrappers.<BasDeviceSalAnalysis>lambdaQuery().in(BasDeviceSalAnalysis::getDeviceId, i).and(wrapper -> {
                wrapper.eq(BasDeviceSalAnalysis::getMonStatus,"生产").or()
                    .isNull(BasDeviceSalAnalysis::getMonStatus);
            }));
            basDeviceSalAnalyses.addAll(list);
        });

        if (CollectionUtil.isEmpty(basDeviceSalAnalyses)){
            return new ArrayList<>();
        }
        //过滤出所有 ！= 停产的 设备
        return getOpenDeviceIds(deviceIds,basDeviceSalAnalyses);
    }

    protected List<String> getOpenDeviceIds(List<String> deviceIds,List<BasDeviceSalAnalysis> basDeviceSalAnalyses){
        return basDeviceSalAnalyses
                .stream()
                .map(BasDeviceSalAnalysis::getDeviceId)
                .collect(Collectors.toList());
    }

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

    /**
     * 获取分页设备条数  todo 根据行业重写
     */
    protected Integer devicePageSize() {
        return 10;
    }


    /**
     * 校验获取添加的异常信息
     *
     * @param groupDataList 分组设备的实时数据
     * @param params   实时数据需要检测字段
     * @return 返回所有 有异常信息的字段
     */
    protected List<AmProblemFi> verifyAnomaly(Map<String, List<T>> groupDataList, Map<String, List<BasDeviceParamListVo>> params) {
        List<AmProblemFi> amProblemFis = new ArrayList<>();
        // 校验字段数据成功去获取异常信息，失败去获取异常信息
        groupDataList.entrySet()
                .stream()
                .map(entry -> verify(entry.getKey(), entry.getValue(), params.get(entry.getKey())))
                .filter(CollectionUtil::isNotEmpty)
                .forEach(amProblemFis::addAll);
        return amProblemFis;
    }

    /**
     * 获取所有设备ids
     */
    protected List<String> queryDeviceIds(EnIndustry enIndustry,String dataType){
        if (enIndustry == null){
            throw new RuntimeException("queryDeviceIds is null!");
        }
//        LambdaQueryWrapper<BasDevice> wrapper = Wrappers.<BasDevice>lambdaQuery()
//                .select(BasDevice::getDeviceId)
//                .eq(BasDevice::getIndustry, enIndustry.id())
//                .eq(BasDevice::getIsStatus,"1");
//        List<BasDevice> basDevices = basDeviceMapper.selectList(wrapper);
//        return basDevices.stream().map(BasDevice::getDeviceId).collect(Collectors.toList());
        List<BasDevice> basDevices = basDeviceMapper.selectListByIndustry(enIndustry.id(),dataType);
        return basDevices.stream().map(BasDevice::getDeviceId).collect(Collectors.toList());
    }

    /**
     * 根据设备ids 查询设备名称 用来填充告警信息
     */
    protected Map<String,BasDevice> queryDeviceByIds(List<String> deviceIds){
        if (CollectionUtil.isEmpty(deviceIds)){
            return new HashMap<>();
        }
        deviceIds = deviceIds.stream().distinct().collect(Collectors.toList());;
        List<BasDevice> basDevices = basDeviceMapper.selectList(Wrappers.<BasDevice>lambdaQuery().in(BasDevice::getDeviceId, deviceIds));
        if (CollectionUtil.isEmpty(basDevices)){
            return new HashMap<>();
        }
        return basDevices.stream().collect(Collectors.toMap(BasDevice::getDeviceId, Function.identity(), (x,y) -> x));
    }

    /**
     * 根据参数ids 查询参数名称 用来填充告警信息
     */
    protected Map<String,String> queryParamsNameByIds(List<String> paramIds){
        if (CollectionUtil.isEmpty(paramIds)){
            return new HashMap<>();
        }
        paramIds = paramIds.stream().distinct().collect(Collectors.toList());;
        List<BasParam> basParams = basParamMapper.selectList(Wrappers.<BasParam>lambdaQuery().in(BasParam::getParamId, paramIds));
        if (CollectionUtil.isEmpty(basParams)){
            return new HashMap<>();
        }
        return basParams.stream().collect(Collectors.toMap(BasParam::getParamId, BasParam::getName, (x,y) -> x));
    }

    /**
     * 根据参数ids 查询参数分析设置
     */
    protected Map<String, BasDeviceParamAnalyse> queryParamsAnalyseByIds(List<String> paramIds){
        if (CollectionUtil.isEmpty(paramIds)){
            return new HashMap<>();
        }
        paramIds = paramIds.stream().distinct().collect(Collectors.toList());;
        List<BasDeviceParamAnalyse> basDeviceParamAnalyses =
                basDeviceParamAnalyseMapper.selectList(Wrappers.<BasDeviceParamAnalyse>lambdaQuery().in(BasDeviceParamAnalyse::getDeviceParamCode, paramIds));
        if (CollectionUtil.isEmpty(basDeviceParamAnalyses)){
            return new HashMap<>();
        }
        return basDeviceParamAnalyses.stream()
                .filter(this::filterDeviceParamTaskSwitch)
                .collect(Collectors.toMap(BasDeviceParamAnalyse::getDeviceParamCode, Function.identity(), (x,y) -> x));
    }

    /**
     * 各任务 实现 参数过滤逻辑
     */
    protected abstract boolean filterDeviceParamTaskSwitch(BasDeviceParamAnalyse analyse);
    /**
     * 获取所有设备ids
     * 需要自行实现
     */
    protected abstract List<String> queryDeviceIds();

    /**
     * 获取实时数据
     *
     * @return
     */
    protected abstract List<T> queryReal(List<String> deviceIds,Date startTime,Date endTime);

    /**
     * 实时数据分组
     *
     * @param dataReal 实时数据
     * @return String = 设备唯一标识  , List<T> 设备实时数据
     */
    protected abstract Map<String, List<T>> groupDeviceRealList(List<T> dataReal,List<String> deviceIds);

    /**
     * 设备数据预警校验
     *
     * @param deviceId   设备id
     * @param deviceRealData 设备id 对应的实时数据
     * @param deviceBindOnlineParams 校验参数信息
     * @return 返回异常信息 修改 或 新增 ， 如果没有要修改的返回 null
     */
    protected abstract List<AmProblemFi> verify(String deviceId, List<T> deviceRealData, List<BasDeviceParamListVo> deviceBindOnlineParams);

    /**
     * 获取查询实时表开始时间
     */
    protected abstract Date getQueryDataStartDateTime();

    /**
     * 获取查询实时表结束时间
     */
    protected abstract Date getQueryDataEndDateTime();

    protected String getDeviceId(String deviceId){
        return deviceId;
    }
}
