package com.yeejoin.amos.boot.module.jyjc.biz.action;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.amos.boot.module.jyjc.api.enums.InspectionCompanyType;
import com.yeejoin.amos.boot.module.jyjc.api.enums.LicenceTypeEnum;
import com.yeejoin.amos.boot.module.jyjc.api.enums.OpenBizTypeEnum;
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.dto.TzBaseEnterpriseInfoDto;
import com.yeejoin.amos.boot.module.ymt.api.entity.TzBaseUnitLicence;
import com.yeejoin.amos.boot.module.ymt.api.mapper.TzBaseEnterpriseInfoMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.TzBaseUnitLicenceMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author LiuLin
 * @date 2024年01月02日 15:45
 */
@Component
@Slf4j
public class RuleActionHandler {

    private final TzBaseUnitLicenceMapper baseUnitLicenceMapper;
    private final TzBaseEnterpriseInfoMapper tzBaseEnterpriseInfoMapper;
    private final RedissonClient redissonClient;
    private RedisUtils redisUtils;
    private RuleCommonServiceImpl ruleCommonService;

    /**
     * 省特检院等特殊公司code
     */
    @Value("${special.inspection.company.code:126100004352004822,12100000400001774F,91340100MA8QN8Q40G}")
    private String specialInspectionCompanyCode;


    public RuleActionHandler(TzBaseUnitLicenceMapper baseUnitLicenceMapper,
                             TzBaseEnterpriseInfoMapper tzBaseEnterpriseInfoMapper,
                             RedissonClient redissonClient, RedisUtils redisUtils,
                             RuleCommonServiceImpl ruleCommonService) {
        this.baseUnitLicenceMapper = baseUnitLicenceMapper;
        this.tzBaseEnterpriseInfoMapper = tzBaseEnterpriseInfoMapper;
        this.redissonClient = redissonClient;
        this.redisUtils = redisUtils;
        this.ruleCommonService = ruleCommonService;
    }

