package com.zd.laboratory.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.StrUtil;
import com.zd.common.core.exception.ServiceException;
import com.zd.common.redis.service.RedisService;
import com.zd.common.security.utils.DictUtils;
import com.zd.laboratory.domain.*;
import com.zd.laboratory.domain.vo.LabSubjectVO;
import com.zd.laboratory.mapper.LabHazardSubjectRelationMapper;
import com.zd.laboratory.mapper.LabRiskPlanAbnormalGroupMapper;
import com.zd.laboratory.mapper.LabSubjectMapper;
import com.zd.laboratory.service.ILabSubjectService;
import com.zd.system.api.domain.CommonCount;
import com.zd.system.api.domain.SubQueryConfig;
import com.zd.system.api.laboratory.domain.SubAddrr;
import com.zd.system.api.laboratory.domain.vo.LabSubClassVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 实验室查询管理： 新版 以后在这个基础上重构，尽量不要使用 {@link LabSubjectManagerService} 类中的组装方法
 * 测试提出之前的查询响应比较慢，所以该类实现了实验室其他信息的异步查询。
 * @Author: zhoupan
 * @Date: 2021/11/12/8:38
 * @Description:
 */
@Service
public class SubQueryManager {

    Logger logger = LoggerFactory.getLogger(SubQueryManager.class);

    @Autowired
    LabSubjectMapper subjectMapper;

    @Autowired
    LabSubjectManagerService subjectManagerService;

    @Autowired
    RedisService redisService;

//    @Autowired
//    private LabSubClassVO getNONELabSubClassVO;

    @Autowired
    private LabRiskPlanAbnormalGroupMapper riskPlanAbnormalGroupMapper;

    @Autowired
    private ILabSubjectService labSubjectService;

    @Autowired
    private LabHazardSubjectRelationMapper labHazardSubjectRelationMapper;


    /**
     * 查询实验室：根据配置条件 通过实验室ID查询
     * @param subQueryConfig 配置
     * @param ids 实验室ids
     * @return
     */
    public List<SubDiyVO> querySubs(SubQueryConfig subQueryConfig, Long...ids)
    {
        return querySubs(Arrays.asList(ids), subQueryConfig);
    }
    /**
     * 查询实验室：根据配置条件 通过实验室ID查询
     * 通过ID查询不验证数据权限， 需要提供方保证，不过以后倒是可以改造成自身验证， 但是没必要，
     * 因为关联数据权限可能需要跟随主数据的权限，所以交给主数据就好了
     * @param subQueryConfig 配置
     * @param subIds 实验室ids
     * @return
     */
    public List<SubDiyVO> querySubs(List<Long> subIds, SubQueryConfig subQueryConfig)
    {
        //根据ids 等查询条件获取实验室
        List<LabSubjectVO> labSubjectVOS = null;
        if(subQueryConfig.getBaseInfo()==false)
        {
            //不查基础信息则不能查地址 以后可以考虑分开， 不要依赖基础信息
            subQueryConfig.setAddr(true);
            labSubjectVOS = subIds.stream().distinct().map(a -> {
                LabSubjectVO subjectVO = new LabSubjectVO();
                subjectVO.setId(a);
                return subjectVO;
            }).collect(Collectors.toList());
        }
//        else
//        {
            //根据ids 等查询条件获取实验室
            labSubjectVOS = subjectMapper.selectLabSubjectVOListByIds(subIds, subQueryConfig.getSafeInfo());
//        }
        return querySubs( subQueryConfig,labSubjectVOS);
    }

    /**
     * 这种方式一般应用与数据权限
     * @param labSubject
     * @param subQueryConfig
     * @return
     */
    public  List<SubDiyVO> querySubs(LabSubject labSubject, SubQueryConfig subQueryConfig)
    {
        List<LabSubjectVO> labSubjectVOS = null;
        //数据和业务权限
        if(SubQueryConfig.mode.data_business.equals(subQueryConfig.getQueryMode()))
        {
            labSubjectVOS = labSubjectService.selectLabSubjectListAUTH(labSubject);
        }
        else if(SubQueryConfig.mode.data.equals(subQueryConfig.getQueryMode()))
        {
            labSubjectVOS = labSubjectService.selectLabSubjectListDataScope(labSubject);
        }
        else if(SubQueryConfig.mode.business.equals(subQueryConfig.getQueryMode()))
        {
            //没有需求需要实现。。。
            throw new ServiceException("查询参数错误！！！");
        }else if(SubQueryConfig.mode.none.equals(subQueryConfig.getQueryMode()))
        {
            labSubjectVOS = subjectMapper.selectLabSubjectVOList(labSubject);;
        }else
        {
            throw new ServiceException("查询参数错误！！！");
        }
        return querySubs(subQueryConfig,labSubjectVOS);

    }

