package jnpf.certificate.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jnpf.authority.utils.PermissionsUtils;
import jnpf.base.ActionResult;
import jnpf.certificate.CertificateWarningApi;
import jnpf.certificate.helper.NoticeHelper;
import jnpf.certificate.helper.OrganizationHelper;
import jnpf.certificate.mapper.CertificateInstanceMapper;
import jnpf.model.certificate.po.CertificateInstanceEntity;
import jnpf.model.storecertificatephoto.po.StoreCertificatePhotoEntity;
import jnpf.model.warningnotice.enums.CertificateTypeEnum;
import jnpf.model.warningnotice.vo.WarningNoticeTargetVO;
import jnpf.model.warningnotice.vo.WarningNoticeUserConfigVO;
import jnpf.model.warningnotice.vo.WarningNoticeVO;
import jnpf.permission.UserApi;
import jnpf.permission.V2UserApi;
import jnpf.permission.dto.v2.user.QueryUserBatchDTO;
import jnpf.permission.entity.UserEntity;
import jnpf.permission.vo.v2.organzie.OrganizeBaseInfoVO;
import jnpf.permission.vo.v2.user.UserBaseInfoVO;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.storecertificatephoto.mapper.StoreCertificatePhotoMapper;
import jnpf.storecertificatephoto.service.WarningNoticeService;
import jnpf.util.CustomTenantUtil;
import jnpf.util.NoDataSourceBind;
import jnpf.util.StringUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static jnpf.certificate.helper.NoticeHelper.*;

/**
 * 证照预警接口实现。
 */
@Slf4j
@RestController
@RequestMapping("/web/certificate-warning-api")
public class CertificateWarningController implements CertificateWarningApi {

    private static final int STATUS_MISSING = 1;
    private static final int STATUS_EXPIRED = 2;
    private static final int STATUS_NEAR_EXPIRE = 3;
    private static final int STATUS_NORMAL = 4;

    private static final int SUBJECT_TYPE_EMPLOYEE = 1;
    private static final int DEFAULT_NEAR_EXPIRE_DAYS = 30;
    private static final int DEFAULT_NOTICE_FREQUENCY_DAYS = 1;
    private static final int STORE_CUSTOM_TEMPLATE_DISABLED_STATUS = 0;
    private static final int USER_ADMINISTRATOR_FLAG = 1;

    private static final String NOTICE_USER_TYPE_POSITION = "position";
    private static final String NOTICE_USER_TYPE_PERSONNEL = "personnel";
    private static final String NOTICE_USER_TYPE_PERSON = "person";

    @Value("${config.certificate.module.id:813679492035805253}")
    private String HEALTH_CERTIFICATE_PERMISSION_MODULE_ID;

    @Autowired
    private CertificateInstanceMapper certificateInstanceMapper;
    @Autowired
    private WarningNoticeService warningNoticeService;
    @Autowired
    private StoreCertificatePhotoMapper storeCertificatePhotoMapper;
    @Autowired
    private NoticeHelper noticeHelper;
    @Autowired
    private V2UserApi v2UserApi;
    @Autowired
    private UserApi userApi;
    @Autowired
    private PermissionsUtils permissionsUtils;
    @Autowired
    private CustomTenantUtil customTenantUtil;
    @Autowired
    private OrganizationHelper organizationHelper;

    /**
     * 检查证照状态并发送临期通知：
     * 1. 将正常/临期状态按规则纠正为临期或过期。
     * 2. 向临期对象发送预警消息（健康证通知本人，组织/门店证照通知组织负责人）。
     *
     * @param tenantId 租户ID
     * @return 处理结果
     */
    @Override
    @NoDataSourceBind
    @PostMapping("/checkAndSendCertificateWarning")
    public ActionResult<Boolean> checkAndSendCertificateWarning(@RequestParam("tenantId") String tenantId) {
        try {
            log.error("checkAndSendCertificateWarning tenantId:{}",tenantId);
            customTenantUtil.checkOutTenant(tenantId);
            doCheckAndSend(StrUtil.trim(tenantId));
            return ActionResult.success(Boolean.TRUE);
        } catch (Exception e) {
            log.error("检查并发送证照预警失败，tenantId={}", tenantId, e);
            return ActionResult.success(Boolean.FALSE);
        }
    }

