package com.yeejoin.amos.boot.module.jyjc.biz.event.rule.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yeejoin.amos.boot.biz.common.bo.ReginParams;
import com.yeejoin.amos.boot.biz.common.entity.BaseEntity;
import com.yeejoin.amos.boot.biz.common.utils.RedisKey;
import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.amos.boot.biz.common.utils.SnowflakeIdUtil;
import com.yeejoin.amos.boot.module.common.api.enums.EquipmentClassifityEnum;
import com.yeejoin.amos.boot.module.jyjc.api.common.BizCommonConstant;
import com.yeejoin.amos.boot.module.jyjc.api.entity.JyjcInspectionApplicationNoAcceptLog;
import com.yeejoin.amos.boot.module.jyjc.api.enums.JYJCTypeEnum;
import com.yeejoin.amos.boot.module.jyjc.api.enums.RequestTypeEnum;
import com.yeejoin.amos.boot.module.jyjc.api.mapper.JyjcInspectionApplicationNoAcceptLogMapper;
import com.yeejoin.amos.boot.module.jyjc.api.model.InspectionEquipInfoModel;
import com.yeejoin.amos.boot.module.jyjc.biz.event.InspectionOrgRefreshEvent;
import com.yeejoin.amos.boot.module.jyjc.biz.event.TouchRuleEvent;
import com.yeejoin.amos.boot.module.jyjc.biz.event.publisher.EventPublisher;
import com.yeejoin.amos.boot.module.jyjc.biz.rule.InspectionEquipInfo;
import com.yeejoin.amos.boot.module.jyjc.biz.service.impl.RuleCommonServiceImpl;
import com.yeejoin.amos.boot.module.ymt.api.entity.IdxBizJgProjectContraption;
import com.yeejoin.amos.boot.module.ymt.api.entity.IdxBizJgUseInfo;
import com.yeejoin.amos.boot.module.ymt.api.entity.TzBaseEnterpriseInfo;
import com.yeejoin.amos.boot.module.ymt.api.mapper.IdxBizJgProjectContraptionMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.IdxBizJgUseInfoMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.TzBaseEnterpriseInfoMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

import static com.yeejoin.amos.boot.module.jyjc.api.enums.CategoryEnum.getCategoryByType;