    /**
     * 根据配置查询实验室额外信息
     * @param subQueryConfig 配置
     * @param labSubjects 实验室基础数据
     * @return
     */
    private List<SubDiyVO> querySubs(@Nonnull SubQueryConfig subQueryConfig, List<LabSubjectVO> labSubjects)
    {
        logger.info("当前线程:{}",Thread.currentThread().getName());
        //为空直接返回
        if(CollUtil.isEmpty(labSubjects)) return Collections.emptyList();
        StopWatch sw = new StopWatch("querySubs_TIME");
        sw.start("主线程构建数据任务");
        final Map<String,String> sys_hazard_category = new HashMap<>();
        final Map<String,String> sys_risk_measure = new HashMap<>();
        final Map<String,String> sys_extinguishing_key_points = new HashMap<>();
        if(subQueryConfig.getSafeInfo())
        {
            sys_hazard_category.putAll(DictUtils.getDictCacheMap("sys_hazard_category"));
            sys_risk_measure.putAll(DictUtils.getDictCacheMap("sys_risk_measure"));
            sys_extinguishing_key_points.putAll(DictUtils.getDictCacheMap("sys_extinguishing_key_points"));
        }


        //1.构建返回的数据主题和数据映射
        Map<Long,SubDiyVO> subDiyVOMap = new HashMap<>(labSubjects.size());
        List<SubDiyVO> subDiyVOList = labSubjects.stream().map(a -> {
            SubDiyVO subDiyVO = new SubDiyVO(a);
            subDiyVO.setDeptId(a.getDeptId());
            subDiyVO.setDeptName(a.getDeptName());
            if(subQueryConfig.getBaseInfo())
            {
                //新的危险等级， 因为现在不能该前端了， 所以只能构造跟之前危险等级一样的数据
                LabSubClassVO labSubClassVO = new LabSubClassVO();
                if(a.getLevel()!=null)
                {
                    labSubClassVO.setDangerLevel(Long.valueOf(a.getLevel()));
                }
                subDiyVO.setSubClassVO(labSubClassVO);
            }

            //查询基础信息时，查询安全信息。
            if(subQueryConfig.getBaseInfo()&&subQueryConfig.getSafeInfo())
            {
                //更新安全信息
                SubDiyVO.SafeInfo safeInfo = ConversionSafeInfo(a, sys_hazard_category, sys_risk_measure, sys_extinguishing_key_points);
                subDiyVO.setSafeInfo(safeInfo);
            }

            subDiyVOMap.put(a.getId(),subDiyVO);
            return subDiyVO;
        }).collect(Collectors.toList());

        //2.异步线程结果
        List<CompletableFuture> completableFutures = new ArrayList<>();
        //实验室ids ,这样再拿一边剔除了重复数据
        Long[] longs = labSubjects.stream().map(a -> a.getId()).toArray(Long[]::new);
        //因为当前采用注解的方式没有手动加入线程池，所以这里需要把异步方法写入其他的类中，这样才可以通过代理类执行，否则注解不会生效
        //todo 这里需要重新配置线程池，不能用原生的默认线程池。
        //todo 需要解决子线程异常
        //先这么写 后面重构成自动匹配。
        //查询危险等级,
        sw.stop();
        sw.start("调用异步任务");
        //=====================危险等级开始 需求变更不需要了
//        if(subQueryConfig.getDangerLevel())
//        {
//            CompletableFuture<Map<Long, LabSubClassVO>> mapListenableFuture = subjectManagerService.querySubClass(longs);
//            completableFutures.add(mapListenableFuture);
//            mapListenableFuture.thenAccept(a->{
//                subDiyVOMap.entrySet().forEach(b->{
//                    LabSubClassVO subClassVO = a.get(b.getKey());
//                    b.getValue().setSubClassVO(subClassVO==null?getNONELabSubClassVO:subClassVO);
//                });
//            });
//        }
        //=====================危险等级结束


        //=====================地址开始
        if(subQueryConfig.getAddr())
        {
            CompletableFuture<Map<Long, SubAddrr>> mapCompletableFuture = subjectManagerService.querySubAddrs(labSubjects);
            completableFutures.add(mapCompletableFuture);
            SubAddrr subAddrr1 = new SubAddrr();
            mapCompletableFuture.thenAccept(a->{
                subDiyVOList.forEach(b->{
                    SubAddrr subAddrr = a.get(b.getLayoutId());
                    b.setSubAddrr(subAddrr==null?subAddrr1:subAddrr);
                    b.setSubAddrrStr(SubAddrr.addrrPlus(b.getSubAddrr()));
                });
            });
        }
        //=====================地址结束


        //===================组装传感器测点功能相关开始
        if(subQueryConfig.getSensorFunction())
        {
            //查询传感器
            CompletableFuture<Integer> mapCompletableFuture = subjectManagerService.querySensorsFunction(subQueryConfig,subDiyVOMap,longs);
            completableFutures.add(mapCompletableFuture);
        }
        //===================组装传感器测点功能相关结束

        //===================硬件设备开始
        if(subQueryConfig.getHardware())
        {
            CompletableFuture<Integer> integerCompletableFuture = subjectManagerService.queryHardwares(subDiyVOMap, longs);
            completableFutures.add(integerCompletableFuture);
        }
        //===================硬件设备结束
        sw.stop();


        sw.start("主线程其他构建数据任务");


        Map<String, String> subject_class = new HashMap<>();
        //主线程任务
        if(subQueryConfig.getBaseInfo())
        {
            subject_class.putAll(DictUtils.getDictCacheMap("subject_class"));
        }
        Map<Long,SubDiyVO.SafeInfo> safeInfoMap = new HashMap<>(longs.length);
        if(!subQueryConfig.getBaseInfo()&&subQueryConfig.getSafeInfo())
        {
            List<LabSubjectVO> labSubjectVOS1 = subjectMapper.selectLabSubSafeInfoByIds(longs);
                        labSubjectVOS1.forEach(a->{
                            SubDiyVO.SafeInfo safeInfo = ConversionSafeInfo(a, sys_hazard_category, sys_risk_measure, sys_extinguishing_key_points);
                            safeInfoMap.put(a.getId(),safeInfo);
            });
        }
        Map<Long,Integer> countMap = new HashMap<>(longs.length);
        if(subQueryConfig.getInitDangerCount())
        {
            List<CommonCount>  commonCounts = labHazardSubjectRelationMapper.queryCountBySubIds(longs);
            commonCounts.forEach(a->{
                countMap.put(a.getId(),a.getCount());
            });
        }

        subDiyVOList.forEach(c -> {


            if(subQueryConfig.getBaseInfo())
            {
//                Map<String, String> subject_class = DictUtils.getDictCacheMap("subject_class");
                //设置字典:学科
                String s = subject_class.get(c.getSubTypeValue());
                c.setSubTypeLable(s);


            }
            else
            {
                if(subQueryConfig.getSafeInfo())
                {
                    //更新安全信息
                    c.setSafeInfo(safeInfoMap.get(c.getId()));
                }
            }
            if(subQueryConfig.getInitDangerCount())
            {
                countMap.computeIfPresent(c.getId(),(k,v)->{
                    c.setSourceRiskCount(v);
                    return v;
                });
            }


        });




        sw.stop();
        CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[completableFutures.size()])).join();
        logger.info("子线程全部完成");
        logger.info(sw.prettyPrint());
        logger.info("总消耗:{}秒",sw.getTotalTimeSeconds());
        return subDiyVOList;
    }

    private SubDiyVO.SafeInfo ConversionSafeInfo(LabSubjectVO sub, Map<String,String> sys_hazard_category, Map<String,String> sys_risk_measure,Map<String,String> sys_extinguishing_key_points)
    {

        //=====安全警示牌=====
        SubDiyVO.SafeInfo safeInfo = new SubDiyVO.SafeInfo();
        safeInfo.setSafeUserName(sub.getSafeUserName());
        safeInfo.setSafePhone(sub.getSafeUserPhone());
        safeInfo.setSubAdminName(sub.getAdminName());
        safeInfo.setSubAdminPhone(sub.getAdminPhone());
        safeInfo.setAdminNameAndPhone(sub.getAdminNameAndPhone());
        safeInfo.setSafeUserNameAdminPhone(sub.getSafeUserNameAdminPhone());
        safeInfo.setQrCode(sub.getQrCode());

        // 安全警示标示
        Optional.ofNullable(sub.getSafeSigns())
                .map(a -> a.split(","))
                .ifPresent(a -> {
                    safeInfo.setSafeSigns(Arrays.asList(a));
                });
        //主要危险类别--字典
        Optional.ofNullable(sub.getHazardCategory())
                .map(a -> a.split(","))
                .ifPresent(a -> {
//                    List<SysDictData> sys_safety_warning = DictUtils.getDictCache("sys_hazard_category");
                    List<String> collect = Arrays.stream(a).map(b -> sys_hazard_category.get(b)).filter(b-> StrUtil.isNotBlank(b)).collect(Collectors.toList());
                    safeInfo.setHazardCategory(collect);
                });
        //安全防控措施
        Optional.ofNullable(sub.getRiskMeasure())
                .map(a -> a.split(","))
                .ifPresent(a -> {
                    // 安全警示标示
//                    List<SysDictData> sys_safety_warning = DictUtils.getDictCache("sys_risk_measure");
                    List<String> collect = Arrays.stream(a).map(b -> sys_risk_measure.get(b)).filter(b-> StrUtil.isNotBlank(b)).collect(Collectors.toList());
                    safeInfo.setRiskMeasure(collect);
                });
        //安全防控措施
        Optional.ofNullable(sub.getOutfire())
                .map(a -> a.split(","))
                .ifPresent(a -> {
                    // 安全警示标示
//                    List<SysDictData> sys_safety_warning = DictUtils.getDictCache("sys_extinguishing_key_points");
                    List<String> collect = Arrays.stream(a).map(b -> sys_extinguishing_key_points.get(b)).filter(b-> StrUtil.isNotBlank(b)).collect(Collectors.toList());
                    safeInfo.setOutfire(collect);
                });
        return safeInfo;

    }

}