    /**
     * 执行检查与发送。
     */
    private void doCheckAndSend(String tenantId) {
        List<CertificateInstanceEntity> candidateList = queryCandidateCertificateList();
        log.error("doCheckAndSend candidateList size:{}", CollUtil.isEmpty(candidateList) ? 0 : candidateList.size());
        if (CollUtil.isEmpty(candidateList)) {
            return;
        }

        Map<String, WarningNoticeVO> warningConfigMap = queryWarningConfigMap();
        Map<String, StoreCertificatePhotoEntity> templateMap = queryTemplateMap(candidateList);
        Map<String, Integer> templateNearExpireDaysMap = buildTemplateNearExpireDaysMap(templateMap);

        List<CertificateInstanceEntity> needUpdateList = new ArrayList<>();
        List<NearExpireNoticeTask> noticeTaskList = new ArrayList<>();
        for (CertificateInstanceEntity entity : candidateList) {
            if (entity == null || StrUtil.isBlank(entity.getId()) || StrUtil.isBlank(entity.getCertificateType())) {
                continue;
            }
            WarningNoticeVO warningNoticeVO = buildWarningNoticeVO(entity, warningConfigMap);
            boolean skipByExpiryReminderDays = shouldSkipWarningNoticeByExpiryReminderDays(warningNoticeVO);
            boolean skipWarningNotice = isStoreCustomTemplateWarningDisabled(entity, templateMap);
            int nearExpireDays = resolveNearExpireDays(entity, warningConfigMap, templateNearExpireDaysMap);
            int targetStatus = calculateStatus(entity, nearExpireDays,templateMap);

            if ((targetStatus == STATUS_NEAR_EXPIRE || targetStatus == STATUS_EXPIRED || targetStatus == STATUS_NORMAL)
                    && !Objects.equals(entity.getStatus(), targetStatus)) {
                entity.setStatus(targetStatus);
                needUpdateList.add(entity);
            }

            int effectiveStatus = (targetStatus == STATUS_NEAR_EXPIRE || targetStatus == STATUS_EXPIRED)
                    ? targetStatus : (entity.getStatus() == null ? STATUS_MISSING : entity.getStatus());
            if (effectiveStatus != STATUS_NEAR_EXPIRE || skipWarningNotice || skipByExpiryReminderDays) {
                continue;
            }
            Integer daysToExpire = calculateDaysToExpire(entity.getExpireDate());
            if (daysToExpire == null || daysToExpire < 0) {
                continue;
            }
            int noticeFrequencyDays = resolveNoticeFrequencyDays(warningNoticeVO);
            if (!shouldSendByFrequency(daysToExpire, noticeFrequencyDays)) {
                continue;
            }
            String templateName = null;
            if(isStoreCustomCertificate(entity)){
                StoreCertificatePhotoEntity storeCertificatePhotoEntity = templateMap.get(entity.getTemplateId());
                if(Objects.nonNull(storeCertificatePhotoEntity)){
                    templateName = storeCertificatePhotoEntity.getCertificateName();
                }
            }
            noticeTaskList.add(new NearExpireNoticeTask(entity, daysToExpire, warningNoticeVO,templateName));
        }

        updateCertificateStatusBatch(needUpdateList);
        sendNearExpireNoticeBatch(noticeTaskList, tenantId);
    }