    /**
     * 规则回调方法-首次提交的检验机构匹配
     *
     * @param bizObj                请求变量参数
     * @param itemCode              核对项目编码，多个用逗号分割
     * @param isMatchArea           是否进行属地过滤（检验检测机构的开通区域包含设备归属地市），true-过滤，false-不过滤
     * @param defaultInspectionCode 默认的检验机构code，多个用逗号分割，在按照属地匹配不上时，默认的检验机构
     * @param legalInspectionCodes  法定的检验机构code，多个用逗号分割，检验机构的最大集合-不为空
     * @param inspectionCompanyType 需要显示的单位类型：legal-法定、third-第3方、all-全部，默认都是legal-法定，预留需求变更点
     * @param isMatchItem           是否需要匹配核准代码：true-匹配； false-不匹配
     * @param isMustAccept          true-必须处理，false-可不予受理
     */
    public void filterInspectionOrgAction(Object bizObj, String itemCode, Boolean isMatchArea, String defaultInspectionCode, String legalInspectionCodes, String inspectionCompanyType, Boolean isMatchItem, Boolean isMustAccept) {
        log.info("收到首次提交的检验机构匹配规则回调：请求变量参数:{},核对项目编码:{},是否进行属地过滤:{},默认的检验机构code:{}, 法定的检验机构code:{}, 检验机构类型: {},是否需要匹配核准代码:{},是否必须处理：{}", bizObj, itemCode, isMatchArea, defaultInspectionCode, legalInspectionCodes, inspectionCompanyType, isMatchItem, isMustAccept);
        InspectionEquipInfo inspectionEquipInfo = (InspectionEquipInfo) bizObj;
        // 规则不支持或者的关系消息会重复故去重处理
        RLock lock = redissonClient.getLock(this.buildLockKey("filterInspectionOrgAction", inspectionEquipInfo.getUuid()));
        try {
            boolean isLocked = lock.tryLock(0, 180, TimeUnit.SECONDS);
            if (!isLocked) {
                log.warn("规则回填重复丢弃：请求变量参数:{},核对项目编码:{},是否进行属地过滤:{},默认的检验机构code:{}, 法定的检验机构code:{}, 法定的检验机构code: {},是否需要匹配核准代码:{}", bizObj, itemCode, isMatchArea, defaultInspectionCode, legalInspectionCodes, inspectionCompanyType, isMatchItem);
                return;
            }
            if (redisUtils.hasKey(this.buildRedisDataKey("filterInspectionOrgAction", inspectionEquipInfo.getUuid()))) {
                log.warn("规则回填重复丢弃：请求变量参数:{},核对项目编码:{},是否进行属地过滤:{},默认的检验机构code:{}, 法定的检验机构code:{}, 法定的检验机构code: {},是否需要匹配核准代码:{}", bizObj, itemCode, isMatchArea, defaultInspectionCode, legalInspectionCodes, inspectionCompanyType, isMatchItem);
                return;
            }
            redisUtils.set(this.buildRedisDataKey("filterInspectionOrgAction", inspectionEquipInfo.getUuid()), inspectionEquipInfo.getUuid(), 3600);
            // 1.获取所有的符合资质条件的单位许可信息
            List<TzBaseUnitLicence> unitLicenceList = getBaseUnitLicenceList(itemCode, isMatchItem);
            // 2.匹配过滤机构信息,默认检验机构（目前检测没配置规则，后续检测也需要配置规则时，需要规则那回调方法新增参数，区分检验还是检测）
            List<TzBaseEnterpriseInfoDto> tzBaseEnterpriseInfoList = getInspectionUnitListForFirstCommit(unitLicenceList, isMatchArea, inspectionEquipInfo, OpenBizTypeEnum.JY.getCode(), defaultInspectionCode, legalInspectionCodes, isMustAccept);
            ruleCommonService.publishMqttMessage(inspectionEquipInfo.getComponentKey(), tzBaseEnterpriseInfoList);
        } catch (MqttException | InterruptedException e) {
            log.error("首次提交的检验机构匹配动作执行失败: {}", e.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private String buildLockKey(String type, String uuid) {
        return String.format("%s-%s", type, uuid);
    }

    private String buildRedisDataKey(String type, String uuid) {
        return String.format("RECEIVED-UUID-RULE:%s-%s", type, uuid);
    }


    /**
     * 规则回调方法-不予受理的检验机构匹配
     *
     * @param bizObj                请求变量参数
     * @param itemCode              核对项目编码，多个用逗号分割
     * @param isMatchArea           是否进行属地过滤（检验检测机构的开通区域包含设备归属地市），true-过滤，false-不过滤
     * @param defaultInspectionCode 默认的检验机构code
     * @param inspectionCompanyType 需要显示的单位类型：legal-法定、third-第3方、all-全部，默认都是legal-法定，预留需求变更点
     * @param legalInspectionCodes  法定的检验机构code，多个用逗号分割，检验机构的最大集合-不为空
     * @param isMustAccept          true-必须处理，false-可不予受理
     */
    public void noAcceptInspectionOrgMatchAction(Object bizObj, String itemCode, Boolean isMatchArea, String defaultInspectionCode, String inspectionCompanyType, Boolean isMatchItem, String legalInspectionCodes, Boolean isMustAccept) {
        log.info("收到不予受理的检验机构匹配规则回调：请求变量参数:{},核对项目编码:{},是否进行属地过滤:{},默认的检验机构code:{}, 法定的检验机构code:{}, 检验机构类型: {},是否需要匹配核准代码:{},是否必须处理：{}", bizObj, itemCode, isMatchArea, defaultInspectionCode, legalInspectionCodes, inspectionCompanyType, isMatchItem, isMustAccept);
        InspectionEquipInfo inspectionEquipInfo = (InspectionEquipInfo) bizObj;
        try {
            // 1.获取所有的符合资质条件的单位许可信息
            List<TzBaseUnitLicence> unitLicenceList = getBaseUnitLicenceList(itemCode, isMatchItem);
            // 2.匹配过滤机构信息,默认检验机构（目前检测没配置规则，后续检测也需要配置规则时，需要规则那回调方法新增参数，区分检验还是检测）
            List<TzBaseEnterpriseInfoDto> tzBaseEnterpriseInfoList = getInspectionUnitListForNoAccept(unitLicenceList, isMatchArea, inspectionEquipInfo, OpenBizTypeEnum.JY.getCode(), defaultInspectionCode, legalInspectionCodes, inspectionCompanyType, isMustAccept);
            ruleCommonService.publishMqttMessage(inspectionEquipInfo.getComponentKey(), tzBaseEnterpriseInfoList);
        } catch (MqttException e) {
            log.error("Error publishing MQTT message: {}", e.getMessage());
        }
    }

    private List<TzBaseUnitLicence> getBaseUnitLicenceList(String itemCode, Boolean isMatchItem) {
        List<TzBaseUnitLicence> tzBaseUnitLicences;
        if (isMatchItem && StringUtils.isNotEmpty(itemCode)) {
            tzBaseUnitLicences = baseUnitLicenceMapper.selectList(new LambdaQueryWrapper<TzBaseUnitLicence>()
                    .select(TzBaseUnitLicence::getUnitCode)
                    .eq(TzBaseUnitLicence::getLicenceType, LicenceTypeEnum.JY_JC.getCode())
                    .in(TzBaseUnitLicence::getItemCode, Arrays.asList(itemCode.split(",")))
                    .ge(TzBaseUnitLicence::getExpiryDate, LocalDate.now()));
        } else {
            tzBaseUnitLicences = baseUnitLicenceMapper.selectList(new LambdaQueryWrapper<TzBaseUnitLicence>()
                    .select(TzBaseUnitLicence::getUnitCode)
                    .eq(TzBaseUnitLicence::getLicenceType, LicenceTypeEnum.JY_JC.getCode())
                    .ge(TzBaseUnitLicence::getExpiryDate, LocalDate.now()));
        }
        return tzBaseUnitLicences;
    }

    private List<TzBaseEnterpriseInfoDto> getInspectionUnitListForNoAccept(List<TzBaseUnitLicence> unitLicenceList, Boolean isMatchArea, InspectionEquipInfo inspectionEquipInfo, String openBizType, String defaultInspectionCode, String legalInspectionCodes, String inspectionCompanyType, Boolean isCanNoAccept) {
        // 是否进行属地过滤
        String areaCode = isMatchArea ? inspectionEquipInfo.getAreaCode() : "";
        // 所有符合单位的code
        Set<String> unitCodes = unitLicenceList.stream().map(TzBaseUnitLicence::getUnitCode).collect(Collectors.toSet());
        List<TzBaseEnterpriseInfoDto> matchEnterpriseInfos = new ArrayList<>();
        // 按照资质 + areaCode，进行设备单位的筛选
        if (unitCodes.size() > 0) {
            matchEnterpriseInfos = tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(new ArrayList<>(unitCodes), areaCode, openBizType);
            // 返回下一步是否可不予返回，供下一节点审核时显示判断是否显示不予受理按钮，在提交时数据会写入到主表：true-必须处理，false-可不予受理
            this.setIsCanNoAccept(matchEnterpriseInfos, isCanNoAccept);
        }

        if (inspectionCompanyType.equals(InspectionCompanyType.THIRD.getCode())) {
            // 第三方时需要去掉法定机构
            log.info("第三方检验机构开始匹配");
            //目前规则配置的法定名单为陕西省内地市的法定机构
            matchEnterpriseInfos = matchEnterpriseInfos.stream().filter(e -> !legalInspectionCodes.contains(e.getUseCode())).collect(Collectors.toList());
            // 还需要去掉定向的机构：陕西省、国机特种设备检验有限公司、中国特种设备检验检测研究院
            matchEnterpriseInfos = matchEnterpriseInfos.stream().filter(c -> !specialInspectionCompanyCode.contains(c.getUseCode())).collect(Collectors.toList());
            log.info("匹配到的第三方检验机构：{}", matchEnterpriseInfos);
            // 未匹配到第三方机构时，返回指定的默认的法定机构, 默认及法定机构不进行开通区域的匹配
            if (matchEnterpriseInfos.isEmpty()) {
                log.info("按照资质、区域未匹配到第三方检验机构");
                matchEnterpriseInfos = tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(new ArrayList<>(Arrays.asList(defaultInspectionCode.split(","))), "", openBizType);
                // 返回下一步是否可不予返回，供下一节点审核时显示判断是否显示不予受理按钮，在提交时数据会写入到主表：true-必须处理，false-可不予受理
                this.setIsCanNoAccept(matchEnterpriseInfos, true);
            }
        } else if (inspectionCompanyType.equals(InspectionCompanyType.LEGAL.getCode())) {
            // 法定时，机构为定义的法定机构 defaultInspectionCode
            log.info("法定检验机构开始匹配");
            matchEnterpriseInfos = matchEnterpriseInfos.stream().filter(e -> defaultInspectionCode.contains(e.getUseCode())).collect(Collectors.toList());
            log.info("匹配到的法定检验机构：{}", matchEnterpriseInfos);
        }
        return matchEnterpriseInfos;
    }

    private List<TzBaseEnterpriseInfoDto> getInspectionUnitListForFirstCommit(List<TzBaseUnitLicence> unitLicenceList, Boolean isMatchArea, InspectionEquipInfo inspectionEquipInfo, String openBizType, String defaultInspectionCode, String legalInspectionCodes, Boolean isCanNoAccept) {
        // 是否进行属地过滤
        String areaCode = isMatchArea ? inspectionEquipInfo.getAreaCode() : "";
        // 所有符合单位的code
        Set<String> unitCodes = unitLicenceList.stream()
                .map(TzBaseUnitLicence::getUnitCode)
                .collect(Collectors.toSet());
        // 将法定机构和符合资质要求的机构取交集
        List<String> legalUnitCodes = Arrays.stream(legalInspectionCodes.split(",")).filter(unitCodes::contains).collect(Collectors.toList());
        List<TzBaseEnterpriseInfoDto> matchEnterpriseInfos = new ArrayList<>();
        // 按照资质 + areaCode，进行设备单位的筛选
        if (legalUnitCodes.size() > 0) {
            matchEnterpriseInfos = tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(legalUnitCodes, areaCode, openBizType);
            // 返回下一步是否可不予返回，供下一节点审核时显示判断是否显示不予受理按钮，在提交时数据会写入
            this.setIsCanNoAccept(matchEnterpriseInfos, isCanNoAccept);
        }
        // 未匹配到法定机构时，返回指定的默认的法定机构, 默认及法定机构不进行开通区域的匹配
        if (matchEnterpriseInfos.isEmpty()) {
            log.info("按照资质、区域未匹配到法定检验机构");
            matchEnterpriseInfos = tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(new ArrayList<>(Arrays.asList(defaultInspectionCode.split(","))), "", openBizType);
            // 返回下一步是否可不予返回，供下一节点审核时显示判断是否显示不予受理按钮，在提交时数据会写入到主表：true-必须处理，false-可不予受理
            this.setIsCanNoAccept(matchEnterpriseInfos, isCanNoAccept);
        }
        return matchEnterpriseInfos;
    }

    private void setIsCanNoAccept(List<TzBaseEnterpriseInfoDto> matchEnterpriseInfos, Boolean isCanNoAccept) {
        matchEnterpriseInfos.forEach(m -> m.setUseCodeAndName(m.getUseCode() + "_" + isCanNoAccept));
    }


}

