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

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
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.api.enums.TopicEnum;
import com.yeejoin.amos.boot.module.jyjc.biz.rule.InspectionEquipInfo;
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.entity.UseInfo;
import com.yeejoin.amos.boot.module.ymt.api.mapper.TzBaseEnterpriseInfoMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.TzBaseUnitLicenceMapper;
import com.yeejoin.amos.boot.module.ymt.api.mapper.UseInfoMapper;
import com.yeejoin.amos.component.rule.config.RuleConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.typroject.tyboot.component.emq.EmqKeeper;

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

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

    private final EmqKeeper emqKeeper;
    private final TzBaseUnitLicenceMapper baseUnitLicenceMapper;
    private final TzBaseEnterpriseInfoMapper tzBaseEnterpriseInfoMapper;
    private final UseInfoMapper useInfoMapper;
    /**
     * 特殊独立的区县
     */
    private static final String[] EXCLUSION_CITY_REGIONS = {"610403", "610581"};

    /**
     * 省特检院公司code
     */
    @Value("${province.inspection.company.code:126100004352004822}")
    private String provinceInspectionCompanyCode;


    public RuleActionHandler(EmqKeeper emqKeeper, TzBaseUnitLicenceMapper baseUnitLicenceMapper,
                             TzBaseEnterpriseInfoMapper tzBaseEnterpriseInfoMapper,
                             UseInfoMapper useInfoMapper) {
        this.emqKeeper = emqKeeper;
        this.baseUnitLicenceMapper = baseUnitLicenceMapper;
        this.tzBaseEnterpriseInfoMapper = tzBaseEnterpriseInfoMapper;
        this.useInfoMapper = useInfoMapper;
    }

    /**
     * 规则回调方法-首次提交的检验机构匹配
     *
     * @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-不匹配
     */
    public void filterInspectionOrgAction(Object bizObj, String itemCode, Boolean isMatchArea, String defaultInspectionCode, String legalInspectionCodes, String inspectionCompanyType, Boolean isMatchItem) {
        log.info("收到首次提交的检验机构匹配规则回调：请求变量参数:{},核对项目编码:{},是否进行属地过滤:{},默认的检验机构code:{}, 法定的检验机构code:{}, 法定的检验机构code: {},是否需要匹配核准代码:{}", bizObj, itemCode, isMatchArea, defaultInspectionCode, legalInspectionCodes, inspectionCompanyType, isMatchItem);
        InspectionEquipInfo inspectionEquipInfo = (InspectionEquipInfo) bizObj;
        try {
            // 1.获取所有的符合资质条件的单位许可信息
            List<TzBaseUnitLicence> unitLicenceList = getBaseUnitLicenceList(itemCode, isMatchItem);
            // 2.匹配过滤机构信息,默认检验机构（目前检测没配置规则，后续检测也需要配置规则时，需要规则那回调方法新增参数，区分检验还是检测）
            List<TzBaseEnterpriseInfoDto> tzBaseEnterpriseInfoList = getInspectionUnitListForFirstCommit(unitLicenceList, isMatchArea, inspectionEquipInfo.getRecord(), OpenBizTypeEnum.JY.getCode(), defaultInspectionCode, legalInspectionCodes);
            publishMqttMessage(inspectionEquipInfo.getComponentKey(), tzBaseEnterpriseInfoList);
        } catch (MqttException e) {
            log.error("Error publishing MQTT message: {}", e.getMessage());
        }
    }


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

    public List<TzBaseEnterpriseInfoDto> getInspectionUnitList(List<TzBaseUnitLicence> unitLicenceList, Boolean isMatchArea, String record, String openBizType) {
        // 是否进行属地过滤
        String areaCode = isMatchArea ? this.getArea(record) : "";
        // 所有符合单位的code
        List<String> unitCodes = unitLicenceList.stream().map(TzBaseUnitLicence::getUnitCode).collect(Collectors.toList());
        // 按照资质 + areaCode，进行设备单位的筛选
        List<TzBaseEnterpriseInfoDto> matchEnterpriseInfos = tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(unitCodes, areaCode, openBizType);
        if (matchEnterpriseInfos.isEmpty()) {
            log.info("按照资质、区域未匹配到对应的单位、按照地市未匹配到单位，将返回全部单位");
            return tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(null, null, openBizType);
        }
        return matchEnterpriseInfos;
    }

    private List<TzBaseEnterpriseInfoDto> getInspectionUnitListForNoAccept(List<TzBaseUnitLicence> unitLicenceList, Boolean isMatchArea, String record, String openBizType, String defaultInspectionCode, String legalInspectionCodes, String inspectionCompanyType) {
        // 是否进行属地过滤
        String areaCode = isMatchArea ? this.getArea(record) : "";
        // 所有符合单位的code
        List<String> unitCodes = unitLicenceList.stream().map(TzBaseUnitLicence::getUnitCode).collect(Collectors.toList());
        // 按照资质 + areaCode，进行设备单位的筛选
        List<TzBaseEnterpriseInfoDto> matchEnterpriseInfos = tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(unitCodes, areaCode, openBizType);
        // 第三方时需要去掉法定机构
        if (inspectionCompanyType.equals(InspectionCompanyType.THIRD.getCode())) {
            log.info("第三方检验机构开始匹配");
            matchEnterpriseInfos = matchEnterpriseInfos.stream().filter(e -> !legalInspectionCodes.contains(e.getUseCode())).collect(Collectors.toList());
            log.info("匹配到的第三方检验机构：{}", matchEnterpriseInfos);
        }
        // 法定时，机构为定义的法定机构 defaultInspectionCode
        if (inspectionCompanyType.equals(InspectionCompanyType.LEGAL.getCode())) {
            log.info("法定检验机构开始匹配");
            matchEnterpriseInfos = matchEnterpriseInfos.stream().filter(e -> defaultInspectionCode.contains(e.getUseCode())).collect(Collectors.toList());
            log.info("匹配到的法定检验机构：{}", matchEnterpriseInfos);
        }
        // 代码严谨判断，如前置调整法定机构单位为注册，返回全部的法定机构和第3方机构
        if (matchEnterpriseInfos.isEmpty()) {
            log.info("指定的默认的检验机构未匹配到，将返回所有的检验机构包括第三方和法定检验机构，省特检院除外");
            matchEnterpriseInfos = tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(null, null, openBizType);
            matchEnterpriseInfos = matchEnterpriseInfos.stream().filter(c -> !provinceInspectionCompanyCode.contains(c.getUseCode())).collect(Collectors.toList());
            log.info("指定的默认的检验机构未匹配到，将返回所有的检验机构包括第三方和法定检验机构，数据为：{}", matchEnterpriseInfos);
        }
        return matchEnterpriseInfos;
    }

    private List<TzBaseEnterpriseInfoDto> getInspectionUnitListForFirstCommit(List<TzBaseUnitLicence> unitLicenceList, Boolean isMatchArea, String record, String openBizType, String defaultInspectionCode, String legalInspectionCodes) {
        // 是否进行属地过滤
        String areaCode = isMatchArea ? this.getArea(record) : "";
        // 所有符合单位的code
        List<String> unitCodes = unitLicenceList.stream()
                .map(TzBaseUnitLicence::getUnitCode)
                .collect(Collectors.toList());
        // 将法定机构和符合资质要求的机构取交集
        List<String> legalUnitCodes = Arrays.stream(legalInspectionCodes.split(",")).filter(unitCodes::contains).collect(Collectors.toList());
        // 按照资质 + areaCode，进行设备单位的筛选
        List<TzBaseEnterpriseInfoDto> matchEnterpriseInfos = tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(legalUnitCodes, areaCode, openBizType);
        // 未匹配到法定机构时，返回指定的默认的法定机构
        if (matchEnterpriseInfos.isEmpty()) {
            log.info("按照资质、区域未匹配到法定检验机构");
            matchEnterpriseInfos = tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(new ArrayList<>(Arrays.asList(defaultInspectionCode.split(","))), areaCode, openBizType);
        }
        // 代码严谨判断，如前置调整法定机构单位为注册，返回全部的法定机构和第3方机构
        if (matchEnterpriseInfos.isEmpty()) {
            log.info("指定的默认的检验机构未匹配到，将返回所有的检验机构包括第三方和法定检验机构，省特检院除外");
            matchEnterpriseInfos = tzBaseEnterpriseInfoMapper.getInspectionUnitListByCode(null, null, openBizType);
            matchEnterpriseInfos = matchEnterpriseInfos.stream().filter(c -> !provinceInspectionCompanyCode.contains(c.getUseCode())).collect(Collectors.toList());
        }
        return matchEnterpriseInfos;
    }

    private String getArea(String record) {
        UseInfo equipUseInfo = useInfoMapper.selectOne(Wrappers.<UseInfo>lambdaQuery().select(UseInfo::getCity, UseInfo::getCounty).eq(UseInfo::getRecord, record));
        if (equipUseInfo == null) {
            return "";
        }
        // 特殊地区特殊处理，目前有韩城、杨凌,原因行政区划上是有层级的，但是业务办理时，他们与所在地市是同级别的
        if (StringUtils.isEmpty(equipUseInfo.getCity()) || StringUtils.isEmpty(equipUseInfo.getCounty())) {
            return "";
        }
        if (Arrays.asList(EXCLUSION_CITY_REGIONS).contains(equipUseInfo.getCounty())) {
            return equipUseInfo.getCounty();
        }
        return equipUseInfo.getCity();
    }

    private void publishMqttMessage(String componentKey, Object message) throws MqttException {
        log.info("报检规则推送到web主题：{}:,消息内容为：{}", String.format(TopicEnum.INSPECTION_LIST_PUSH.getTopic(), componentKey), JSON.toJSONString(message));
        try {
            emqKeeper.getMqttClient().publish(String.format(TopicEnum.INSPECTION_LIST_PUSH.getTopic(), componentKey),
                    JSON.toJSONString(message).getBytes(), RuleConfig.DEFAULT_QOS, false);
        } catch (MqttException e) {
            log.error("Error publishing MQTT message: {}", e.getMessage());
            throw e;
        }
    }
}