    private WarningNoticeVO buildWarningNoticeVO(CertificateInstanceEntity entity, Map<String, WarningNoticeVO> warningConfigMap) {
        String certificateType = entity.getCertificateType();
        if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType())) {
            return warningConfigMap.get(entity.getTemplateId());
        }
        return warningConfigMap.get(certificateType);
    }

    /**
     * 查询待检查的证照数据（仅正常、临期）。
     */
    private List<CertificateInstanceEntity> queryCandidateCertificateList() {
        LambdaQueryWrapper<CertificateInstanceEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(CertificateInstanceEntity::getEnabledMark, 0);
        queryWrapper.eq(CertificateInstanceEntity::getTemplateStatus,1);
        queryWrapper.in(CertificateInstanceEntity::getStatus, Arrays.asList(STATUS_NORMAL, STATUS_NEAR_EXPIRE));
        queryWrapper.in(CertificateInstanceEntity::getCertificateType, Arrays.asList(
                CertificateTypeEnum.HEALTH_CERTIFICATE.getType(),
                CertificateTypeEnum.BUSINESS_LICENSE.getType(),
                CertificateTypeEnum.HYGIENE_LICENSE.getType(),
                CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType()
        ));
        queryWrapper.orderByAsc(CertificateInstanceEntity::getCertificateType);
        queryWrapper.orderByAsc(CertificateInstanceEntity::getSubjectType);
        queryWrapper.orderByAsc(CertificateInstanceEntity::getSubjectId);
        return certificateInstanceMapper.selectList(queryWrapper);
    }

    /**
     * 查询预警设置（健康证、营业执照、食品经营许可证、门店自定义证照）。
     */
    private Map<String, WarningNoticeVO> queryWarningConfigMap() {
        Map<String, WarningNoticeVO> result = new HashMap<>(4);
        List<WarningNoticeVO> warningNoticeVOList = warningNoticeService.queryAll();
        for (WarningNoticeVO warningNoticeVO:warningNoticeVOList){
            result.put(warningNoticeVO.getTypeOrTemplateId(), warningNoticeVO);
        }
        return result;
    }

    /**
     * 查询门店自定义证照模板临期天数配置。
     */
    private Map<String, StoreCertificatePhotoEntity> queryTemplateMap(List<CertificateInstanceEntity> instanceList) {
        List<String> templateIds = instanceList.stream()
                .filter(Objects::nonNull)
                .filter(entity -> StrUtil.equalsIgnoreCase(entity.getCertificateType(), CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType()))
                .map(CertificateInstanceEntity::getTemplateId)
                .filter(StrUtil::isNotBlank)
                .map(StrUtil::trim)
                .distinct()
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(templateIds)) {
            return Collections.emptyMap();
        }

        LambdaQueryWrapper<StoreCertificatePhotoEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.in(StoreCertificatePhotoEntity::getId, templateIds);
        queryWrapper.eq(StoreCertificatePhotoEntity::getEnabledMark, 0);
        List<StoreCertificatePhotoEntity> templateList = storeCertificatePhotoMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(templateList)) {
            return Collections.emptyMap();
        }

        Map<String, StoreCertificatePhotoEntity> result = new HashMap<>(templateList.size());
        for (StoreCertificatePhotoEntity template : templateList) {
            if (template == null || StrUtil.isBlank(template.getId())) {
                continue;
            }
            result.put(StrUtil.trim(template.getId()), template);
        }
        return result;
    }

    /**
     * 构建门店自定义证照模板临期天数映射。
     */
    private Map<String, Integer> buildTemplateNearExpireDaysMap(Map<String, StoreCertificatePhotoEntity> templateMap) {
        if (CollUtil.isEmpty(templateMap)) {
            return Collections.emptyMap();
        }
        Map<String, Integer> result = new HashMap<>(templateMap.size());
        for (Map.Entry<String, StoreCertificatePhotoEntity> entry : templateMap.entrySet()) {
            String templateId = StrUtil.trim(entry.getKey());
            if (StrUtil.isBlank(templateId)) {
                continue;
            }
            StoreCertificatePhotoEntity template = entry.getValue();
            Integer reminderDays = template == null ? null : template.getExpiryReminderDays();
            result.put(templateId, reminderDays == null || reminderDays < 0 ? DEFAULT_NEAR_EXPIRE_DAYS : reminderDays);
        }
        return result;
    }

    /**
     * 按证照类型解析临期阈值天数。
     */
    private int resolveNearExpireDays(CertificateInstanceEntity entity,
                                      Map<String, WarningNoticeVO> warningConfigMap,
                                      Map<String, Integer> templateNearExpireDaysMap) {
        if (entity == null || StrUtil.isBlank(entity.getCertificateType())) {
            return DEFAULT_NEAR_EXPIRE_DAYS;
        }
        String certificateType = StrUtil.trim(entity.getCertificateType());
        if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType())) {
            String templateId = StrUtil.trim(entity.getTemplateId());
            Integer templateDays = templateNearExpireDaysMap.get(templateId);
            return templateDays == null || templateDays < 0 ? DEFAULT_NEAR_EXPIRE_DAYS : templateDays;
        }
        WarningNoticeVO warningNoticeVO = warningConfigMap.get(certificateType);
        Integer reminderDays = warningNoticeVO == null ? null : warningNoticeVO.getExpiryReminderDays();
        return reminderDays == null || reminderDays < 0 ? DEFAULT_NEAR_EXPIRE_DAYS : reminderDays;
    }

    /**
     * 若是门店自定义证照，且模板状态为0，则不发送预警通知。
     */
    private boolean isStoreCustomTemplateWarningDisabled(CertificateInstanceEntity entity,
                                                         Map<String, StoreCertificatePhotoEntity> templateMap) {
        if (entity == null || !StrUtil.equalsIgnoreCase(entity.getCertificateType(), CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType())) {
            return false;
        }
        String templateId = StrUtil.trim(entity.getTemplateId());
        if (StrUtil.isBlank(templateId)) {
            return false;
        }
        StoreCertificatePhotoEntity template = templateMap.get(templateId);
        return template != null && Integer.valueOf(STORE_CUSTOM_TEMPLATE_DISABLED_STATUS).equals(template.getStatus());
    }

    /**
     * 按证照类型解析通知频率天数。
     */
    private int resolveNoticeFrequencyDays(WarningNoticeVO warningNoticeVO) {
        Integer frequencyDays = warningNoticeVO == null ? null : warningNoticeVO.getNoticeFrequencyDays();
        return frequencyDays == null || frequencyDays <= 0 ? DEFAULT_NOTICE_FREQUENCY_DAYS : frequencyDays;
    }

    /**
     * 当预警配置的临期提醒天数小于等于0时，跳过预警通知发送。
     */
    private boolean shouldSkipWarningNoticeByExpiryReminderDays(WarningNoticeVO warningNoticeVO) {
        Integer expiryReminderDays = warningNoticeVO == null ? null : warningNoticeVO.getExpiryReminderDays();
        return expiryReminderDays == null || expiryReminderDays <= 0;
    }

    /**
     * 批量更新证照状态。
     */
    private void updateCertificateStatusBatch(List<CertificateInstanceEntity> needUpdateList) {
        if (CollUtil.isEmpty(needUpdateList)) {
            return;
        }
        for (CertificateInstanceEntity entity : needUpdateList) {
            if (entity == null || StrUtil.isBlank(entity.getId()) || entity.getStatus() == null) {
                continue;
            }
            CertificateInstanceEntity updateEntity = new CertificateInstanceEntity();
            updateEntity.setId(entity.getId());
            updateEntity.setStatus(entity.getStatus());
            certificateInstanceMapper.updateById(updateEntity);
        }
    }

    /**
     * 批量发送临期通知。
     */
    private void sendNearExpireNoticeBatch(List<NearExpireNoticeTask> noticeTaskList, String tenantId) {
        if (CollUtil.isEmpty(noticeTaskList) || StrUtil.isBlank(tenantId)) {
            return;
        }

        Pair<Map<String, OrganizeBaseInfoVO>,Map<String,UserBaseInfoVO>> organizeMapAndUserOrgMap = queryOrganizeInfoMap(noticeTaskList,tenantId);
        Map<String, OrganizeBaseInfoVO> organizeMap = organizeMapAndUserOrgMap.getLeft();
        Map<String,UserBaseInfoVO> userBaseInfoByUserId = organizeMapAndUserOrgMap.getRight();
        for (NearExpireNoticeTask task : noticeTaskList) {
            if (task == null || task.getEntity() == null) {
                continue;
            }
            CertificateInstanceEntity entity = task.getEntity();
            OrganizeBaseInfoVO organizeBaseInfoVO = organizeMap.get(entity.getSubjectId());
            //组织被禁用，则不推送
            if(!isHealthEmployeeCertificate(entity) && Objects.nonNull(organizeBaseInfoVO) && organizeBaseInfoVO.isDisabled()){
                continue;
            }

            //如果不是健康证
            if(!isHealthEmployeeCertificate(entity)){
                List<String> receiverUserIds = resolveReceiverUserIds(entity, organizeMap, task.getWarningNoticeVO(), tenantId);
                if(CollUtil.isEmpty(receiverUserIds)){
                    continue;
                }
                NoticeMessage noticeMessage = buildNearExpireMessage(entity, task, organizeMap,false,null);
                noticeHelper.sendMessage(receiverUserIds, tenantId, noticeMessage.getTitle(), noticeMessage.getContent(), null, null, null);
                continue;
            }

            //如果是健康证的通知
            //获取健康证本人
            List<String> receiverUserIds = resolveReceiverUserIds(entity, organizeMap, task.getWarningNoticeVO(), tenantId);
            if (CollUtil.isNotEmpty(receiverUserIds)){
                NoticeMessage noticeMessage = buildNearExpireMessage(entity, task, organizeMap,true,null);
                noticeHelper.sendMessage(receiverUserIds, tenantId, noticeMessage.getTitle(), noticeMessage.getContent(), BUTTON_NAME_HANDLER, buildJumpHealthCertificateUrl(entity), MP_ID_CERTIFICATE);
            }
            //获取健康证所属组织的负责人
            OrganizeBaseInfoVO healthEmployeeOrgLeaderReceiver = resolveHealthEmployeeOrgLeaderReceiver(entity, organizeMap, userBaseInfoByUserId);
            List<String> otherReceiverUserIds = new ArrayList<>();
            if(Objects.nonNull(healthEmployeeOrgLeaderReceiver)){
                otherReceiverUserIds.add(healthEmployeeOrgLeaderReceiver.getLeaderId());
            }
            //获取配置的通知者
            List<String> configUserIds = resolveWarningConfigUserIds(entity, task.getWarningNoticeVO(), tenantId);
            if(CollUtil.isNotEmpty(configUserIds)){
                otherReceiverUserIds.addAll(configUserIds);
            }

            if (CollUtil.isEmpty(otherReceiverUserIds)) {
                continue;
            }

            //健康证风险，发给该健康证的组织负责人以及配置的人员
            NoticeMessage healthEmployLeaderNoticeMessage = buildNearExpireMessage(entity, task, organizeMap,false,userBaseInfoByUserId);
            if(Objects.isNull(healthEmployLeaderNoticeMessage)){
                continue;
            }
            noticeHelper.sendMessage(otherReceiverUserIds, tenantId, healthEmployLeaderNoticeMessage.getTitle(), healthEmployLeaderNoticeMessage.getContent(), BUTTON_NAME_HANDLER, buildJumpHealthCertificateUrl(entity), MP_ID_CERTIFICATE);
        }
    }

    private String buildJumpHealthCertificateUrl(CertificateInstanceEntity entity) {
        if(Objects.isNull(entity)){
            return null;
        }
        String subjectId = entity.getSubjectId();
        if(StringUtil.isBlank(subjectId)){
            return null;
        }
        return String.format(URL_CERTIFICATE,entity.getId());
    }

    /**
     * 左侧 查询组织信息映射（用于拿负责人和组织名称）。 key为组织id，value为基础组织信息。
     * 右侧 如果是健康证还需要查询员工所属组织信息。key为userId，value为该userId的基础信息
     * 包含非员工主体的组织信息，以及健康证员工所属组织的信息。
     */
    private Pair<Map<String, OrganizeBaseInfoVO>,Map<String,UserBaseInfoVO>> queryOrganizeInfoMap(List<NearExpireNoticeTask> noticeTaskList,String tenantId) {
        Set<String> organizeIdSet = new LinkedHashSet<>();
        // 收集健康证员工的 subjectId，用于查询其所属组织
        Set<String> healthEmployeeUserIds = new LinkedHashSet<>();
        for (NearExpireNoticeTask task : noticeTaskList) {
            if (task == null || task.getEntity() == null) {
                continue;
            }
            CertificateInstanceEntity entity = task.getEntity();
            if (Integer.valueOf(SUBJECT_TYPE_EMPLOYEE).equals(entity.getSubjectType())) {
                if (isHealthEmployeeCertificate(entity) && StrUtil.isNotBlank(entity.getSubjectId())) {
                    healthEmployeeUserIds.add(StrUtil.trim(entity.getSubjectId()));
                }
                continue;
            }
            String organizeId = StrUtil.trim(entity.getSubjectId());
            if (StrUtil.isNotBlank(organizeId)) {
                organizeIdSet.add(organizeId);
            }
        }

        Map<String, UserBaseInfoVO> userBaseInfoByUserId = Collections.emptyMap();
        // 查询健康证员工所属的组织ID（批量SQL查询）
        if (CollUtil.isNotEmpty(healthEmployeeUserIds)) {
            userBaseInfoByUserId = organizationHelper.buildUserBaseInfoByUserIds(healthEmployeeUserIds,tenantId);
            organizeIdSet.addAll(userBaseInfoByUserId.values()
                    .stream()
                            .map(UserBaseInfoVO::getOrganizeId)
                    .collect(Collectors.toSet()));
        }

        if (CollUtil.isEmpty(organizeIdSet)) {
            return Pair.of(Collections.emptyMap(),userBaseInfoByUserId);
        }

        return Pair.of(organizationHelper.buildBaseOrganizeVO(organizeIdSet,tenantId),userBaseInfoByUserId);
    }

    /**
     * 解析接收人：
     * 1. 健康证通知当前人员
     * 2. 组织/门店证照通知组织负责人。
     */
    private List<String> resolveReceiverUserIds(CertificateInstanceEntity entity,
                                                Map<String, OrganizeBaseInfoVO> organizeMap,
                                                WarningNoticeVO warningNoticeVO,
                                                String tenantId) {
        if (entity == null) {
            return Collections.emptyList();
        }
        String subjectId = StrUtil.trim(entity.getSubjectId());
        if (StrUtil.isBlank(subjectId)) {
            return Collections.emptyList();
        }

        List<String> receiverUserIds = new ArrayList<>();
        if (isHealthEmployeeCertificate(entity)) {
            receiverUserIds.add(subjectId);
        } else {
            addOrganizationLeaderReceiver(entity, organizeMap, receiverUserIds);
        }

        if (warningNoticeVO != null && !isHealthEmployeeCertificate(entity)) {
            List<String> configUserIds = resolveWarningConfigUserIds(entity, warningNoticeVO, tenantId);
            if (CollUtil.isNotEmpty(configUserIds)) {
                receiverUserIds.addAll(configUserIds);
            }
        }
        return normalizeIdList(receiverUserIds);
    }

    /**
     * 非健康证默认通知组织负责人。
     */
    private void addOrganizationLeaderReceiver(CertificateInstanceEntity entity,
                                               Map<String, OrganizeBaseInfoVO> organizeMap,
                                               List<String> receiverUserIds) {
        if (entity == null || CollUtil.isEmpty(organizeMap) || receiverUserIds == null) {
            return;
        }
        String subjectId = StrUtil.trim(entity.getSubjectId());
        if (StrUtil.isBlank(subjectId)) {
            return;
        }
        OrganizeBaseInfoVO organize = organizeMap.get(subjectId);
        if (organize == null || StrUtil.isBlank(organize.getLeaderId())) {
            return;
        }
        receiverUserIds.add(StrUtil.trim(organize.getLeaderId()));
    }

    /**
     * 健康证通知该员工所属组织的负责人。
     */
    private OrganizeBaseInfoVO resolveHealthEmployeeOrgLeaderReceiver(CertificateInstanceEntity entity,
                                                     Map<String, OrganizeBaseInfoVO> organizeMap,
                                                    Map<String,UserBaseInfoVO> userBaseInfoByUserId) {
        if (Objects.isNull(entity) || CollUtil.isEmpty(organizeMap)) {
            return null;
        }
        if(!isHealthEmployeeCertificate(entity)){
            return null;
        }
        String subjectId = StrUtil.trim(entity.getSubjectId());
        UserBaseInfoVO userBaseInfoVO = userBaseInfoByUserId.get(subjectId);
        if(Objects.isNull(userBaseInfoVO)){
            log.error("resolveHealthEmployeeOrgLeaderReceiver 用户的基础为空!.subjectId:{}",subjectId);
            return null;
        }
        String orgId = userBaseInfoVO.getOrganizeId();
        if(StrUtil.isBlank(orgId)){
            log.error("resolveHealthEmployeeOrgLeaderReceiver 用户的组织id为空!.subjectId:{}",subjectId);
            return null;
        }
        OrganizeBaseInfoVO organize = organizeMap.get(orgId);
        if (organize != null && StrUtil.isNotBlank(organize.getLeaderId())) {
            return organize;
        }
        return null;
    }

    /**
     * 解析预警配置接收人。
     */
    private List<String> resolveWarningConfigUserIds(CertificateInstanceEntity entity,
                                                     WarningNoticeVO warningNoticeVO,
                                                     String tenantId) {
        if (entity == null || warningNoticeVO == null || StrUtil.isBlank(tenantId)) {
            return Collections.emptyList();
        }
        List<WarningNoticeUserConfigVO> noticeConfigList = warningNoticeVO.getNoticeConfigList();
        if (CollUtil.isEmpty(noticeConfigList)) {
            return Collections.emptyList();
        }

        boolean healthCertificate = isHealthEmployeeCertificate(entity);
        List<String> receiverUserIds = new ArrayList<>();
        for (WarningNoticeUserConfigVO config : noticeConfigList) {
            if (config == null || StrUtil.isBlank(config.getNoticeUserType()) || CollUtil.isEmpty(config.getNoticeUserList())) {
                continue;
            }
            String noticeUserType = StrUtil.trim(config.getNoticeUserType());
            if (StrUtil.equalsAnyIgnoreCase(noticeUserType, NOTICE_USER_TYPE_POSITION)) {
                receiverUserIds.addAll(resolvePositionUserIds(config.getNoticeUserList(), entity, tenantId));
                continue;
            }
            if (StrUtil.equalsAnyIgnoreCase(noticeUserType, NOTICE_USER_TYPE_PERSONNEL, NOTICE_USER_TYPE_PERSON)) {
                if (healthCertificate) {
                    receiverUserIds.addAll(resolveHealthPersonnelConfigUserIds(config.getNoticeUserList(), entity, tenantId));
                } else {
                    receiverUserIds.addAll(extractTargetIds(config.getNoticeUserList()));
                }
            }
        }
        return normalizeIdList(receiverUserIds);
    }

    /**
     * 按岗位解析通知人。健康证场景下需按数据权限过滤。
     */
    private List<String> resolvePositionUserIds(List<WarningNoticeTargetVO> noticeUserList,
                                                CertificateInstanceEntity entity,
                                                String tenantId) {
        List<String> positionIds = extractTargetIds(noticeUserList);
        if (CollUtil.isEmpty(positionIds) || StrUtil.isBlank(tenantId)) {
            return Collections.emptyList();
        }

        QueryUserBatchDTO dto = new QueryUserBatchDTO();
        dto.setPositionIds(positionIds);
        dto.setTenantId(tenantId);
        ActionResult<List<UserBoundVO>> userInfoBatch = v2UserApi.getUserInfoBatch(dto);
        if (userInfoBatch == null || !Integer.valueOf(200).equals(userInfoBatch.getCode()) || CollUtil.isEmpty(userInfoBatch.getData())) {
            return Collections.emptyList();
        }
        List<String> positionUserIds = normalizeIdList(userInfoBatch.getData().stream().map(UserBoundVO::getId).collect(Collectors.toList()));

        // 健康证场景：按数据权限过滤岗位人员
        if (isHealthEmployeeCertificate(entity) && CollUtil.isNotEmpty(positionUserIds)) {
            return filterByDataPermission(positionUserIds, entity, tenantId);
        }
        return positionUserIds;
    }

    /**
     * 按数据权限过滤通知人员（健康证场景）。
     */
    private List<String> filterByDataPermission(List<String> candidateUserIds,
                                                 CertificateInstanceEntity entity,
                                                 String tenantId) {
        String subjectId = entity == null ? null : StrUtil.trim(entity.getSubjectId());
        if (StrUtil.isBlank(subjectId) || StrUtil.isBlank(tenantId) || CollUtil.isEmpty(candidateUserIds)) {
            return Collections.emptyList();
        }

        List<UserEntity> userEntityList = userApi.getUserListNoData(candidateUserIds, tenantId);
        if (CollUtil.isEmpty(userEntityList)) {
            return Collections.emptyList();
        }

        List<String> result = new ArrayList<>();
        for (UserEntity userEntity : userEntityList) {
            if (userEntity == null || StrUtil.isBlank(userEntity.getId())) {
                continue;
            }
            String userId = StrUtil.trim(userEntity.getId());
            if (Integer.valueOf(USER_ADMINISTRATOR_FLAG).equals(userEntity.getIsAdministrator())) {
                result.add(userId);
                continue;
            }
            List<String> dataPermissionUserIds;
            try {
                dataPermissionUserIds = permissionsUtils.obtainPersonnelUserIdDataPermissions(userId, HEALTH_CERTIFICATE_PERMISSION_MODULE_ID);
            } catch (Exception ex) {
                log.warn("query health certificate position user permission failed,userId={},module={}", userId, HEALTH_CERTIFICATE_PERMISSION_MODULE_ID, ex);
                continue;
            }
            if (dataPermissionUserIds == null) {
                result.add(userId);
                continue;
            }
            if (CollUtil.isNotEmpty(dataPermissionUserIds) && dataPermissionUserIds.contains(subjectId)) {
                result.add(userId);
            }
        }
        return normalizeIdList(result);
    }

    /**
     * 健康证-按人员配置时，按数据权限过滤可通知人员。
     */
    private List<String> resolveHealthPersonnelConfigUserIds(List<WarningNoticeTargetVO> noticeUserList,
                                                             CertificateInstanceEntity entity,
                                                             String tenantId) {
        String subjectId = entity == null ? null : StrUtil.trim(entity.getSubjectId());
        if (StrUtil.isBlank(subjectId) || StrUtil.isBlank(tenantId)) {
            return Collections.emptyList();
        }
        List<String> targetUserIds = extractTargetIds(noticeUserList);
        if (CollUtil.isEmpty(targetUserIds)) {
            return Collections.emptyList();
        }

        List<UserEntity> userEntityList = userApi.getUserListNoData(targetUserIds, tenantId);
        if (CollUtil.isEmpty(userEntityList)) {
            return Collections.emptyList();
        }

        List<String> result = new ArrayList<>();
        for (UserEntity userEntity : userEntityList) {
            if (userEntity == null || StrUtil.isBlank(userEntity.getId())) {
                continue;
            }
            String userId = StrUtil.trim(userEntity.getId());
            if (Integer.valueOf(USER_ADMINISTRATOR_FLAG).equals(userEntity.getIsAdministrator())) {
                result.add(userId);
                continue;
            }
            List<String> dataPermissionUserIds;
            try {
                dataPermissionUserIds = permissionsUtils.obtainPersonnelUserIdDataPermissions(userId, HEALTH_CERTIFICATE_PERMISSION_MODULE_ID);
            } catch (Exception ex) {
                log.warn("query health certificate user permission failed,userId={},module={}", userId, HEALTH_CERTIFICATE_PERMISSION_MODULE_ID, ex);
                continue;
            }
            if (dataPermissionUserIds == null || CollUtil.isEmpty(dataPermissionUserIds)) {
                if (dataPermissionUserIds == null) {
                    result.add(userId);
                }
                continue;
            }
            if (dataPermissionUserIds.contains(subjectId)) {
                result.add(userId);
            }
        }
        return normalizeIdList(result);
    }

    /**
     * 提取配置对象ID。
     */
    private List<String> extractTargetIds(List<WarningNoticeTargetVO> noticeUserList) {
        if (CollUtil.isEmpty(noticeUserList)) {
            return Collections.emptyList();
        }
        return normalizeIdList(noticeUserList.stream().map(WarningNoticeTargetVO::getId).collect(Collectors.toList()));
    }

    /**
     * 标准化ID列表。
     */
    private List<String> normalizeIdList(Collection<String> idList) {
        if (CollUtil.isEmpty(idList)) {
            return Collections.emptyList();
        }
        return idList.stream()
                .filter(StrUtil::isNotBlank)
                .map(StrUtil::trim)
                .distinct()
                .collect(Collectors.toList());
    }

    /**
     * 判断是否健康证+人员主体。
     */
    private boolean isHealthEmployeeCertificate(CertificateInstanceEntity entity) {
        return entity != null
                && StrUtil.equalsIgnoreCase(entity.getCertificateType(), CertificateTypeEnum.HEALTH_CERTIFICATE.getType())
                && Integer.valueOf(SUBJECT_TYPE_EMPLOYEE).equals(entity.getSubjectType());
    }

    /**
     * 判断是否自定义证照
     */
    private boolean isStoreCustomCertificate(CertificateInstanceEntity entity) {
        return entity != null
                && StrUtil.equalsIgnoreCase(entity.getCertificateType(), CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType());
    }

    /**
     * 计算状态。
     */
    private int calculateStatus(CertificateInstanceEntity entity, Integer nearExpireDays,Map<String, StoreCertificatePhotoEntity> templateMap) {
        Integer isLongTerm = entity.getIsLongTerm();
        Date expireDate = entity.getExpireDate();
        String certificateType = entity.getCertificateType();
        String templateId = entity.getTemplateId();
        StoreCertificatePhotoEntity storeCertificatePhotoEntity = templateMap.get(templateId);
        if(CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType().equals(certificateType) &&//自定义证照，如果临期提醒设置为0，则计算为正常
                Objects.nonNull(storeCertificatePhotoEntity) &&
                Integer.valueOf(0).equals(nearExpireDays)){
            return STATUS_NORMAL;
        }
        if (Integer.valueOf(1).equals(isLongTerm)) {
            return STATUS_NORMAL;
        }
        if (expireDate == null) {
            return STATUS_MISSING;
        }
        Date today = DateUtil.beginOfDay(DateUtil.date());
        Date target = DateUtil.beginOfDay(expireDate);
        if (target.before(today)) {
            return STATUS_EXPIRED;
        }
        int threshold = nearExpireDays == null || nearExpireDays < 0 ? 0 : nearExpireDays;
        long daysDiff = DateUtil.betweenDay(today, target, false);
        return daysDiff <= threshold ? STATUS_NEAR_EXPIRE : STATUS_NORMAL;
    }

    /**
     * 计算距离到期天数。
     */
    private Integer calculateDaysToExpire(Date expireDate) {
        if (expireDate == null) {
            return null;
        }
        Date today = DateUtil.beginOfDay(DateUtil.date());
        Date target = DateUtil.beginOfDay(expireDate);
        return (int) DateUtil.betweenDay(today, target, false);
    }

    /**
     * 按通知频率判断是否发送。
     */
    private boolean shouldSendByFrequency(Integer daysToExpire, Integer noticeFrequencyDays) {
        if (daysToExpire == null || daysToExpire < 0) {
            return false;
        }
        int frequency = noticeFrequencyDays == null || noticeFrequencyDays <= 0 ? DEFAULT_NOTICE_FREQUENCY_DAYS : noticeFrequencyDays;
        return frequency <= 1 || daysToExpire % frequency == 0;
    }

    /**
     * 构建临期通知文案。
     */
    private NoticeMessage buildNearExpireMessage(CertificateInstanceEntity entity,
                                                 NearExpireNoticeTask task,
                                                 Map<String, OrganizeBaseInfoVO> organizeMap,
                                                 boolean healthCertificateReceiverBySelf,
                                                 Map<String,UserBaseInfoVO> userBaseInfoByUserId) {
        Integer daysToExpire = task.getDaysToExpire();
        String certificateName = resolveCertificateName(entity == null ? null : entity.getCertificateType(),task.getCertificateTemplateName());
        int remainDays = daysToExpire == null ? 0 : Math.max(daysToExpire, 0);

        if(isHealthEmployeeCertificate(entity)){
            return buildHealthCertificateNearExpireMessage(entity,healthCertificateReceiverBySelf,userBaseInfoByUserId,remainDays);
        }

        String subjectName = "-";
        if (entity != null && StrUtil.isNotBlank(entity.getSubjectId())) {
            OrganizeBaseInfoVO organize = organizeMap.get(StrUtil.trim(entity.getSubjectId()));
            if (organize != null && StrUtil.isNotBlank(organize.getName())) {
                subjectName = StrUtil.trim(organize.getName());
            }
        }
        String title = subjectName + certificateName + "即将到期，请及时处理";
        String content;
        if(remainDays <= 0){
            content = String.format("%s%s今天到期，请及时更新。", subjectName, certificateName);
        }else{
            content = String.format("%s%s离到期时间还有%s天，请及时更新。", subjectName, certificateName, remainDays);
        }
        return new NoticeMessage(title, content);
    }

    private NoticeMessage buildHealthCertificateNearExpireMessage(CertificateInstanceEntity entity,
                                                                  boolean receiverBySelf,
                                                                  Map<String,UserBaseInfoVO> userBaseInfoByUserId,
                                                                  int remainDays) {
        if(receiverBySelf){
            return new NoticeMessage("您的健康证存在风险，请及时处理", buildHealthCertificateNearExpireContent(remainDays));
        }
        UserBaseInfoVO baseInfoVO = userBaseInfoByUserId.get(entity.getSubjectId());
        if(Objects.isNull(baseInfoVO)){
            log.error("buildNearExpireMessage 通知健康证所属负责人，该健康证的用户名称获取为空。entity:{}",entity);
            return null;
        }
        return new NoticeMessage("您管理组织的健康证存在风险，请及时处理", buildNearExpireContentByNoSelf(baseInfoVO.getUserName(),remainDays));
    }

    private String buildHealthCertificateNearExpireContent(int remainDays) {
        if(remainDays <= 0){
            return "您的健康证今天过期，请及时更新健康证。";
        }
        return String.format("您的健康证离到期时间还有%s天，请及时更新健康证。", remainDays);
    }

    private String buildNearExpireContentByNoSelf(String username, int remainDays) {
        if(remainDays <= 0){
            return String.format("%s的健康证今天过期，请及时更新健康证。",username);
        }
        return String.format("%s的健康证离到期时间还有%s天，请及时更新健康证。",username,remainDays);
    }

    /**
     * 证照类型转中文名称。
     */
    private String resolveCertificateName(String certificateType,String certificateTemplateName) {
        if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HEALTH_CERTIFICATE.getType())) {
            return "健康证";
        }
        if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.BUSINESS_LICENSE.getType())) {
            return "营业执照";
        }
        if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.HYGIENE_LICENSE.getType())) {
            return "食品经营许可证";
        }
        if (StrUtil.equalsIgnoreCase(certificateType, CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType())) {
            return certificateTemplateName;
        }
        return "证照";
    }

    /**
     * 临期通知任务。
     */
    @Data
    private static class NearExpireNoticeTask {
        private final CertificateInstanceEntity entity;
        private final Integer daysToExpire;
        private final WarningNoticeVO warningNoticeVO;
        private final String certificateTemplateName;

        private NearExpireNoticeTask(CertificateInstanceEntity entity, Integer daysToExpire, WarningNoticeVO warningNoticeVO,String certificateTemplateName) {
            this.entity = entity;
            this.daysToExpire = daysToExpire;
            this.warningNoticeVO = warningNoticeVO;
            this.certificateTemplateName = certificateTemplateName;
        }

        private CertificateInstanceEntity getEntity() {
            return entity;
        }

        private Integer getDaysToExpire() {
            return daysToExpire;
        }

        private WarningNoticeVO getWarningNoticeVO() {
            return warningNoticeVO;
        }
    }

    /**
     * 通知文案。
     */
    private static class NoticeMessage {
        private final String title;
        private final String content;

        private NoticeMessage(String title, String content) {
            this.title = title;
            this.content = content;
        }

        private String getTitle() {
            return title;
        }

        private String getContent() {
            return content;
        }
    }
}