/**
 * 启用规则时业务处理
 *
 * @author Administrator
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class EnableRuleDataPreparationService {

    private final SnowflakeIdUtil sequence;


    private final RuleCommonServiceImpl ruleCommonService;

    private final JyjcInspectionApplicationNoAcceptLogMapper inspectionApplicationNoAcceptLogMapper;

    private final EventPublisher publisher;

    private final List<BlockingQueue<InspectionEquipInfoModel>> hashCodeBlockingQueues = new ArrayList<>();

    private final IdxBizJgProjectContraptionMapper jgProjectContraptionMapper;

    private final TzBaseEnterpriseInfoMapper baseEnterpriseInfoMapper;

    private final RedisUtils redisUtils;
    private final IdxBizJgUseInfoMapper idxBizJgUseInfoMapper;

    @Value("${rule.data.preparation.thread.number:2}")
    private int threadNumber;

    /**
     * 默认的企业标签：管控等级
     */
    @Value("${enterprise.regulatoryLabels.default.value:三级管控}")
    private String defaultRegulatoryLabels;

    /**
     * 压力类设备种类
     */
    private static final List<String> PRESSURE_EQU_LIST = Arrays.asList("8000", "1000", "2000");


    public void onApplicationEvent(InspectionOrgRefreshEvent event) {
        int queueIndex = Math.abs(event.getInspectionEquipInfoModel().getRecord().hashCode()) % threadNumber;
        hashCodeBlockingQueues.get(queueIndex).add(event.getInspectionEquipInfoModel());
    }

    @PostConstruct
    public void init() {
        // 1.初始化队列，按照线程数动态创建队列
        initQueue();
        // 2.初始化多线程消费线程
        ExecutorService executorService = Executors.newFixedThreadPool(threadNumber);
        for (int i = 0; i < threadNumber; i++) {
            BlockingQueue<InspectionEquipInfoModel> queue = hashCodeBlockingQueues.get(i);
            executorService.execute(() -> {
                while (true) {
                    try {
                        InspectionEquipInfoModel fillingRecordStatisticsUnitDayDto = queue.take();
                        this.dealData(fillingRecordStatisticsUnitDayDto);
                    } catch (Exception e) {
                        log.error(e.getMessage(), e);
                    }
                }
            });
        }
    }

    private void initQueue() {
        for (int i = 0; i < threadNumber; i++) {
            hashCodeBlockingQueues.add(new LinkedBlockingQueue<>());
        }
    }

    private void dealData(InspectionEquipInfoModel equipInfoModel) {
        InspectionEquipInfo inspectionEquipInfo = new InspectionEquipInfo();
        inspectionEquipInfo.setEquList(equipInfoModel.getEquList());
        inspectionEquipInfo.setEquCategory(equipInfoModel.getEquCategory());
        inspectionEquipInfo.setEquDefine(equipInfoModel.getEquDefine());
        inspectionEquipInfo.setTechParams(new HashMap<>());
        inspectionEquipInfo.setInspectionType(equipInfoModel.getInspectionType());
        inspectionEquipInfo.setBizType(getCategoryByType(JYJCTypeEnum.of(inspectionEquipInfo.getInspectionType())));
        ReginParams reginParams = JSON.parseObject(redisUtils.get(RedisKey.buildReginKey(equipInfoModel.getUserId(), equipInfoModel.getToken())).toString(), ReginParams.class);
        setCompanyInfo(reginParams, inspectionEquipInfo);
        inspectionEquipInfo.setAreaCode(ruleCommonService.getArea(equipInfoModel.getCity(), equipInfoModel.getCounty()));
        inspectionEquipInfo.setDistrictOrCountyCode(equipInfoModel.getCounty());
        inspectionEquipInfo.setComponentKey(equipInfoModel.getComponentKey());
        inspectionEquipInfo.setRecord(equipInfoModel.getRecord());
        inspectionEquipInfo.setUuid(sequence.nextId() + "");
        JyjcInspectionApplicationNoAcceptLog jyjcInspectionApplicationNoAcceptLog = getLastNoAcceptLog(equipInfoModel);
        inspectionEquipInfo.setRequestType(this.buildRequestType(equipInfoModel, jyjcInspectionApplicationNoAcceptLog));
        inspectionEquipInfo.setLastNoAcceptInspectionCode(this.buildLastNoAcceptInspectionCode(jyjcInspectionApplicationNoAcceptLog));
        inspectionEquipInfo.setAppSeq(equipInfoModel.getAppSeq());
        buildInspectionEquipByEquList(equipInfoModel, inspectionEquipInfo);
        // 触发规则
        publisher.publish(new TouchRuleEvent(this, inspectionEquipInfo));
    }

    /**
     * 设置按照设备种类、类别
     *
     * @param equipInfoModel      上送数据
     * @param inspectionEquipInfo 触发规则的
     */
    private void buildInspectionEquipByEquList(InspectionEquipInfoModel equipInfoModel, InspectionEquipInfo inspectionEquipInfo) {
        // 固定时压力容器时需要设置是否球罐
        if (BizCommonConstant.EQU_CATEGORY_CODE_GDYLRQ.equals(equipInfoModel.getEquCategory())) {
            // 是否球罐 0 1 转 boolean
            inspectionEquipInfo.setIsBallValve(!"0".equals(equipInfoModel.getEquipTag1()));
        }
        // 大型游乐设施需要设置设备分类
        if (EquipmentClassifityEnum.YLSS.getCode().equals(equipInfoModel.getEquList())) {
            // 设备分类
            inspectionEquipInfo.setEquipTag1(equipInfoModel.getEquipTag1());
        }
        // 锅炉设置技术参数
        if (EquipmentClassifityEnum.GL.getCode().equals(equipInfoModel.getEquList())) {
            inspectionEquipInfo.setTechParams(this.getTechParams(equipInfoModel));
        }
        // 承压类设置管控级别-非压力管道,取设备的使用单位
        if (PRESSURE_EQU_LIST.contains(equipInfoModel.getEquList()) && !EquipmentClassifityEnum.YLGD.getCode().equals(equipInfoModel.getEquList())) {
            // 取设备的useUnitCreditCode（前端已经限制按照使用单位筛选进行报检）
            String useUnitCode = this.getUseInfo(equipInfoModel.getRecord());
            TzBaseEnterpriseInfo enterpriseInfo = baseEnterpriseInfoMapper.selectOne(new LambdaQueryWrapper<TzBaseEnterpriseInfo>()
                    .eq(TzBaseEnterpriseInfo::getUseUnitCode, useUnitCode).
                    select(BaseEntity::getSequenceNbr, TzBaseEnterpriseInfo::getRegulatoryLabels));
            inspectionEquipInfo.setManageLevel(StringUtils.isNotBlank(enterpriseInfo.getRegulatoryLabels()) ? enterpriseInfo.getRegulatoryLabels() : defaultRegulatoryLabels);
        }
        // 承压类设置管控级别-压力管道,取装置的使用单位
        if (EquipmentClassifityEnum.YLGD.getCode().equals(equipInfoModel.getEquList())) {
            // 取设备的useUnitCreditCode（前端已经限制按照使用单位筛选进行报检）
            String useUnitCode = this.getUseInfoProject(equipInfoModel.getRecord());
            TzBaseEnterpriseInfo enterpriseInfo = baseEnterpriseInfoMapper.selectOne(new LambdaQueryWrapper<TzBaseEnterpriseInfo>()
                    .eq(TzBaseEnterpriseInfo::getUseUnitCode, useUnitCode).
                    select(BaseEntity::getSequenceNbr, TzBaseEnterpriseInfo::getRegulatoryLabels));
            inspectionEquipInfo.setManageLevel(StringUtils.isNotBlank(enterpriseInfo.getRegulatoryLabels()) ? enterpriseInfo.getRegulatoryLabels() : defaultRegulatoryLabels);
        }
    }

    private String getUseInfoProject(String projectContraptionId) {
        IdxBizJgProjectContraption projectContraption = jgProjectContraptionMapper.selectById(projectContraptionId);
        return projectContraption.getUseUnitCreditCode();
    }

    private String getUseInfo(String record) {
        IdxBizJgUseInfo useInfo = idxBizJgUseInfoMapper.selectOne(new LambdaQueryWrapper<IdxBizJgUseInfo>().eq(IdxBizJgUseInfo::getRecord, record).select(IdxBizJgUseInfo::getRecord, IdxBizJgUseInfo::getUseUnitCreditCode));
        return useInfo.getUseUnitCreditCode();
    }

    private static void setCompanyInfo(ReginParams reginParams, InspectionEquipInfo inspectionEquipInfo) {
        Map<String, Object> companyInfo = new HashMap<>();
        companyInfo.put("companyType", reginParams.getCompany().getCompanyType());
        companyInfo.put("companyCode", reginParams.getCompany().getCompanyCode());
        inspectionEquipInfo.setCompanyInfo(companyInfo);
    }

    private String buildLastNoAcceptInspectionCode(JyjcInspectionApplicationNoAcceptLog jyjcInspectionApplicationNoAcceptLog) {
        if (jyjcInspectionApplicationNoAcceptLog != null) {
            return jyjcInspectionApplicationNoAcceptLog.getInspectionUnitCode();
        }
        return "";
    }

    private Map<String, Object> getTechParams(InspectionEquipInfoModel equipInfoModel) {
        Map<String, Object> param = new HashMap<>();
        BigDecimal capacityThermalPower = Optional.ofNullable(equipInfoModel.getRatedEvaporationCapacityThermalPower())
                .map(val -> new BigDecimal(val).setScale(0, RoundingMode.HALF_UP))
                .orElse(BigDecimal.ZERO); // 默认值
        BigDecimal workingPressure = Optional.ofNullable(equipInfoModel.getRatedWorkingPressure())
                .map(val -> new BigDecimal(val).setScale(0, RoundingMode.HALF_UP))
                .orElse(BigDecimal.ZERO); // 默认值
        param.put(BizCommonConstant.TECH_PARAM1, capacityThermalPower);
        param.put(BizCommonConstant.TECH_PARAM2, workingPressure);
        return param;
    }

    private String buildRequestType(InspectionEquipInfoModel equipInfoModel, JyjcInspectionApplicationNoAcceptLog jyjcInspectionApplicationNoAcceptLog) {
        // 工作台发起申请
        if (StringUtils.isEmpty(equipInfoModel.getAppSeq())) {
            return RequestTypeEnum.FIRST_REQUEST.getCode();
        }
        // 没有不予受理请求记录
        if (jyjcInspectionApplicationNoAcceptLog == null) {
            return RequestTypeEnum.FIRST_REQUEST.getCode();
        }
        // 有记录
        return RequestTypeEnum.NO_ACCEPT_REQUEST.getCode();
    }

    private JyjcInspectionApplicationNoAcceptLog getLastNoAcceptLog(InspectionEquipInfoModel equipInfoModel) {
        LambdaQueryWrapper<JyjcInspectionApplicationNoAcceptLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(JyjcInspectionApplicationNoAcceptLog::getApplicationSeq, equipInfoModel.getAppSeq());
        wrapper.orderByDesc(JyjcInspectionApplicationNoAcceptLog::getRecDate);
        wrapper.last("limit 1");
        wrapper.select(JyjcInspectionApplicationNoAcceptLog::getApplicationSeq, JyjcInspectionApplicationNoAcceptLog::getInspectionUnitCode);
        return inspectionApplicationNoAcceptLogMapper.selectOne(wrapper);
    }
}

