package com.fs.app.service;

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.CompanyDept;
import com.fs.company.domain.CompanyMiniapp;
import com.fs.company.domain.CompanyUser;
import com.fs.company.mapper.CompanyDeptMapper;
import com.fs.company.mapper.CompanyUserMapper;
import com.fs.company.service.ICompanyMiniappService;
import com.fs.course.domain.FsCoursePlaySourceConfig;
import com.fs.course.domain.FsCourseWatchLog;
import com.fs.course.domain.FsUserCourse;
import com.fs.course.domain.FsUserCourseVideo;
import com.fs.course.mapper.FsCoursePlaySourceConfigMapper;
import com.fs.course.mapper.FsUserCourseMapper;
import com.fs.course.mapper.FsUserCourseVideoMapper;
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.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.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.wxwork.dto.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
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 CompanyUserMapper companyUserMapper;
    private final CompanyDeptMapper companyDeptMapper;
    private final IFsCoursePlaySourceConfigService playSourceConfigService;
    private final FsUserMapper fsUserMapper;
    private final FsUserCourseVideoMapper fsUserCourseVideoMapper;
    private final FsUserCourseMapper fsUserCourseMapper;


    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, Long companyUserId) {
        log.info("ID:{} 小程序发送前关键字段 - miniprogramPage:{}, miniprogramTitle:{}, miniprogramAppid:{}, articleUrl:{}",
            vo.getId(), content.getMiniprogramPage(), content.getMiniprogramTitle(), content.getMiniprogramAppid(), content.getArticleUrl());

        // 发送参数原本的appid
        String appid = content.getMiniprogramAppid();
        List<CompanyMiniapp> listAll = Collections.emptyList();
        Set<String> companyAppIds = Collections.emptySet();
        boolean forceCompanyApp = false;
        if (companyId != null && miniMap != null && !miniMap.isEmpty()) {
            companyAppIds = miniMap.values().stream()
                    .filter(Objects::nonNull)
                    .filter(cfg -> isConfigBoundToCompany(cfg, companyId))
                    .map(FsCoursePlaySourceConfig::getAppid)
                    .filter(StringUtils::isNotEmpty)
                    .collect(Collectors.toSet());
            forceCompanyApp = !companyAppIds.isEmpty();
        }
        final Set<String> allowedCompanyAppIds = companyAppIds;
        if (companyId != null) {
            listAll = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId));
        }
        // 判断销售工时ID不为空并且有小程序类型
        if (companyId != null && content.getMiniType() != null) {
            String resolvedAppId = resolveAppIdByType(companyUserId, content.getMiniType(), listAll);
            if (StringUtils.isNotEmpty(resolvedAppId)) {
                appid = resolvedAppId;
            }
            // 获取配置文件里面的项目名称
            //区分新老用户，新用户发送备用小程序，老用户发送主小程序，TODO 需要使用的项目在参数里面加上
            log.info("ID:{}, qwUserId:{}，externalId:{}，进入区分发小程序逻辑", vo.getId(), vo.getQwUserId(), vo.getExId());
            // 判断消息是否是群聊，不是群聊再进去，TODO 当权无法判断群聊是否可以发送对应的小程序
            if (!vo.isRoom()) {
                log.info("ID:{}, qwUserId:{}，externalId:{}，不是群聊", vo.getId(), 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(","));
                            Map<String, Integer> miniSortMap = new HashMap<>();
                            for (int i = 0; i < miniAppList.size(); i++) {
                                miniSortMap.put(miniAppList.get(i), i);
                            }
                            // 根据小程序ID查询小程序列表
                            List<FsCoursePlaySourceConfig> configList = playSourceConfigService.selectByAppIds(miniAppList);
                            if (forceCompanyApp) {
                                configList = configList.stream()
                                        .filter(e -> allowedCompanyAppIds.contains(e.getAppid()))
                                        .collect(Collectors.toList());
                            }
                            configList.sort(Comparator.comparing(e -> miniSortMap.getOrDefault(e.getAppid(), 100)));
                            // 过滤掉完全封禁(status=2)的小程序，只保留正常(status=0)和半封禁(status=1)的小程序
                            List<FsCoursePlaySourceConfig> availableList = configList.stream()
                                    .filter(e -> e.getStatus() == null || e.getStatus() != 2)
                                    .collect(Collectors.toList());
                            // 筛选出半封禁的小程序数据，得到这个数据然后优先发这个小程序
                            Optional<FsCoursePlaySourceConfig> optional = availableList.stream().filter(e -> e.getStatus() != null && e.getStatus() == 1).findFirst();
                            Optional<FsCoursePlaySourceConfig> optional2 = availableList.stream().filter(e -> e.getStatus() != null && e.getStatus() == 0).findFirst();
                            if (miniAppList.size() == 1) {
                                // 用户只看过一个小程序，检查是否可用
                                if (!availableList.isEmpty()) {
                                    appid = availableList.get(0).getAppid();
                                    log.info("ID:{}, qwUserId:{}，externalId:{}，判断看过一个小程序：{}", vo.getId(), vo.getQwUserId(), vo.getExId(), appid);
                                } else {
                                    String backupAppId = resolveAppIdByType(companyUserId, 1, listAll);
                                    if (StringUtils.isNotEmpty(backupAppId)) {
                                        appid = backupAppId;
                                        log.info("ID:{}, qwUserId:{}，externalId:{}，看过的小程序已封禁，使用备用小程序:{}", vo.getId(), vo.getQwUserId(), vo.getExId(), appid);
                                    }
                                }
                            } else {
                                // 判断是否找到半封禁
                                if (optional.isPresent()) {
                                    // 找到半封禁并且在appid里面存在证明这个客户是打开过小程序，优先发送这个
                                    appid = optional.get().getAppid();
                                    log.info("ID:{}, qwUserId:{}，externalId:{}，找到半封禁小程序:{}", vo.getId(), vo.getQwUserId(), vo.getExId(), appid);
                                } else if (optional2.isPresent()) {
                                    appid = optional2.get().getAppid();
                                    log.info("ID:{}, qwUserId:{}，externalId:{}，找到正常小程序:{}", vo.getId(), vo.getQwUserId(), vo.getExId(), appid);
                                } else {
                                    String backupAppId = resolveAppIdByType(companyUserId, 1, listAll);
                                    if (StringUtils.isNotEmpty(backupAppId)) {
                                        appid = backupAppId;
                                        log.info("ID:{}, qwUserId:{}，externalId:{}，找到备用小程序:{}", vo.getId(), vo.getQwUserId(), vo.getExId(), appid);
                                    } else {
                                        log.info("ID:{}, qwUserId:{}，externalId:{}，没找到小程序:{}", vo.getId(), vo.getQwUserId(), vo.getExId(), appid);
                                    }
                                }
                            }
                        } 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, 10, 23, 59, 59);
                            // 判断客户创建时间是在这个之前还是之后
                            int listIndex = createTime.isAfter(lastTime) ? 1 : 0;
                            String resolvedByTime = resolveAppIdByType(companyUserId, listIndex, listAll);
                            if (StringUtils.isNotEmpty(resolvedByTime)) {
                                appid = resolvedByTime;
                                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());
                }
            }
        }
        if (forceCompanyApp && StringUtils.isNotEmpty(appid) && !allowedCompanyAppIds.contains(appid)) {
            String deptAppId = resolveDeptMiniAppId(companyUserId, content.getMiniType());
            Optional<String> preferredAppId = Optional.empty();
            if (StringUtils.isNotEmpty(deptAppId) && allowedCompanyAppIds.contains(deptAppId)) {
                preferredAppId = Optional.of(deptAppId);
            }
            if (!preferredAppId.isPresent()) {
                preferredAppId = listAll.stream()
                        .filter(e -> e.getType().equals(content.getMiniType()) && StringUtils.isNotEmpty(e.getAppId()))
                        .map(CompanyMiniapp::getAppId)
                        .filter(allowedCompanyAppIds::contains)
                        .findFirst();
            }
            if (!preferredAppId.isPresent()) {
                preferredAppId = allowedCompanyAppIds.stream().findFirst();
            }
            if (preferredAppId.isPresent()) {
                appid = preferredAppId.get();
                log.info("ID:{}, companyId:{}，命中公司隔离规则，改用本公司小程序:{}", vo.getId(), companyId, appid);
            }
        }
        FsCoursePlaySourceConfig courseMaConfig = miniMap.get(appid);
        if (forceCompanyApp && courseMaConfig != null && !isConfigBoundToCompany(courseMaConfig, companyId)) {
            courseMaConfig = null;
        }
        if (courseMaConfig == null) {
            String backupAppId = resolveAppIdByType(companyUserId, 1, listAll);
            if (StringUtils.isNotEmpty(backupAppId)) {
                courseMaConfig = miniMap.get(backupAppId);
            }
        }
        if (courseMaConfig == null && forceCompanyApp) {
            Optional<FsCoursePlaySourceConfig> companyConfig = miniMap.values().stream()
                    .filter(Objects::nonNull)
                    .filter(cfg -> StringUtils.isNotEmpty(cfg.getAppid()))
                    .filter(cfg -> allowedCompanyAppIds.contains(cfg.getAppid()))
                    .findFirst();
            if (companyConfig.isPresent()) {
                courseMaConfig = companyConfig.get();
                appid = courseMaConfig.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();
        log.info("ID:{} 构造小程序消息体 - desc:{}, title:{}, imgUrl:{}, pagepath:{}, appid:{}",
            vo.getId(), miniProgramVo.getDesc(), miniProgramVo.getTitle(), miniProgramVo.getImgUrl(), miniProgramVo.getPagepath(), miniProgramVo.getAppid());
        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 String resolveDeptMiniAppId(Long companyUserId, Integer miniType) {
        if (companyUserId == null || miniType == null) {
            return null;
        }
        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(companyUserId);
        if (companyUser == null || companyUser.getDeptId() == null) {
            return null;
        }
        CompanyDept dept = companyDeptMapper.selectCompanyDeptById(companyUser.getDeptId());
        if (dept == null) {
            return null;
        }
        if (miniType == 0) {
            return dept.getMiniAppMaster();
        }
        if (miniType == 1) {
            return dept.getMiniAppServer();
        }
        return null;
    }

    private String resolveAppIdByType(Long companyUserId, Integer miniType, List<CompanyMiniapp> companyMiniapps) {
        if (miniType == null) {
            return null;
        }
        String deptAppId = resolveDeptMiniAppId(companyUserId, miniType);
        if (StringUtils.isNotEmpty(deptAppId)) {
            return deptAppId;
        }
        if (companyMiniapps == null || companyMiniapps.isEmpty()) {
            return null;
        }
        return companyMiniapps.stream()
                .filter(e -> miniType.equals(e.getType()) && StringUtils.isNotEmpty(e.getAppId()))
                .map(CompanyMiniapp::getAppId)
                .findFirst()
                .orElse(null);
    }

    private boolean isConfigBoundToCompany(FsCoursePlaySourceConfig config, Long companyId) {
        if (config == null || companyId == null || StringUtils.isEmpty(config.getCompanyIds())) {
            return false;
        }
        String targetCompanyId = String.valueOf(companyId);
        return Arrays.stream(config.getCompanyIds().split(","))
                .map(String::trim)
                .anyMatch(targetCompanyId::equals);
    }

    /**
     * 业务字段在 {@code content} 嵌套 JSON 里；未合并时 fileUrl/fileName 为空，
     * 发送文件会退回用 URL 路径名（如 1775377962733.xlsx）作为展示名。
     */
    private void applyNestedContentIfPresent(QwSopCourseFinishTempSetting.Setting content) {
        if (content == null || StringUtils.isEmpty(content.getContent())) {
            return;
        }
        try {
            JSONObject inner = JSON.parseObject(content.getContent());
            if (inner == null || inner.isEmpty()) {
                return;
            }
            if (StringUtils.isEmpty(content.getFileUrl()) && StringUtils.isNotEmpty(inner.getString("fileUrl"))) {
                content.setFileUrl(inner.getString("fileUrl"));
            }
            if (StringUtils.isEmpty(content.getFileName())) {
                String fn = inner.getString("fileName");
                if (StringUtils.isEmpty(fn)) {
                    fn = inner.getString("value");
                }
                if (StringUtils.isNotEmpty(fn)) {
                    content.setFileName(fn);
                }
            }
            if (StringUtils.isEmpty(content.getImgUrl()) && StringUtils.isNotEmpty(inner.getString("imgUrl"))) {
                content.setImgUrl(inner.getString("imgUrl"));
            }
            if (StringUtils.isEmpty(content.getVideoUrl()) && StringUtils.isNotEmpty(inner.getString("videoUrl"))) {
                content.setVideoUrl(inner.getString("videoUrl"));
            }
            if (StringUtils.isEmpty(content.getVoiceUrl()) && StringUtils.isNotEmpty(inner.getString("voiceUrl"))) {
                content.setVoiceUrl(inner.getString("voiceUrl"));
            }
            if (StringUtils.isEmpty(content.getValue()) && StringUtils.isNotEmpty(inner.getString("value"))) {
                content.setValue(inner.getString("value"));
            }
            if (StringUtils.isEmpty(content.getContentType()) && inner.get("contentType") != null) {
                content.setContentType(String.valueOf(inner.get("contentType")));
            }
        } catch (Exception e) {
            log.warn("applyNestedContentIfPresent parse failed: {}", e.getMessage());
        }
    }

    private void sendFile(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
        FileVo fileVo = FileVo.builder()
                .url(content.getFileUrl())
                .fileName(content.getFileName())  // 传递文件名
                .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());
        }
    }

    /**
     * 发送前兜底：按前端逻辑补齐空字段
     * linkTitle=课程名，linkDescribe=课节名，linkImageUrl=课程封面图
     */
    private void fillLinkCardDefaultsBeforeSend(QwSopCourseFinishTempSetting.Setting content, QwSopLogs qwSopLogs) {
        if (content == null) {
            return;
        }
        if (StringUtils.isNotEmpty(content.getLinkTitle())
                && StringUtils.isNotEmpty(content.getLinkDescribe())
                && StringUtils.isNotEmpty(content.getLinkImageUrl())) {
            return;
        }
        try {
            if (qwSopLogs == null || StringUtils.isEmpty(qwSopLogs.getContentJson())) {
                return;
            }
            JSONObject root = JSON.parseObject(qwSopLogs.getContentJson());
            if (root == null) {
                return;
            }
            Long courseId = root.getLong("courseId");
            Long videoId = root.getLong("videoId");
            if (courseId != null && (StringUtils.isEmpty(content.getLinkTitle()) || StringUtils.isEmpty(content.getLinkImageUrl()))) {
                FsUserCourse course = fsUserCourseMapper.selectFsUserCourseByCourseId(courseId);
                if (course != null) {
                    if (StringUtils.isEmpty(content.getLinkTitle()) && StringUtils.isNotEmpty(course.getCourseName())) {
                        content.setLinkTitle(course.getCourseName());
                    }
                    if (StringUtils.isEmpty(content.getLinkImageUrl()) && StringUtils.isNotEmpty(course.getImgUrl())) {
                        content.setLinkImageUrl(course.getImgUrl());
                    }
                }
            }
            if (videoId != null && StringUtils.isEmpty(content.getLinkDescribe())) {
                String videoTitle = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoForTitle(videoId);
                if (StringUtils.isNotEmpty(videoTitle)) {
                    content.setLinkDescribe(videoTitle);
                }
            }
        } catch (Exception e) {
            log.warn("发送前补齐链接卡片失败 logId={}", qwSopLogs != null ? qwSopLogs.getId() : null, e);
        }
    }

    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(), vo.getId());
    }

    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()));
        // sendType=6为群聊，sendType=21为群公告（也是群聊）
        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);
            applyNestedContentIfPresent(content);
            // 如果是群公告，使用SendNotice接口
            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":
                case "17":
                case "18":
                    // 链接卡片：3=H5，17=飞书看课，18=飞书NEW
                    fillLinkCardDefaultsBeforeSend(content, qwSopLogs);
                    sendLink(vo, content);
                    break;
                case "4":
                case "10":
                case "13":
                    sendMiniProgram(vo, content, miniMap, qwUser.getCompanyId(), qwUser.getCompanyUserId());
                    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 "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());
        }
    }

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