package com.hotwj.platform.noticeManagerment.systemNotification.task;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hotwj.platform.config.companyDeptConfig.domain.HotCompanyDeptConfig;
import com.hotwj.platform.config.companyDeptConfig.mapper.HotCompanyDeptConfigMapper;
import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.bo.HotVehicleSecondaryMaintenanceConfigBo;
import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.vo.HotVehicleSecondaryMaintenanceConfigVo;
import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.service.IHotVehicleSecondaryMaintenanceConfigService;
import com.hotwj.platform.driverManagement.driver.domain.HotDriver;
import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper;
import com.hotwj.platform.integration.sms.SmsBaoClient;
import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo;
import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService;
import com.hotwj.platform.resourceManagement.company.domain.SysCompany;
import com.hotwj.platform.resourceManagement.company.mapper.SysCompanyMapper;
import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.HotCompanyChangeHistory;
import com.hotwj.platform.resourceManagement.companyChangeHistory.mapper.HotCompanyChangeHistoryMapper;
import com.hotwj.platform.resourceManagement.companyInsurance.domain.HotCompanyInsurance;
import com.hotwj.platform.resourceManagement.companyInsurance.mapper.HotCompanyInsuranceMapper;
import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager;
import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper;
import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo;
import com.hotwj.platform.resourceManagement.vehicleMaintenance.service.IHotVehicleMaintenanceService;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle;
import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 系统消息定时任务
 *
 * @author shihongwei
 * @date 2026-02-25
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SystemNotificationTask {

    private final SysCompanyMapper sysCompanyMapper;
    private final HotCompanySafetyManagerMapper managerMapper;
    private final IHotSystemNotificationService notificationService;
    private final HotCompanyChangeHistoryMapper changeHistoryMapper;
    private final HotCompanyDeptConfigMapper deptConfigMapper;
    private final HotCompanyInsuranceMapper insuranceMapper;
    private final HotVehicleMapper vehicleMapper;
    private final IHotVehicleSecondaryMaintenanceConfigService secondaryMaintenanceConfigService;
    private final IHotVehicleMaintenanceService vehicleMaintenanceService;
    private final HotDriverMapper hotDriverMapper;
    private final SmsBaoClient smsBaoClient;

    /**
     * 道路运输许可证到期
     */
    @Scheduled(cron = "0 0 9 * * ?")
    public void notifyUpcomingExpiry() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            List<SysCompany> companies = sysCompanyMapper.selectList(
                    Wrappers.<SysCompany>lambdaQuery()
                            .eq(SysCompany::getStatus, 1L)
                            .eq(SysCompany::getIsDeleted, 0L)
                            .eq(SysCompany::getRoadExpireDate, target)
            );
            for (SysCompany c : companies) {
                List<HotCompanySafetyManager> managers = managerMapper.selectList(
                        Wrappers.<HotCompanySafetyManager>lambdaQuery()
                                .eq(HotCompanySafetyManager::getCompanyId, c.getId())
                                .eq(HotCompanySafetyManager::getIsDeleted, 0L)
                                .eq(HotCompanySafetyManager::getIsResigned, 0L)
                                .eq(HotCompanySafetyManager::getStatus, 1L)
                );
                if (managers == null || managers.isEmpty()) {
                    continue;
                }
                List<String> receiverIds = managers.stream()
                        .map(m -> String.valueOf(m.getId()))
                        .collect(Collectors.toList());

                HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                bos.setLevel("紧急");
                bos.setContent("企业《道路运输经营许可证》将于" + days + "天后到期（" + c.getRoadExpireDate() + "），请及时办理。");
                bos.setSourceType("企业管理");
                bos.setSenderType("SYSTEM");
                bos.setReceiverType("管理员");
                bos.setReceiverIds(receiverIds);
                bos.setIsDeleted(0L);
                try {
                    notificationService.insertByBo(bos);
                } catch (Exception e) {
                    log.warn("发送道路运输许可证到期提醒失败 companyId={} days={}", c.getId(), days, e);
                }
            }
        }
    }

    /**
     * 企业营业期到期
     */
    @Scheduled(cron = "0 0 9 * * ?")
    public void notifyBusinessTermExpiry() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            List<SysCompany> companies = sysCompanyMapper.selectList(
                    Wrappers.<SysCompany>lambdaQuery()
                            .eq(SysCompany::getStatus, 1L)
                            .eq(SysCompany::getIsDeleted, 0L)
                            .eq(SysCompany::getBusinessLongTerm, false)
                            .eq(SysCompany::getBusinessTerm, target)
            );
            for (SysCompany c : companies) {
                List<HotCompanySafetyManager> managers = managerMapper.selectList(
                        Wrappers.<HotCompanySafetyManager>lambdaQuery()
                                .eq(HotCompanySafetyManager::getCompanyId, c.getId())
                                .eq(HotCompanySafetyManager::getIsDeleted, 0L)
                                .eq(HotCompanySafetyManager::getIsResigned, 0L)
                                .eq(HotCompanySafetyManager::getStatus, 1L)
                );
                if (managers == null || managers.isEmpty()) {
                    continue;
                }
                List<String> receiverIds = managers.stream()
                        .map(m -> String.valueOf(m.getId()))
                        .collect(Collectors.toList());

                HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                bos.setLevel("紧急");
                bos.setContent("企业营业期限将于" + days + "天后到期（" + c.getBusinessTerm() + "），请及时办理相关手续。");
                bos.setSourceType("企业管理");
                bos.setSenderType("SYSTEM");
                bos.setReceiverType("管理员");
                bos.setReceiverIds(receiverIds);
                bos.setIsDeleted(0L);
                try {
                    notificationService.insertByBo(bos);
                } catch (Exception e) {
                    log.warn("发送企业营业期限到期提醒失败 companyId={} days={}", c.getId(), days, e);
                }
            }
        }
    }

    /**
     * 上岗资格证
     */
    @Scheduled(cron = "0 0 9 * * ?")
    public void notifyManagerQualificationExpiry() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            java.sql.Date targetDate = java.sql.Date.valueOf(target);
            List<HotCompanySafetyManager> expiringManagers = managerMapper.selectList(
                    Wrappers.<HotCompanySafetyManager>lambdaQuery()
                            .eq(HotCompanySafetyManager::getIsDeleted, 0L)
                            .eq(HotCompanySafetyManager::getIsResigned, 0L)
                            .eq(HotCompanySafetyManager::getStatus, 1L)
                            .eq(HotCompanySafetyManager::getCertificateExpireDate, targetDate)
            );
            if (expiringManagers == null || expiringManagers.isEmpty()) {
                continue;
            }
            var grouped = expiringManagers.stream().collect(Collectors.groupingBy(HotCompanySafetyManager::getCompanyId));
            for (Long companyId : grouped.keySet()) {
                List<HotCompanySafetyManager> managers = managerMapper.selectList(
                        Wrappers.<HotCompanySafetyManager>lambdaQuery()
                                .eq(HotCompanySafetyManager::getCompanyId, companyId)
                                .eq(HotCompanySafetyManager::getIsDeleted, 0L)
                                .eq(HotCompanySafetyManager::getIsResigned, 0L)
                                .eq(HotCompanySafetyManager::getStatus, 1L)
                );
                if (managers == null || managers.isEmpty()) {
                    continue;
                }
                List<String> receiverIds = managers.stream()
                        .map(m -> String.valueOf(m.getId()))
                        .collect(Collectors.toList());

                HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                bos.setLevel("紧急");
                bos.setContent("企业上岗资格证将于" + days + "天后到期（到期日：" + targetDate + "），请及时办理。");
                bos.setSourceType("企业管理");
                bos.setSenderType("SYSTEM");
                bos.setReceiverType("管理员");
                bos.setReceiverIds(receiverIds);
                bos.setIsDeleted(0L);
                try {
                    notificationService.insertByBo(bos);
                } catch (Exception e) {
                    log.warn("发送企业上岗资格证到期提醒失败 companyId={} days={}", companyId, days, e);
                }
            }
        }
    }

    /**
     * 企业管理人员变更职位、部门
     */
    @Scheduled(cron = "0 0/10 * * * ?")
    public void notifyManagerDeptOrJobChange() {
        LocalDateTime end = LocalDateTime.now();
        LocalDateTime start = end.minusMinutes(10);
        List<HotCompanyChangeHistory> changes = changeHistoryMapper.selectList(
                Wrappers.<HotCompanyChangeHistory>lambdaQuery()
                        .eq(HotCompanyChangeHistory::getIsDeleted, 0)
                        .in(HotCompanyChangeHistory::getChangeType, Arrays.asList("调岗位", "调部门", "调岗调部门"))
                        .ge(HotCompanyChangeHistory::getChangeTime, java.sql.Timestamp.valueOf(start))
                        .lt(HotCompanyChangeHistory::getChangeTime, java.sql.Timestamp.valueOf(end))
        );
        if (changes == null || changes.isEmpty()) {
            return;
        }
        List<Long> deptIds = changes.stream()
                .flatMap(h -> Arrays.stream(new Long[]{h.getBeforeDeptId(), h.getAfterDeptId()}))
                .filter(id -> id != null && id > 0)
                .distinct()
                .collect(Collectors.toList());
        List<HotCompanyDeptConfig> depts = deptIds.isEmpty() ? java.util.Collections.emptyList() : deptConfigMapper.selectBatchIds(deptIds);
        java.util.Map<Long, String> deptNameMap = depts.stream().collect(Collectors.toMap(HotCompanyDeptConfig::getId, HotCompanyDeptConfig::getDeptName));

        for (HotCompanyChangeHistory h : changes) {
            List<HotCompanySafetyManager> managers = managerMapper.selectList(
                    Wrappers.<HotCompanySafetyManager>lambdaQuery()
                            .eq(HotCompanySafetyManager::getCompanyId, h.getCompanyId())
                            .eq(HotCompanySafetyManager::getIsDeleted, 0L)
                            .eq(HotCompanySafetyManager::getIsResigned, 0L)
                            .eq(HotCompanySafetyManager::getStatus, 1L)
            );
            if (managers == null || managers.isEmpty()) {
                continue;
            }
            List<String> receiverIds = managers.stream()
                    .map(m -> String.valueOf(m.getId()))
                    .collect(Collectors.toList());

            String beforeDept = h.getBeforeDeptId() != null ? deptNameMap.getOrDefault(h.getBeforeDeptId(), String.valueOf(h.getBeforeDeptId())) : "未填写";
            String afterDept = h.getAfterDeptId() != null ? deptNameMap.getOrDefault(h.getAfterDeptId(), String.valueOf(h.getAfterDeptId())) : "未填写";
            String beforeJob = h.getBeforeJobName() != null ? h.getBeforeJobName() : "未填写";
            String afterJob = h.getAfterJobName() != null ? h.getAfterJobName() : "未填写";

            HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
            bos.setLevel("普通");
            bos.setContent("企业管理人员【" + h.getName() + "】" +
                    "发生" + h.getChangeType() +
                    "：岗位【" + beforeJob + "】→【" + afterJob + "】，" +
                    "部门【" + beforeDept + "】→【" + afterDept + "】。");
            bos.setSourceType("企业管理");
            bos.setSenderType("SYSTEM");
            bos.setReceiverType("管理员");
            bos.setReceiverIds(receiverIds);
            bos.setIsDeleted(0L);
            try {
                notificationService.insertByBo(bos);
            } catch (Exception e) {
                log.warn("发送企业人员变更提醒失败 companyId={} employeeId={}", h.getCompanyId(), h.getEmployeeId(), e);
            }
        }
    }


    /**
     * 车辆强制报废提醒
     */
    @Scheduled(cron = "0 0 10 * * ?")
    public void notifyVehicleMandatoryScrap() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            java.sql.Date targetDate = java.sql.Date.valueOf(target);
            List<HotVehicle> vehicles = vehicleMapper.selectList(
                    Wrappers.<HotVehicle>lambdaQuery()
                            .eq(HotVehicle::getIsDeleted, 0L)
                            .eq(HotVehicle::getMandatoryScrapEndDate, targetDate)
            );
            if (vehicles == null || vehicles.isEmpty()) {
                continue;
            }
            for (HotVehicle v : vehicles) {
                List<HotCompanySafetyManager> managers = managerMapper.selectList(
                        Wrappers.<HotCompanySafetyManager>lambdaQuery()
                                .eq(HotCompanySafetyManager::getCompanyId, v.getCompanyId())
                                .eq(HotCompanySafetyManager::getIsDeleted, 0L)
                                .eq(HotCompanySafetyManager::getIsResigned, 0L)
                                .eq(HotCompanySafetyManager::getStatus, 1L)
                );
                if (managers == null || managers.isEmpty()) {
                    continue;
                }
                List<String> receiverIds = managers.stream()
                        .map(m -> String.valueOf(m.getId()))
                        .collect(Collectors.toList());

                HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                bos.setLevel("紧急");
                bos.setContent("车辆【" + v.getPlateNumber() + "】将于" + days + "天后达到强制报废期（" + v.getMandatoryScrapEndDate() + "），请按规定及时办理相关处置。");
                bos.setSourceType("车辆管理");
                bos.setSenderType("SYSTEM");
                bos.setReceiverType("管理员");
                bos.setReceiverIds(receiverIds);
                bos.setIsDeleted(0L);
                try {
                    notificationService.insertByBo(bos);
                } catch (Exception e) {
                    log.warn("发送车辆强制报废期提醒失败 vehicleId={} companyId={} days={}", v.getId(), v.getCompanyId(), days, e);
                }
            }
        }
    }

    /**
     * 车辆年检到期提醒
     */
    @Scheduled(cron = "0 0 10 * * ?")
    public void notifyVehicleInspectionExpiry() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        List<HotVehicle> all = vehicleMapper.selectList(
                Wrappers.<HotVehicle>lambdaQuery()
                        .eq(HotVehicle::getIsDeleted, 0L)
                        .isNotNull(HotVehicle::getInspectionValidMonth)
        );
        if (all == null || all.isEmpty()) {
            return;
        }
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            for (HotVehicle v : all) {
                String ymStr = v.getInspectionValidMonth();
                if (ymStr == null || ymStr.isEmpty()) {
                    continue;
                }
                try {
                    YearMonth ym = YearMonth.parse(ymStr);
                    LocalDate lastDay = ym.atEndOfMonth();
                    if (!lastDay.equals(target)) {
                        continue;
                    }
                } catch (Exception ignore) {
                    continue;
                }
                List<HotCompanySafetyManager> managers = managerMapper.selectList(
                        Wrappers.<HotCompanySafetyManager>lambdaQuery()
                                .eq(HotCompanySafetyManager::getCompanyId, v.getCompanyId())
                                .eq(HotCompanySafetyManager::getIsDeleted, 0L)
                                .eq(HotCompanySafetyManager::getIsResigned, 0L)
                                .eq(HotCompanySafetyManager::getStatus, 1L)
                );
                if (managers == null || managers.isEmpty()) {
                    continue;
                }
                List<String> receiverIds = managers.stream()
                        .map(m -> String.valueOf(m.getId()))
                        .collect(Collectors.toList());

                HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                bos.setLevel("紧急");
                bos.setContent("车辆【" + v.getPlateNumber() + "】检验有效期将于" + days + "天后到期（" + v.getInspectionValidMonth() + "），请及时办理检验/年审。");
                bos.setSourceType("车辆管理");
                bos.setSenderType("SYSTEM");
                bos.setReceiverType("管理员");
                bos.setReceiverIds(receiverIds);
                bos.setIsDeleted(0L);
                try {
                    notificationService.insertByBo(bos);
                } catch (Exception e) {
                    log.warn("发送车辆检验有效期提醒失败 vehicleId={} companyId={} days={}", v.getId(), v.getCompanyId(), days, e);
                }
            }
        }
    }


    /**
     * 驾驶员身份证到期提醒
     */
    @Scheduled(cron = "0 45 10 * * ?")
    public void notifyDriverIdCardExpiry() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            java.util.Date start = DateUtils.toDate(target.atStartOfDay());
            java.util.Date next = DateUtils.toDate(target.plusDays(1).atStartOfDay());
            List<HotDriver> drivers = hotDriverMapper.selectList(
                    Wrappers.<HotDriver>lambdaQuery()
                            .eq(HotDriver::getIsDeleted, 0L)
                            .eq(HotDriver::getStatus, 1L)
                            .ge(HotDriver::getIdCardExpireDate, start)
                            .lt(HotDriver::getIdCardExpireDate, next)
            );
            if (drivers == null || drivers.isEmpty()) {
                continue;
            }
            List<String> receiverIds = drivers.stream().map(HotDriver::getId).collect(java.util.stream.Collectors.toList());
            String endStr = DateUtils.formatDate(DateUtils.toDate(target));
            HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
            bos.setLevel("紧急");
            bos.setContent("您的身份证将于" + days + "天后到期（到期日：" + endStr + "），请及时更新身份信息。");
            bos.setSourceType("驾驶员管理");
            bos.setSenderType("SYSTEM");
            bos.setReceiverType("驾驶员");
            bos.setReceiverIds(receiverIds);
            bos.setIsDeleted(0L);
            try {
                notificationService.insertByBo(bos);
            } catch (Exception e) {
                log.warn("发送驾驶员身份证到期提醒失败 days={} size={}", days, receiverIds.size(), e);
            }
        }
    }

    /**
     * 车辆运输证到期提醒
     */
    @Scheduled(cron = "0 0 10 * * ?")
    public void notifyVehicleTransportExpiry() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        List<HotVehicle> all = vehicleMapper.selectList(
                Wrappers.<HotVehicle>lambdaQuery()
                        .eq(HotVehicle::getIsDeleted, 0L)
                        .isNotNull(HotVehicle::getTransportValidMonth)
        );
        if (all == null || all.isEmpty()) {
            return;
        }
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            for (HotVehicle v : all) {
                String ymStr = v.getTransportValidMonth();
                if (ymStr == null || ymStr.isEmpty()) {
                    continue;
                }
                try {
                    YearMonth ym = YearMonth.parse(ymStr);
                    LocalDate lastDay = ym.atEndOfMonth();
                    if (!lastDay.equals(target)) {
                        continue;
                    }
                } catch (Exception ignore) {
                    continue;
                }
                List<HotCompanySafetyManager> managers = managerMapper.selectList(
                        Wrappers.<HotCompanySafetyManager>lambdaQuery()
                                .eq(HotCompanySafetyManager::getCompanyId, v.getCompanyId())
                                .eq(HotCompanySafetyManager::getIsDeleted, 0L)
                                .eq(HotCompanySafetyManager::getIsResigned, 0L)
                                .eq(HotCompanySafetyManager::getStatus, 1L)
                );
                if (managers == null || managers.isEmpty()) {
                    continue;
                }
                List<String> receiverIds = managers.stream()
                        .map(m -> String.valueOf(m.getId()))
                        .collect(Collectors.toList());

                HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                bos.setLevel("紧急");
                bos.setContent("车辆【" + v.getPlateNumber() + "】运输证有效期将于" + days + "天后到期（" + v.getTransportValidMonth() + "），请及时办理续证。");
                bos.setSourceType("车辆管理");
                bos.setSenderType("SYSTEM");
                bos.setReceiverType("管理员");
                bos.setReceiverIds(receiverIds);
                bos.setIsDeleted(0L);
                try {
                    notificationService.insertByBo(bos);
                } catch (Exception e) {
                    log.warn("发送车辆运输证有效期提醒失败 vehicleId={} companyId={} days={}", v.getId(), v.getCompanyId(), days, e);
                }
            }
        }
    }

    /**
     * 驾驶员驾驶证到期提醒
     */
    @Scheduled(cron = "0 50 10 * * ?")
    public void notifyDriverLicenseExpiry() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            java.util.Date start = DateUtils.toDate(target.atStartOfDay());
            java.util.Date next = DateUtils.toDate(target.plusDays(1).atStartOfDay());
            List<HotDriver> drivers = hotDriverMapper.selectList(
                    Wrappers.<HotDriver>lambdaQuery()
                            .eq(HotDriver::getIsDeleted, 0L)
                            .eq(HotDriver::getStatus, 1L)
                            .ne(HotDriver::getDriverLicenseLongTerm, 1L)
                            .ge(HotDriver::getDriverLicenseExpireDate, start)
                            .lt(HotDriver::getDriverLicenseExpireDate, next)
            );
            if (drivers == null || drivers.isEmpty()) {
                continue;
            }
            List<String> receiverIds = drivers.stream().map(HotDriver::getId).collect(java.util.stream.Collectors.toList());
            String endStr = DateUtils.formatDate(DateUtils.toDate(target));
            HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
            bos.setLevel("紧急");
            bos.setContent("您的驾驶证将于" + days + "天后到期（到期日：" + endStr + "），请及时更新驾驶证。");
            bos.setSourceType("驾驶员管理");
            bos.setSenderType("SYSTEM");
            bos.setReceiverType("驾驶员");
            bos.setReceiverIds(receiverIds);
            bos.setIsDeleted(0L);
            try {
                notificationService.insertByBo(bos);
            } catch (Exception e) {
                log.warn("发送驾驶员驾驶证到期提醒失败 days={} size={}", days, receiverIds.size(), e);
            }
        }
    }

    /**
     * 驾驶员从业资格证到期提醒
     */
    @Scheduled(cron = "0 55 10 * * ?")
    public void notifyDriverQualificationExpiry() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            java.util.Date start = DateUtils.toDate(target.atStartOfDay());
            java.util.Date next = DateUtils.toDate(target.plusDays(1).atStartOfDay());
            List<HotDriver> drivers = hotDriverMapper.selectList(
                    Wrappers.<HotDriver>lambdaQuery()
                            .eq(HotDriver::getIsDeleted, 0L)
                            .eq(HotDriver::getStatus, 1L)
                            .ge(HotDriver::getQualificationValidEndDate, start)
                            .lt(HotDriver::getQualificationValidEndDate, next)
            );
            if (drivers == null || drivers.isEmpty()) {
                continue;
            }
            List<String> receiverIds = drivers.stream().map(HotDriver::getId).collect(java.util.stream.Collectors.toList());
            String endStr = DateUtils.formatDate(DateUtils.toDate(target));
            HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
            bos.setLevel("紧急");
            bos.setContent("您的从业资格证将于" + days + "天后到期（到期日：" + endStr + "），请及时更新从业资格。");
            bos.setSourceType("驾驶员管理");
            bos.setSenderType("SYSTEM");
            bos.setReceiverType("驾驶员");
            bos.setReceiverIds(receiverIds);
            bos.setIsDeleted(0L);
            try {
                notificationService.insertByBo(bos);
            } catch (Exception e) {
                log.warn("发送驾驶员从业资格证到期提醒失败 days={} size={}", days, receiverIds.size(), e);
            }
        }
    }

    /**
     * 驾驶员诚信承诺书到期提醒
     */
    @Scheduled(cron = "0 0 10 * * ?")
    public void notifyDriverPromiseExpiry() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            java.util.Date start = DateUtils.toDate(target.atStartOfDay());
            java.util.Date next = DateUtils.toDate(target.plusDays(1).atStartOfDay());
            List<HotDriver> drivers = hotDriverMapper.selectList(
                    Wrappers.<HotDriver>lambdaQuery()
                            .eq(HotDriver::getIsDeleted, 0L)
                            .eq(HotDriver::getStatus, 1L)
                            .ge(HotDriver::getIntegrityPromiseExpireDate, start)
                            .lt(HotDriver::getIntegrityPromiseExpireDate, next)
            );
            if (drivers == null || drivers.isEmpty()) {
                continue;
            }
            List<String> receiverIds = drivers.stream().map(HotDriver::getId).collect(java.util.stream.Collectors.toList());
            String endStr = DateUtils.formatDate(DateUtils.toDate(target));
            HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
            bos.setLevel("紧急");
            bos.setContent("您的诚信承诺书将于" + days + "天后到期（到期日：" + endStr + "），请及时更新承诺书。");
            bos.setSourceType("驾驶员管理");
            bos.setSenderType("SYSTEM");
            bos.setReceiverType("驾驶员");
            bos.setReceiverIds(receiverIds);
            bos.setIsDeleted(0L);
            try {
                notificationService.insertByBo(bos);
            } catch (Exception e) {
                log.warn("发送驾驶员承诺书到期提醒失败 days={} size={}", days, receiverIds.size(), e);
            }
        }
    }


    /**
     * 企业保险到期短信提醒
     */
    @Scheduled(cron = "0 10 9 * * ?")
    public void notifyInsuranceExpiryBySms() {
        LocalDate today = LocalDate.now();
        java.util.Date start = DateUtils.toDate(today.atStartOfDay());
        java.util.Date next = DateUtils.toDate(today.plusDays(1).atStartOfDay());
        List<HotCompanyInsurance> ins = insuranceMapper.selectList(
                Wrappers.<HotCompanyInsurance>lambdaQuery()
                        .eq(HotCompanyInsurance::getIsDeleted, 0L)
                        .ge(HotCompanyInsurance::getEndDate, start)
                        .lt(HotCompanyInsurance::getEndDate, next)
        );
        if (ins == null || ins.isEmpty()) {
            return;
        }
        for (HotCompanyInsurance i : ins) {
            SysCompany c = sysCompanyMapper.selectById(i.getCompanyId());
            if (c == null) {
                continue;
            }
            String phone = c.getResponsibleMobile();
            if (StringUtils.isBlank(phone)) {
                continue;
            }
            String endStr = DateUtils.formatDate(i.getEndDate());
            String content = "【企业保险提醒】贵单位保险已到期（险种：" + i.getInsuranceType() + "，终保日期：" + endStr + "），请尽快办理续费。";
            try {
                smsBaoClient.send(phone, content);
            } catch (Exception e) {
                log.warn("企业保险到期短信发送失败 companyId={} phone={}", i.getCompanyId(), phone, e);
            }
        }
    }

    /**
     * 车辆年审到期短信提醒
     */
    @Scheduled(cron = "0 20 9 * * ?")
    public void notifyVehicleInspectionExpiryBySms() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        List<HotVehicle> all = vehicleMapper.selectList(
                Wrappers.<HotVehicle>lambdaQuery()
                        .eq(HotVehicle::getIsDeleted, 0L)
                        .isNotNull(HotVehicle::getInspectionValidMonth)
        );
        if (all == null || all.isEmpty()) {
            return;
        }
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            for (HotVehicle v : all) {
                String ymStr = v.getInspectionValidMonth();
                if (ymStr == null || ymStr.isEmpty()) {
                    continue;
                }
                try {
                    YearMonth ym = YearMonth.parse(ymStr);
                    LocalDate lastDay = ym.atEndOfMonth();
                    if (!lastDay.equals(target)) {
                        continue;
                    }
                } catch (Exception ignore) {
                    continue;
                }
                SysCompany c = sysCompanyMapper.selectById(v.getCompanyId());
                if (c == null) {
                    continue;
                }
                String phone = c.getResponsibleMobile();
                if (StringUtils.isBlank(phone)) {
                    continue;
                }
                String plate = StringUtils.blankToDefault(v.getPlateNumber(), "未知车牌");
                String content = "【车辆年审提醒】车辆【" + plate + "】检验有效期将于" + days + "天后到期（" + ymStr + "），请及时办理检验/年审。";
                try {
                    smsBaoClient.send(phone, content);
                } catch (Exception e) {
                    log.warn("车辆年审到期短信发送失败 vehicleId={} companyId={} phone={}", v.getId(), v.getCompanyId(), phone, e);
                }
            }
        }
    }

    /**
     * 车辆二级保养到期短信提醒
     */
    @Scheduled(cron = "0 30 9 * * ?")
    public void notifyVehicleSecondaryMaintenanceBySms() {
        LocalDate today = LocalDate.now();
        List<Integer> offsets = Arrays.asList(30, 15, 7, 1);
        List<HotVehicle> vehicles = vehicleMapper.selectList(
                Wrappers.<HotVehicle>lambdaQuery()
                        .eq(HotVehicle::getIsDeleted, 0L)
        );
        if (vehicles == null || vehicles.isEmpty()) {
            return;
        }
        for (Integer days : offsets) {
            LocalDate target = today.plusDays(days);
            for (HotVehicle v : vehicles) {
                if (v.getCompanyId() == null || v.getCertificateIssueDate() == null) {
                    continue;
                }
                HotVehicleSecondaryMaintenanceConfigBo configBo = new HotVehicleSecondaryMaintenanceConfigBo();
                configBo.setCompanyId(v.getCompanyId());
                List<HotVehicleSecondaryMaintenanceConfigVo> allConfigs = secondaryMaintenanceConfigService.queryList(configBo);
                if (allConfigs == null || allConfigs.isEmpty()) {
                    continue;
                }
                List<HotVehicleSecondaryMaintenanceConfigVo> configs = allConfigs.stream()
                        .filter(c -> c.getIsEnabled() == null || c.getIsEnabled() == 1L)
                        .sorted(java.util.Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears()))
                        .toList();
                if (configs.isEmpty()) {
                    continue;
                }
                LocalDate issueDate = v.getCertificateIssueDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                long ageYears = ChronoUnit.YEARS.between(issueDate, today);
                HotVehicleSecondaryMaintenanceConfigVo matchConfig = null;
                for (HotVehicleSecondaryMaintenanceConfigVo c : configs) {
                    if (c.getIntervalYears() != null && c.getIntervalYears() > ageYears) {
                        matchConfig = c;
                        break;
                    }
                }
                if (matchConfig == null) {
                    matchConfig = configs.get(configs.size() - 1);
                }
                if (matchConfig == null || matchConfig.getInspectionTimes() == null || matchConfig.getInspectionTimes() == 0) {
                    continue;
                }
                long intervalMonths = 12 / matchConfig.getInspectionTimes();
                HotVehicleMaintenanceVo lastMaintenance = vehicleMaintenanceService.queryLatestByVehicleId(v.getId());
                LocalDate lastMaintenanceDate;
                if (lastMaintenance != null && lastMaintenance.getFinishTime() != null) {
                    lastMaintenanceDate = lastMaintenance.getFinishTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                } else {
                    lastMaintenanceDate = issueDate;
                }
                LocalDate dueDate = lastMaintenanceDate.plusMonths(intervalMonths);
                if (!dueDate.equals(target)) {
                    continue;
                }
                SysCompany c = sysCompanyMapper.selectById(v.getCompanyId());
                if (c == null) {
                    continue;
                }
                String phone = c.getResponsibleMobile();
                if (StringUtils.isBlank(phone)) {
                    continue;
                }
                String plate = StringUtils.blankToDefault(v.getPlateNumber(), "未知车牌");
                String dueStr = DateUtils.formatDate(DateUtils.toDate(dueDate));
                String content = "【车辆二级维护提醒】车辆【" + plate + "】将于" + days + "天后到期（二级维护到期日：" + dueStr + "），请及时安排维护。";
                try {
                    smsBaoClient.send(phone, content);
                } catch (Exception e) {
                    log.warn("车辆二级维护到期短信发送失败 vehicleId={} companyId={} phone={}", v.getId(), v.getCompanyId(), phone, e);
                }
            }
        }
    }


}
