package com.fs.app.service;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.exception.base.BaseException;
import com.fs.common.utils.StringUtils;
import com.fs.common.utils.date.DateUtil;
import com.fs.common.utils.spring.SpringUtils;
import com.fs.company.domain.Company;
import com.fs.company.domain.CompanyMiniapp;
import com.fs.company.mapper.CompanyMapper;
import com.fs.company.service.ICompanyMiniappService;
import com.fs.course.config.CourseConfig;
import com.fs.course.domain.*;
import com.fs.course.mapper.FsCoursePlaySourceConfigMapper;
import com.fs.course.mapper.FsUserCourseVideoMapper;
import com.fs.course.service.IFsCourseLinkService;
import com.fs.course.service.IFsCoursePlaySourceConfigService;
import com.fs.course.service.IFsCourseWatchLogService;
import com.fs.his.domain.FsUser;
import com.fs.his.mapper.FsUserMapper;
import com.fs.his.utils.LinkUtil;
import com.fs.his.utils.PhoneUtil;
import com.fs.ipad.IpadSendUtils;
import com.fs.ipad.vo.*;
import com.fs.live.domain.LiveWatchLog;
import com.fs.live.mapper.LiveWatchLogMapper;
import com.fs.qw.domain.QwExternalContact;
import com.fs.qw.domain.QwUser;
import com.fs.qw.domain.QwUserVideo;
import com.fs.qw.mapper.LuckyBagCollectRecordMapper;
import com.fs.qw.mapper.LuckyBagMapper;
import com.fs.qw.mapper.QwExternalContactMapper;
import com.fs.qw.mapper.QwUserMapper;
import com.fs.qw.service.IQwUserService;
import com.fs.qw.service.IQwUserVideoService;
import com.fs.qw.vo.QwSopCourseFinishTempSetting;
import com.fs.qwApi.param.QwExternalContactHParam;
import com.fs.sop.domain.QwSopLogs;
import com.fs.sop.service.IQwSopLogsService;
import com.fs.sop.service.impl.QwSopLogsServiceImpl;
import com.fs.system.service.ISysConfigService;
import com.fs.wxwork.dto.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@AllArgsConstructor
public class IpadSendServer {

    private final QwUserMapper qwUserMapper;
    private final IQwUserService qwUserService;
    private final IpadSendUtils ipadSendUtils;
    private final IQwSopLogsService qwSopLogsService;
    private final QwExternalContactMapper qwExternalContactMapper;
    private final IFsCourseWatchLogService watchLogService;
    private final IQwUserVideoService qwUserVideoService;
    private final RedisCache redisCache;
    private final ICompanyMiniappService companyMiniappService;
    private final IFsCoursePlaySourceConfigService playSourceConfigService;
    private final FsUserMapper fsUserMapper;
    private final FsUserCourseVideoMapper fsUserCourseVideoMapper;
    private final ISysConfigService configService;
    private final LuckyBagMapper luckyBagMapper;

    private final CompanyMapper companyMapper;

    @Autowired
    private LuckyBagCollectRecordMapper luckyBagCollectRecordMapper;

    private final IFsCourseLinkService linkService;

    private static final List<String> PROJECT_NAMES = Arrays.asList("济南联志健康", "北京存在文化","宽益堂");
    private final LiveWatchLogMapper liveWatchLogMapper;

    private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap, Long companyId) {
        // 发送参数原本的appid
        String appid = content.getMiniprogramAppid();
        // 判断销售工时ID不为空并且有小程序类型
        if(companyId != null && content.getMiniType() != null){
            // 获取销售公司下面绑定的主备小程序，并且根据当前应该发送的主备类型查询出数据
            List<CompanyMiniapp> list = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId).eq("type", content.getMiniType()));
            // 判断当前绑定的最新的小程序，并且覆盖以前的值（可以达到实时替换小程序的功能）
            if(!list.isEmpty() && list.get(0) != null && StringUtils.isNotEmpty(list.get(0).getAppId())){
                appid = list.get(0).getAppId();
            }
            // 获取配置文件里面的项目名称
            String signProjectName = SpringUtils.getProperty("cloud_host.company_name");
            //区分新老用户，新用户发送备用小程序，老用户发送主小程序，TODO 需要使用的项目在参数里面加上
            if(PROJECT_NAMES.contains(signProjectName)){
                log.info("ID:{}, qwUserId:{}，externalId:{}，进入区分发小程序逻辑", vo.getId(), vo.getQwUserId(), vo.getExId());
                // 判断消息是否是群聊，不是群聊再进去，TODO 当权无法判断群聊是否可以发送对应的小程序
                if(!vo.isRoom()){
                    log.info("qwUserId:{}，externalId:{}，不是群聊", vo.getQwUserId(), vo.getExId());
                    try {
                        // 获取当前外部联系人
                        QwExternalContact qwExternalContact = qwExternalContactMapper.selectOne(new LambdaQueryWrapper<QwExternalContact>().eq(QwExternalContact::getQwUserId,vo.getQwUserId()).eq(QwExternalContact::getExternalUserId,vo.getExId()).last(" limit 1"));
                        // 当前外部联系是否绑定用户数据，如果数据为空则是没看过小程序的新用户，不用去判断小程序，跳过判断
                        if(qwExternalContact.getFsUserId() != null){
                            // 根据用户绑定信息去查询用户信息
                            FsUser fsUser = fsUserMapper.selectFsUserByUserId(qwExternalContact.getFsUserId());
                            // 判断用户是否有看过其他的小程序多个
                            if(StringUtils.isNotEmpty(fsUser.getAppId())){
                                // 获取用户看过的小程序ID集合
                                List<String> miniAppList = Arrays.asList(fsUser.getAppId().split(","));
                                // 根据小程序ID查询小程序列表
                                List<FsCoursePlaySourceConfig> configList = playSourceConfigService.selectByAppIds(miniAppList);
                                // 筛选出半封禁的小程序数据，得到这个数据然后优先发这个小程序
                                Optional<FsCoursePlaySourceConfig> optional = configList.stream().filter(e -> e.getStatus() != null && e.getStatus() == 1).findFirst();
                                // 判断是否找到
                                if(optional.isPresent()){
                                    // 找到半封禁并且在appid里面存在证明这个客户是打开过小程序，优先发送这个
                                    appid = optional.get().getAppid();
                                }
                            }else{
                                // 获取用户的创建时间
                                LocalDateTime createTime = DateUtil.dateToLocalDateTime(fsUser.getCreateTime());
                                log.info("ID:{}, qwUserId:{}，externalId:{}，已绑定小程序，判断时间：{}", vo.getId(), vo.getQwUserId(), vo.getExId(), createTime);
                                // 这个时间是写死的，目前判断的芳华封禁的时间，可以更具项目调整
                                LocalDateTime lastTime = LocalDateTime.of(2025, 11, 6, 23, 59, 59);
                                // 判断客户创建时间是在这个之前还是之后
                                int listIndex = createTime.isAfter(lastTime) ? 1 : 0 ;
                                // 获取公司里面的主备小程序，根据用户创建时间来发送主备，如果创建时间大于上看的时间就发送备用小程序，如果小于就发送主要小程序
                                List<CompanyMiniapp> collect2 = list.stream().filter(e -> e.getType().equals(listIndex)).collect(Collectors.toList());
                                // 判断是否获取到了配置
                                if(!collect2.isEmpty() && collect2.get(0) != null && StringUtils.isNotEmpty(collect2.get(0).getAppId())){
                                    appid = collect2.get(0).getAppId();
                                    log.info("ID:{}, qwUserId:{}，externalId:{}，发送小程序：{}", vo.getId(), vo.getQwUserId(), vo.getExId(), appid);
                                }
                            }
                        }else{
                            log.info("ID:{}, qwUserId:{}，externalId:{}，未绑定小程序用户", vo.getId(), vo.getQwUserId(), vo.getExId());
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        log.error("6.输出外部联系人ID-------------->{}",vo.getExId());
                        log.error("7.数据异常----------------------》{}",e.getMessage());
                    }
                }
            }
        }
        FsCoursePlaySourceConfig courseMaConfig = miniMap.get(appid);
        if(courseMaConfig == null){
            List<CompanyMiniapp> list = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId).eq("type", 1));
            if(!list.isEmpty() && list.get(0) != null && StringUtils.isNotEmpty(list.get(0).getAppId())){
                courseMaConfig = miniMap.get(list.get(0).getAppId());
            }
        }
        if(courseMaConfig == null){
            throw new BaseException("未找到小程序配置：{}", appid);
        }
        // 小程序
        MiniProgramVo miniProgramVo = MiniProgramVo.builder()
                .desc(content.getMiniprogramTitle())
                .title(courseMaConfig.getName())
                .weappIconUrl(courseMaConfig.getImg())
                .imgUrl(content.getMiniprogramPicUrl())
                .username(courseMaConfig.getOriginalId() + "@app")
                .pagepath(content.getMiniprogramPage())
                .appid(courseMaConfig.getAppid())
                .build();
        miniProgramVo.setBase(vo);
        WxWorkResponseDTO<WxWorkSendAppMsgRespDTO> resp = ipadSendUtils.sendMiniProgram(miniProgramVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    private void sendFile(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
        FileVo fileVo = FileVo.builder()
                .url(content.getFileUrl())
                .build();
        fileVo.setBase(vo);
        WxWorkResponseDTO<WxwSendCDNFileMsgRespDTO> resp = ipadSendUtils.sendFile(fileVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    public void sendTxt(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
        TxtVo txtVo = TxtVo.builder().content(content.getValue()).build();
        txtVo.setBase(vo);
        WxWorkResponseDTO<WxWorkSendTextMsgRespDTO> resp = ipadSendUtils.sendTxt(txtVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    /**
     * 发送群公告
     */
    public void sendNotice(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
        WxWorkResponseDTO<WxwSendNoticeMsgRespDTO> resp = ipadSendUtils.sendNotice(vo, content.getValue());
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    public void sendImg(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
        FileVo imgVo = FileVo.builder().url(content.getImgUrl()).build();
        imgVo.setBase(vo);
        WxWorkResponseDTO<WxwSendCDNImgMsgRespDTO> resp = ipadSendUtils.sendImg(imgVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    public void sendLink(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
        // 文本
        LinkVo linkVo = LinkVo.builder()
                .title(content.getLinkTitle())
                .content(content.getLinkDescribe())
                .url(content.getLinkUrl())
                .imgUrl(content.getLinkImageUrl())
                .build();
        linkVo.setBase(vo);
        WxWorkResponseDTO<WxwSendLinkMsgRespDTO> resp = ipadSendUtils.sendLink(linkVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    public void sendVideo(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
        FileVo videoVo = FileVo.builder()
                .url(content.getVideoUrl())
                .build();
        videoVo.setBase(vo);
        WxWorkResponseDTO<WxwSendCDNVideoMsgRespDTO> resp = ipadSendUtils.sendVideo(videoVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    public void sendWxVideo(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
        QwUserVideo qwUserVideo = qwUserVideoService.selectQwUserVideoById(content.getVideoId());
        WxVideoVo videoVo = WxVideoVo.builder()
                .coverUrl(qwUserVideo.getCoverUrl())
                .thumbUrl(qwUserVideo.getThumbUrl())
                .avatar(qwUserVideo.getAvatar())
                .nickname(qwUserVideo.getNickName())
                .desc(qwUserVideo.getDesc())
                .url(qwUserVideo.getUrl())
                .extras(qwUserVideo.getExtras())
                .objectId(qwUserVideo.getObjectId())
                .objectNonceId(qwUserVideo.getObjectNonceId())
                .build();
        videoVo.setBase(vo);
        WxWorkResponseDTO<WxwSendVideoNumberRespDTO> resp = ipadSendUtils.sendWxVideo(videoVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    public void sendTxtAtMsg(BaseVo vo) {
        WxSendTextAtMsgTwoDTO dto = new WxSendTextAtMsgTwoDTO();
        List<WxSendTextAtMsgTwoDTO.Contentva> contentvaList = new ArrayList<>();
        WxSendTextAtMsgTwoDTO.Contentva contentva = new WxSendTextAtMsgTwoDTO.Contentva();
        contentva.setMsgtype(5);
        contentva.setVid(0);
        contentvaList.add(contentva);
        dto.setContentva(contentvaList);
        dto.setBase(vo);
        dto.setUuid(vo.getUuid());
        dto.setSend_userid(ipadSendUtils.userIds(vo));
        dto.setIsRoom(true);
        ipadSendUtils.sendTxtAtMsgVo(dto, vo.getServerId());
    }

    public void sendVoice(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
        if (StringUtils.isEmpty(content.getVoiceUrl()) || StringUtils.isEmpty(content.getVoiceDuration())) {
            log.debug("语音未生成无法发送，转文字发送：{}", vo);
            sendTxt(vo, content);
            return;
        }
        FileVo voiceVo = FileVo.builder()
                .url(content.getVoiceUrl())
                .voiceTime(Integer.parseInt(content.getVoiceDuration()))
                .build();
        voiceVo.setBase(vo);
        WxWorkResponseDTO<WxwSendCDNVoiceMsgRespDTO> resp = ipadSendUtils.sendVoice(voiceVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    public boolean isSend(QwUser qwUser, BaseVo parentVo) {
        // 判断企微发送方式是否是ipad
        if (qwUser.getSendMsgType() == 0) {
            log.debug("发送方式是侧边栏企微用户：ID：{} 名称：{}", qwUser.getId(), qwUser.getQwUserName());
            return false;
        }
        // 判断是否信息准确
        if (org.springframework.util.StringUtils.isEmpty(qwUser.getUid())) {
            log.debug("企微用户：ID：{} 名称：{}", qwUser.getId(), qwUser.getQwUserName());
            return false;
        }
        // 是否已经授权服务器地址
        if (qwUser.getServerId() == null) {
            log.debug("企微用户：ID：{} 名称：{} 未绑定AI主机", qwUser.getId(), qwUser.getQwUserName());
            return false;
        }
        // ipad状态是否是登录状态
        if (qwUser.getIpadStatus() == 0) {
            log.debug("企微用户：ID：{} 名称：{} 未登录uid:{}，serveID:{}", qwUser.getId(), qwUser.getQwUserName(), qwUser.getUid(), qwUser.getServerId());
            return false;
        }
        // 更新信息使用的类
        QwUser updateQwUser = new QwUser();
        try {
            // 调用ipad接口判断是否真实登录
            WxLoginResp login = ipadSendUtils.isLogin(qwUser.getUid(), qwUser.getServerId());
            if (login.getLoginType() == 0) {
                updateQwUser.setId(qwUser.getId());
                updateQwUser.setRemark("AI未初始化");
                updateQwUser.setIpadStatus(0);
                qwUserMapper.updateById(updateQwUser);
                return false;
            }
            if (login.getLoginType() == 1) {
                updateQwUser.setId(qwUser.getId());
                updateQwUser.setRemark("未登录");
                updateQwUser.setIpadStatus(0);
                qwUserMapper.updateById(updateQwUser);
                qwUserService.atMsg(qwUser, "掉线提醒（未登录）");
                return false;
            }
            if (!login.getUser_info().isLogin()) {
                updateQwUser.setId(qwUser.getId());
                updateQwUser.setRemark("登录状态异常");
                updateQwUser.setIpadStatus(0);
                qwUserMapper.updateById(updateQwUser);
                qwUserService.atMsg(qwUser, "掉线提醒（登录异常）");
                return false;
            }
            parentVo.setCorpId(login.getUser_info().getObject().getCorp_id());
            log.debug("QwUserID:{}, AI主机信息：{}", qwUser.getId(), login);
        } catch (Exception e) {
            updateQwUser.setId(qwUser.getId());
            updateQwUser.setRemark("登录状态异常：" + e.getMessage());
            updateQwUser.setIpadStatus(0);
            qwUserMapper.updateById(updateQwUser);
            qwUserService.atMsg(qwUser, "掉线提醒（登录信息异常，请联系管理员："+e.getMessage()+"）");
            return false;
        }

        return true;
    }

    public boolean isSendLogs(QwSopLogs qwSopLogs, QwSopCourseFinishTempSetting setting, QwUser qwUser) {
        if(qwSopLogs.getSendStatus() != 3){
            log.info("状态异常不发送：{}, LOGID: {}", qwUser.getQwUserName(), qwSopLogs.getId());
            return false;
        }
        if(redisCache.getCacheObject("qw:user:id:" + qwUser.getId()) != null){
            log.info("频率异常不发送：{}", qwUser.getQwUserName());
            return false;
        }

        boolean noSop = qwSopLogs.getSendType() != 3 && qwSopLogs.getSendType() != 7;

        if (qwSopLogs.getExpiryTime() == null && noSop) {
            // 作废消息
            log.warn("SOP_LOG_ID:{}, SOP任务被删除", qwSopLogs.getId());
            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "SOP任务被删除");
            return false;
        }

        LocalDateTime sendTime = DateUtil.stringToLocalDateTime(qwSopLogs.getSendTime());
        LocalDateTime expiryDateTime;

        // 判断是否过期
        if(qwSopLogs.getSendType() == 3 || qwSopLogs.getSendType() == 7){
            expiryDateTime = sendTime.plusHours(12);
        }else{
            expiryDateTime = sendTime.plusHours(qwSopLogs.getExpiryTime());
        }

        if (LocalDateTime.now().isAfter(expiryDateTime)  ) {
            // 作废消息
            log.warn("SOP_LOG_ID:{}, 已过期，不发送", qwSopLogs.getId());
            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "已过期，不发送");
            return false;
        }

        if (setting.getCourseType() == null && noSop && setting.getType() == 2) {
            log.warn("SOP_LOG_ID:{}, 模板未选消息类型，不发送", qwSopLogs.getId());
            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "模板未选消息类型，不发送");
            return false;
        }

        // 查询视频是否下架
        if(setting.getVideoId()!= null){
            FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId( setting.getVideoId().longValue());
            if(video != null){
                if(video.getIsOnPut()!=null && video.getIsOnPut() == 1){
                    log.warn("SOP_LOG_ID:{}, 视频已下架，不发送", qwSopLogs.getId());
                    qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "视频已下架，不发送");
                    return false;
                }
            }
        }

        if (qwSopLogs.getSendType() != 6 && noSop) {
            // 客户的信息
//            QwExternalContactHParam contactHParam = new QwExternalContactHParam();
//            contactHParam.setUserId(qwUser.getQwUserId().trim());
//            contactHParam.setExternalUserId(qwSopLogs.getExternalUserId().trim());
//            contactHParam.setCorpId(qwUser.getCorpId().trim());
            Integer courseType = setting.getCourseType();
            if (setting.getType() == 2 && courseType != 0) {// 课程消息，进行复杂的条件判断
//                log.debug("企微查询：{}", contactHParam);
//                Long qwExternalContactId = qwExternalContactMapper.getQwExternalContactId(contactHParam);
                FsCourseWatchLog watchLog = watchLogService.getWatchCourseLogVideoBySop(
                        setting.getVideoId().longValue(),
                        String.valueOf(qwUser.getId()),
                        qwSopLogs.getExternalId()
                );
                log.debug("ID:{}-看课记录参数：videoID:{}， qwUserID:{}， extID:{}", qwSopLogs.getId(), setting.getVideoId().longValue(), qwUser.getId(), qwSopLogs.getExternalId());
                log.debug("ID:{}-看课记录：{}", qwSopLogs.getId(), watchLog);
                String logId = qwSopLogs.getId();
                if (watchLog != null) {
                    //新逻辑
                    if (!QwSopLogsServiceImpl.isCourseTypeValid(courseType, watchLog.getLogType())) {
                        // 作废消息
                        log.warn("SOP_LOG_ID:{}, 看课状态未满足，不发送", qwSopLogs.getId());
                        qwSopLogsService.updateQwSopLogsByWatchLogType(logId, "看课状态未满足，不发送");
                        return false;
                    }
                } else {
                    log.warn("SOP_LOG_ID:{}, 无观看记录，不发送", qwSopLogs.getId());
                    qwSopLogsService.updateQwSopLogsByWatchLogType(logId, "无观看记录，不发送");
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 直播间判定消息
     * @param qwSopLogs
     * @param setting
     * @param qwUser
     * @return
     */
    public boolean isSendLogsLive(QwSopLogs qwSopLogs, QwSopCourseFinishTempSetting setting, QwUser qwUser){
        if(qwSopLogs.getSendStatus() != 3){
            log.info("直播状态异常不发送：{}, LOGID: {}", qwUser.getQwUserName(), qwSopLogs.getId());
            return false;
        }
        if(redisCache.getCacheObject("qw:user:id:" + qwUser.getId()) != null){
            log.info("直播频率异常不发送：{}", qwUser.getQwUserName());
            return false;
        }

        boolean noSop = qwSopLogs.getSendType() != 3 && qwSopLogs.getSendType() != 7;

        if (qwSopLogs.getExpiryTime() == null && noSop) {
            // 作废消息
            log.warn("直播SOP_LOG_ID:{}, 直播SOP任务被删除", qwSopLogs.getId());
            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "直播SOP任务被删除");
            return false;
        }

        LocalDateTime sendTime = DateUtil.stringToLocalDateTime(qwSopLogs.getSendTime());
        LocalDateTime expiryDateTime;

        // 判断是否过期
        if(qwSopLogs.getSendType() == 3 || qwSopLogs.getSendType() == 7){
            expiryDateTime = sendTime.plusHours(12);
        }else{
            expiryDateTime = sendTime.plusHours(qwSopLogs.getExpiryTime());
        }

        if (LocalDateTime.now().isAfter(expiryDateTime)) {
            // 作废消息
            log.warn("直播SOP_LOG_ID:{}, 已过期，不发送", qwSopLogs.getId());
            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "已过期，不发送");
            return false;
        }

        if (setting.getCourseType() == null && noSop && setting.getType() == 2) {
            log.warn("直播SOP_LOG_ID:{}, 模板未选消息类型，不发送", qwSopLogs.getId());
            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "直播模板未选消息类型，不发送");
            return false;
        }

        // 查询视频是否下架
//        if(setting.getVideoId()!= null){
//            FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId( setting.getVideoId().longValue());
//            if(video != null){
//                if(video.getIsOnPut()!=null && video.getIsOnPut() == 1){
//                    log.warn("SOP_LOG_ID:{}, 视频已下架，不发送", qwSopLogs.getId());
//                    qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "视频已下架，不发送");
//                    return false;
//                }
//            }
//        }
        Long queryLiveId = null;
        queryLiveId = setting.getLiveId();
        if(null == queryLiveId){
            for (QwSopCourseFinishTempSetting.Setting a : setting.getSetting()) {
                if (StringUtils.isNotBlank(a.getLiveId())) {
                    queryLiveId = Long.valueOf(a.getLiveId());
                    break;
                }
            }
        }
        LiveWatchLog liveWatchLog = liveWatchLogMapper.selectOneLogByLiveIdAndQwUserIdAndExternalId(
                queryLiveId,
                String.valueOf(qwUser.getId()),
                qwSopLogs.getExternalId()
        );
        Integer courseType = setting.getCourseType();
        String logId = qwSopLogs.getId();
        if(null != liveWatchLog){
            if (!QwSopLogsServiceImpl.isCourseTypeValid(courseType, liveWatchLog.getLogType())) {
                // 作废消息
                log.warn("SOP_LOG_ID:{}, 看课状态未满足，不发送", qwSopLogs.getId());
                qwSopLogsService.updateQwSopLogsByWatchLogType(logId, "看课状态未满足，不发送");
                return false;
            }
        }
        else{
            log.warn("SOP_LOG_ID:{}, 无观看记录，不发送", qwSopLogs.getId());
            qwSopLogsService.updateQwSopLogsByWatchLogType(logId, "无观看记录，不发送");
            return false;
        }
//        if (qwSopLogs.getSendType() != 6 && noSop) {
//            // 客户的信息
////            QwExternalContactHParam contactHParam = new QwExternalContactHParam();
////            contactHParam.setUserId(qwUser.getQwUserId().trim());
////            contactHParam.setExternalUserId(qwSopLogs.getExternalUserId().trim());
////            contactHParam.setCorpId(qwUser.getCorpId().trim());
//            Integer courseType = setting.getCourseType();
//            if (setting.getType() == 2 && courseType != 0) {// 课程消息，进行复杂的条件判断
////                log.debug("企微查询：{}", contactHParam);
////                Long qwExternalContactId = qwExternalContactMapper.getQwExternalContactId(contactHParam);
//                FsCourseWatchLog watchLog = watchLogService.getWatchCourseLogVideoBySop(
//                        setting.getVideoId().longValue(),
//                        String.valueOf(qwUser.getId()),
//                        qwSopLogs.getExternalId()
//                );
//                log.debug("ID:{}-看课记录参数：videoID:{}， qwUserID:{}， extID:{}", qwSopLogs.getId(), setting.getVideoId().longValue(), qwUser.getId(), qwSopLogs.getExternalId());
//                log.debug("ID:{}-看课记录：{}", qwSopLogs.getId(), watchLog);
//                String logId = qwSopLogs.getId();
//                if (watchLog != null) {
//                    //新逻辑
//                    if (!QwSopLogsServiceImpl.isCourseTypeValid(courseType, watchLog.getLogType())) {
//                        // 作废消息
//                        log.warn("SOP_LOG_ID:{}, 看课状态未满足，不发送", qwSopLogs.getId());
//                        qwSopLogsService.updateQwSopLogsByWatchLogType(logId, "看课状态未满足，不发送");
//                        return false;
//                    }
//                } else {
//                    log.warn("SOP_LOG_ID:{}, 无观看记录，不发送", qwSopLogs.getId());
//                    qwSopLogsService.updateQwSopLogsByWatchLogType(logId, "无观看记录，不发送");
//                    return false;
//                }
//            }
//        }
        return true;
    }

    /**
     * 群公告判定消息
     * @param qwSopLogs
     * @param setting
     * @param qwUser
     * @return
     */
    public boolean isSendLogsGroupNotice(QwSopLogs qwSopLogs, QwSopCourseFinishTempSetting setting, QwUser qwUser){
        // 群公告只能发给群聊
        if(qwSopLogs.getSendType() != 6 && qwSopLogs.getSendType() != 21){
            log.warn("群公告SOP_LOG_ID:{}, 不是群聊消息，不发送", qwSopLogs.getId());
            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "群公告只能发给群聊，不发送");
            return false;
        }

        if(qwSopLogs.getSendStatus() != 3){
            log.info("群公告状态异常不发送：{}, LOGID: {}", qwUser.getQwUserName(), qwSopLogs.getId());
            return false;
        }
        if(redisCache.getCacheObject("qw:user:id:" + qwUser.getId()) != null){
            log.info("群公告频率异常不发送：{}", qwUser.getQwUserName());
            return false;
        }

        boolean noSop = qwSopLogs.getSendType() != 3 && qwSopLogs.getSendType() != 7 && qwSopLogs.getSendType() != 21;

        if (qwSopLogs.getExpiryTime() == null && noSop) {
            // 作废消息
            log.warn("群公告SOP_LOG_ID:{}, SOP任务被删除", qwSopLogs.getId());
            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "SOP任务被删除");
            return false;
        }

        LocalDateTime sendTime = DateUtil.stringToLocalDateTime(qwSopLogs.getSendTime());
        LocalDateTime expiryDateTime;

        // 判断是否过期
        if(qwSopLogs.getSendType() == 3 || qwSopLogs.getSendType() == 7 || qwSopLogs.getSendType() == 21){
            expiryDateTime = sendTime.plusHours(12);
        }else{
            expiryDateTime = sendTime.plusHours(qwSopLogs.getExpiryTime());
        }

        if (LocalDateTime.now().isAfter(expiryDateTime)) {
            // 作废消息
            log.warn("群公告SOP_LOG_ID:{}, 已过期，不发送", qwSopLogs.getId());
            qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "已过期，不发送");
            return false;
        }

        return true;
    }

    public void send(QwSopCourseFinishTempSetting.Setting content, QwUser qwUser, QwSopLogs qwSopLogs, Map<String, FsCoursePlaySourceConfig> miniMap, BaseVo parentVo) {
        BaseVo vo = new BaseVo();
        vo.setId(Long.parseLong(qwSopLogs.getId()));
        vo.setRoom(qwSopLogs.getSendType() == 6 || qwSopLogs.getSendType() == 21);
        vo.setUuid(qwUser.getUid());
        vo.setExId(qwSopLogs.getExternalUserId());
        vo.setServerId(qwUser.getServerId());
        vo.setCorpCode(parentVo.getCorpCode());
        vo.setCorpId(parentVo.getCorpId());
        vo.setQwUserId(qwUser.getId());
        try {
            content.setSendStatus(1);
            if(qwSopLogs.getSendType() == 21 && "11".equals(content.getContentType())){
                sendNotice(vo, content);
                return;
            }
            switch (content.getContentType()) {
                case "1":
                    // 文本
                    sendTxt(vo, content);
                    break;
                case "2":
                    // 图片
                    sendImg(vo, content);
                    break;
                case "3":
                    // 链接
                    sendLink(vo, content);
                    break;
                case "4":
                case "10":
                    sendMiniProgram(vo, content, miniMap, qwUser.getCompanyId());
                    break;
                case "5":
                    // 文件
                    sendFile(vo, content);
                    break;
                case "6":
                    // 视频
                    sendVideo(vo, content);
                    break;
                case "7":
                    // 语音
                    sendVoice(vo, content);
                    break;
                case "8":
                    // 语音
                    sendWxVideo(vo, content);
                    break;
                case "13":
                    // 注册过的不在发送了
                    QwExternalContact qwExternalContact =qwExternalContactMapper.selectQwExternalContactById(qwSopLogs.getExternalId());
                    if(qwExternalContact!=null && qwExternalContact.getFsUserId()!=null){
                        qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "已经注册过的客户不发送");
                    }
                    sendMiniProgram(vo, content, miniMap, qwUser.getCompanyId());
                case "14":
                    // 记录福袋发送记录
                    Long businessId = addLuckyBagCollectRecord(qwUser, content,qwSopLogs);
                    if (ObjectUtil.isEmpty(businessId)) {
                        qwSopLogsService.updateQwSopLogsByWatchLogType(qwSopLogs.getId(), "福袋发放失败,请重新发送!");
                    }
                    sendMiniProgram(vo, content, miniMap, qwUser.getCompanyId());
                case "17":
                    // 小程序H5看课
                    sendMiniProgram(vo, content, miniMap);
                    break;
                case "18":
//                    // 发送直播卡片
//                    sendLiveMiniProgram(vo, content, miniMap);
                    // 链接
                    sendLink(vo, content);
                    break;
                case "19":
                    // 发送直播短链
                    sendLiveShortLink(vo, content, miniMap);
                    break;
                case "21":
                    content.setSendStatus(0);
                    content.setSendRemarks("短信待发送");
                    break;
                    //跳转APP看课链接
                case "23":
                    //跳转APP直播链接
                case "24":
                    sendAppShortLink(vo, content, miniMap);
                    break;
                case "99":
                    // 群发
                    sendTxtAtMsg(vo);
                    break;
                default:
                    // 未知类型，记录警告
                    log.error("SOP_LOG_ID:{}错误的发送类型: {}", qwSopLogs.getId(), content.getContentType());
                    break;
            }
        } catch (Exception e) {
            log.error("ID:{}-发送失败QW_SOP_ID：{},content:{},vo:{}", qwSopLogs.getId(), qwSopLogs.getId(), JSON.toJSONString(content), JSON.toJSONString(vo), e);
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + e.getMessage());
        }
    }

    private void sendAppShortLink(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap) {
//        //发送短链前，先发送一个介绍语
//        TxtVo introduction = TxtVo.builder().content("请复制以下短链内容").build();
//        introduction.setBase(vo);
//        ipadSendUtils.sendTxt(introduction);
        //发送短链内容
        String miniProgramPage = content.getMiniprogramPage();
        String sendShortLink = null;
        //解析直播短链信息
        String livePrefix = "/pages_live/livingList?link=";
        if (miniProgramPage.startsWith(livePrefix)) {
            JSONObject obj = JSONObject.parseObject(miniProgramPage.substring(livePrefix.length()));
            sendShortLink = livePrefix + obj.getString("link");
        }
        //解析课程短链信息
        String coursePrefix = "/pages/courseAnswer/index?link=";
        if (miniProgramPage.startsWith(coursePrefix)) {
            JSONObject obj = JSONObject.parseObject(miniProgramPage.substring(coursePrefix.length()));
            sendShortLink = coursePrefix + obj.getString("link");
        }
        if (null == sendShortLink) {
            log.warn("发送链接为空");
            return;
        }
        sendShortLink = sendShortLink.replace(".html","");
        String InvitationCode = LinkUtil.encryptLink(sendShortLink);
        TxtVo txtVo = TxtVo.builder().content("康好健康"+InvitationCode).build();
        txtVo.setBase(vo);
        WxWorkResponseDTO<WxWorkSendTextMsgRespDTO> resp = ipadSendUtils.sendTxt(txtVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    /**
     * 发送直播短链
     * @param vo
     * @param content
     * @param miniMap
     */
    private void sendLiveShortLink(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap) {
        FsCoursePlaySourceConfig courseMaConfig = miniMap.get(content.getMiniprogramAppid());
        String miniProgramPage = content.getMiniprogramPage();
        miniProgramPage = miniProgramPage.replace(".html","");
        String link = linkService.getGotoWxAppLink(miniProgramPage, courseMaConfig.getAppid());

        TxtVo txtVo = TxtVo.builder().content(link).build();
        txtVo.setBase(vo);
        WxWorkResponseDTO<WxWorkSendTextMsgRespDTO> resp = ipadSendUtils.sendTxt(txtVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    /**
     * 发送直播卡片
     * @param vo
     * @param content
     * @param miniMap
     */
    private void sendLiveMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap) {
        FsCoursePlaySourceConfig courseMaConfig = miniMap.get(content.getMiniprogramAppid());
        // 小程序
        MiniProgramVo miniProgramVo = MiniProgramVo.builder()
                .desc(content.getMiniprogramTitle().replaceFirst("-.*", ""))
                .title(courseMaConfig.getName())
                .weappIconUrl(courseMaConfig.getImg())
                .imgUrl(content.getMiniprogramPicUrl())
                .username(courseMaConfig.getOriginalId() + "@app")
                .pagepath(content.getMiniprogramPage())
                .appid(content.getMiniprogramAppid())
                .build();
        miniProgramVo.setBase(vo);
        WxWorkResponseDTO<WxWorkSendAppMsgRespDTO> resp = ipadSendUtils.sendMiniProgram(miniProgramVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }

    }

    /**
     * 发送看课H5
     * @param vo
     * @param content
     * @param miniMap
     */
    private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content,Map<String, FsCoursePlaySourceConfig> miniMap) {
        FsCoursePlaySourceConfig courseMaConfig = miniMap.get(content.getMiniprogramAppid());
        // 小程序
        MiniProgramVo miniProgramVo = MiniProgramVo.builder()
                .desc(content.getMiniprogramTitle().replaceFirst("-.*", ""))
                .title(courseMaConfig.getName())
                .weappIconUrl(courseMaConfig.getImg())
                .imgUrl(content.getMiniprogramPicUrl())
                .username(courseMaConfig.getOriginalId() + "@app")
                .pagepath(content.getMiniprogramPage())
                .appid(content.getMiniprogramAppid())
                .build();
        miniProgramVo.setBase(vo);
        WxWorkResponseDTO<WxWorkSendAppMsgRespDTO> resp = ipadSendUtils.sendMiniProgram(miniProgramVo);
        if (resp.getErrcode() != 0) {
            log.debug("ID:{}-ipad接口请求返回异常：{}", vo.getId(), resp.getErrmsg());
            content.setSendStatus(2);
            content.setSendRemarks("发送失败：" + resp.getErrmsg());
        }
    }

    public void loginOut(QwUser user) {
        ipadSendUtils.loginOut(user.getUid(), user.getServerId());
    }

    /**
     * @Description: 生成福袋记录
     * @Param:
     * @Return:
     * @Author xgb
     * @Date 2026/2/4 11:39
     */
    private Long addLuckyBagCollectRecord(QwUser qwUser, QwSopCourseFinishTempSetting.Setting content, QwSopLogs qwSopLogs) {

        try{
            String json = configService.selectConfigByKey("course.config");
            CourseConfig config = JSON.parseObject(json, CourseConfig.class);
            Date updateTime = createUpdateTime(content, new Date(), config);
            LuckyBag luckyBag = luckyBagMapper.selectLuckyBagById(content.getLuckyBagId());
            String companyUserId = String.valueOf(qwUser.getCompanyUserId()).trim();
            String companyId = String.valueOf(qwUser.getCompanyId()).trim();
            Long businessId = addLuckyBagCollectRecord(qwUser,luckyBag,content,qwSopLogs,updateTime,companyUserId,companyId,content.getChatId());
            return businessId;
        }catch (Exception e){
            log.error("福袋创建失败：qwUser={}，qwSopLogs={}",qwUser,qwSopLogs);
            return null;
        }
    }

    /**
     * 过期时间
     *
     * @param setting
     * @param sendTime
     * @param config
     * @return
     */
    private Date createUpdateTime(QwSopCourseFinishTempSetting.Setting setting, Date sendTime, CourseConfig config) {

        Integer expireDays = (setting.getExpiresDays() == null || setting.getExpiresDays() == 0)
                ? config.getVideoLinkExpireDate()
                : setting.getExpiresDays();

//         使用 Java 8 时间 API 计算过期时间
        LocalDateTime sendDateTime = sendTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        LocalDateTime expireDateTime = sendDateTime.plusDays(expireDays - 1);
        expireDateTime = expireDateTime.toLocalDate().atTime(23, 59, 59);
        Date updateTime = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());

        return updateTime;
    }

    /**
     * 增加福袋发放记录、领取记录
     *
     * @param companyId
     * @param qwUser
     * @param luckyBag
     * @param content
     * @param qwSopLogs
     * @param sendTime
     * @param companyUserId
     * @param chatId
     */
    private Long addLuckyBagCollectRecord(QwUser qwUser, LuckyBag luckyBag, QwSopCourseFinishTempSetting.Setting content,
                                          QwSopLogs qwSopLogs,
                                          Date sendTime,
                                          String companyUserId,
                                          String companyId,
                                          String chatId) {
        try {
            // 参数校验
            if (content == null || qwSopLogs == null || sendTime == null) {
                log.error("添加福袋记录失败：必要参数为空 [content:{}, qwSopLogs:{}, sendTime:{}]",
                        content, qwSopLogs, sendTime);
                return null;
            }

            // 验证福袋ID
            if (content.getLuckyBagId() == null) {
                log.error("福袋ID为空");
                return null;
            }

            // 查询福袋信息
            if (luckyBag == null) {
                log.error("未找到对应的福袋信息 [luckyBagId:{}]", content.getLuckyBagId());
                return null;
            }

            // 检查福袋状态
            if (luckyBag.getDataStatus() != null && luckyBag.getDataStatus().equals(0)) {
                log.error("福袋被禁用 [luckyBagId:{}]", content.getLuckyBagId());
                return null;
            }

            // 查询公司信息
            Company company = (Company)redisCache.getCacheObject("companyId:"+companyId);
            if (company == null) {
                company = companyMapper.selectCompanyById(Long.valueOf(companyId));
                redisCache.setCacheObject("companyId:"+companyId, company, 1 , TimeUnit.DAYS);
                log.error("未找到对应的公司信息 [companyId:{}]", companyId);
            }

            // 构建福袋记录
            LuckyBagCollectRecord luckyBagCollectRecord = buildLuckyBagRecord(qwUser,content, qwSopLogs, sendTime,
                    companyUserId, companyId, chatId, company, luckyBag);
            luckyBagCollectRecord.setSendTime(new Date());
            luckyBagCollectRecord.setCollectType("0");
            // 插入记录并返回ID
            int result = luckyBagCollectRecordMapper.insertLuckyBagCollectRecord(luckyBagCollectRecord);
            if (result <= 0) {
                log.error("福袋记录插入失败 [luckyBagId:{}]", content.getLuckyBagId());
                return null;
            }

            // 返回新增记录的ID
            Long recordId = luckyBagCollectRecord.getId();
            if (recordId == null) {
                log.error("福袋记录插入成功但未返回ID [luckyBagId:{}]", content.getLuckyBagId());
                return null;
            }

            log.info("福袋记录添加成功 [recordId:{}, luckyBagId:{}]", recordId, content.getLuckyBagId());
            return recordId;

        } catch (NumberFormatException e) {
            log.error("ID转换失败 [companyId:{}, companyUserId:{}]", companyId, companyUserId, e);
            return null;
        } catch (Exception e) {
            log.error("ID:" + (content != null ? content.getLuckyBagId() : "unknown") + "-添加福袋记录失败", e);
            return null;
        }
    }


    /**
     * 构建福袋记录对象
     */
    private LuckyBagCollectRecord buildLuckyBagRecord(QwUser qwUser,QwSopCourseFinishTempSetting.Setting content,
                                                      QwSopLogs qwSopLogs,
                                                      Date sendTime,
                                                      String companyUserId,
                                                      String companyId,
                                                      String chatId,
                                                      Company company,
                                                      LuckyBag luckyBag) {
        LuckyBagCollectRecord record = new LuckyBagCollectRecord();
        record.setQwUserId(qwUser.getQwUserId());
        record.setQwUserName(qwUser.getQwUserName());
        record.setLuckyBagId(content.getLuckyBagId());
        record.setExpiryTime(sendTime);
        record.setCollectType("3");
        record.setCompanyId(Long.valueOf(companyId));
        record.setUserId(qwSopLogs.getFsUserId());
        if (ObjectUtil.isNotEmpty(qwSopLogs.getFsUserId())){
            FsUser fsUser = fsUserMapper.selectFsUserByUserId(qwSopLogs.getFsUserId());
            record.setUserName(ObjectUtil.isNotEmpty(fsUser)?fsUser.getNickName():null);
        }
        record.setCompanyName(company.getCompanyName());
        record.setCompanyUserId(Long.valueOf(companyUserId));
        record.setSendLink(content.getMiniprogramPage());

        // 设置奖励类型和聊天信息
        if (StringUtils.isNotEmpty(chatId)) {
            record.setRewardType(1L);
            record.setChatId(chatId);
            record.setExternalUserName(qwSopLogs.getExternalUserName());
        } else {
            record.setRewardType(2L);
        }

        // 设置币种金额
        if (luckyBag.getRewardType() != null && luckyBag.getRewardType().equals("1")) {
            record.setCoinAmount(luckyBag.getAmount());
        }

        return record;
    }

}
