package com.fs.sop.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fs.common.core.domain.R;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.utils.PubFun;
import com.fs.common.utils.date.DateUtil;
import com.fs.course.domain.FsCourseWatchLog;
import com.fs.course.mapper.FsCourseWatchLogMapper;
import com.fs.course.param.FsCourseLinkCreateParam;
import com.fs.course.service.IFsCourseLinkService;
import com.fs.crm.domain.CrmMsg;
import com.fs.crm.mapper.CrmMsgMapper;
import com.fs.qw.domain.QwExternalContact;
import com.fs.qw.domain.QwGroupChat;
import com.fs.qw.domain.QwUser;
import com.fs.qw.mapper.QwExternalContactMapper;
import com.fs.qw.mapper.QwUserMapper;
import com.fs.qw.param.SopUserLogsVO;
import com.fs.qw.service.IQwGroupChatService;
import com.fs.qw.vo.QwSopRuleTimeVO;
import com.fs.qw.vo.QwSopTempSetting;
import com.fs.qwApi.service.QwApiService;
import com.fs.sop.domain.QwSopLogs;
import com.fs.sop.domain.SopUserLogs;
import com.fs.sop.domain.SopUserLogsInfo;
import com.fs.sop.mapper.QwSopMapper;
import com.fs.sop.mapper.SopUserLogsInfoMapper;
import com.fs.sop.mapper.SopUserLogsMapper;
import com.fs.sop.params.QwRatingConfig;
import com.fs.sop.params.SopUserLogsList;
import com.fs.sop.params.SopUserLogsParam;
import com.fs.sop.params.SopUserLogsParamByDate;
import com.fs.sop.service.IQwSopLogsService;
import com.fs.sop.service.IQwSopTempDayService;
import com.fs.sop.service.ISopUserLogsService;
import com.fs.sop.vo.QwRatingVO;
import com.fs.sop.vo.SopUserLogsInfoVo;
import com.fs.sop.vo.SopUserLogsVo;
import com.fs.system.service.ISysConfigService;
import com.fs.voice.utils.StringUtil;
import com.google.common.util.concurrent.AtomicDouble;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SopUserLogsServiceImpl  implements ISopUserLogsService {

    private static final Logger logger = LoggerFactory.getLogger(SopUserLogsServiceImpl.class);

    @Autowired
    private SopUserLogsMapper sopUserLogsMapper;

    @Autowired
    private SopUserLogsInfoMapper sopUserLogsInfoMapper;

    @Autowired
    private QwSopMapper sopMapper;
    @Autowired
    private QwApiService qwApiService;
    @Autowired
    private IQwGroupChatService qwGroupChatService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IQwSopLogsService qwSopLogsService;

    @Autowired
    private QwExternalContactMapper qwExternalContactMapper;

    @Autowired
    private IQwSopTempDayService qwSopTempDayService;

    @Autowired
    private ISopUserLogsService sopUserLogsService;

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private IFsCourseLinkService iFsCourseLinkService;
    @Autowired
    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
    @Autowired
    private QwUserMapper qwUserMapper;
    @Autowired
    private CrmMsgMapper crmMsgMapper;

    @Override
    public int insertSopUserLogsByList(SopUserLogsList userLogsList) {
        return sopUserLogsMapper.insertSopUserLogsByList(userLogsList);
    }

    @Override
    public int insertSopUserLogsByDate(SopUserLogsParamByDate logsParamByDate) {
        return sopUserLogsMapper.insertSopUserLogsByDate(logsParamByDate);
    }


//    @Override
//    public int updateSopUserLogsDistinct(SopUserLogs sopUserLogs) {
//        return sopUserLogsMapper.updateSopUserLogsDistinct(sopUserLogs);
//    }

//    @Override
//    public int updateSopUserLogsDistinctByList(SopUserLogsList userLogsList) {
//        return sopUserLogsMapper.updateSopUserLogsDistinctByList(userLogsList);
//    }

//    @Override
//    public int updateSopUserLogsDistinctByList(SopUserLogsArray userLogsArray) {
//        return sopUserLogsMapper.updateSopUserLogsDistinctByList(userLogsArray);
//    }

    @Override
    public int deleteSopUserLogsBySopId(String sopId) {
        return sopUserLogsMapper.deleteSopUserLogsBySopId(sopId);
    }

    @Override
    public void selectSopUserLogsListByTime() {
        // 获取当前时间
        LocalDate currentDate = LocalDate.now(); // 假设当前日期是 2021-11-27

        // 当前时间
        LocalDateTime currentDateTime = LocalDateTime.now();

        List<SopUserLogsVo> sopUserLogsVos = sopUserLogsMapper.selectSopUserLogsListByTime();
        // 创建一个 Map，用于分组：key 是sop_id，value 是对应的 QwSopLogs 列表
        Map<String, List<SopUserLogsVo>> stringListMap = sopUserLogsVos.stream()
                .collect(Collectors.groupingBy(SopUserLogsVo::getSopId));

        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

        // 遍历分组数据
        for (Map.Entry<String, List<SopUserLogsVo>> entry : stringListMap.entrySet()) {
            String key = entry.getKey();

            QwSopRuleTimeVO ruleTimeVO = sopMapper.selectQwSopByClickHouseId(key);

            //如果为空，则任务删除了 ，则全部营期删除
            if (ruleTimeVO == null) {
                sopUserLogsMapper.deleteSopUserLogsBySopId(key);
            }else if (ruleTimeVO.getStatus()==0){
                SopUserLogs sopUserLogs = new SopUserLogs();
                sopUserLogs.setSopId(key);
                sopUserLogs.setStatus(2);
                sopUserLogsMapper.updateSopUserLogsByStatus(sopUserLogs);
            }else {

                //如果模板停用了，也改这个营期记录
                if (ruleTimeVO.getTempStatus().equals("0")){
                    SopUserLogs sopUserLogs = new SopUserLogs();
                    sopUserLogs.setSopId(key);
                    sopUserLogs.setStatus(2);
                    sopUserLogsMapper.updateSopUserLogsByStatus(sopUserLogs);
                }else {

                    // 分组的 key
                    List<SopUserLogsVo> userLogsVos = entry.getValue(); // 分组对应的日志列表

                    // 处理日志数据
                    userLogsVos.forEach(log -> {

                            // 获取开始时间和间隔天数
                            Date startTime = null; // 假设 startTime 是 java.util.Date 类型
                            try {
                                startTime = formatter.parse(log.getStartTime());
                            } catch (ParseException e) {
                                logger.error("selectSopUserLogsListByTime解析开始时间失败: {}", e.getMessage());
                            }

                            Integer tempGap = ruleTimeVO.getTempGap();

                            // 将 startTime 转换为 LocalDate
                            LocalDate startLocalDate = startTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

                            // 计算当前时间距离开始时间的天数差
                            long daysBetween = ChronoUnit.DAYS.between(startLocalDate, currentDate);

                            // TODO 修改
                            // 获取 QwSopTempSetting 内容
//                            List<QwSopTempSetting> qwSopTempSettings = JSON.parseArray(ruleTimeVO.getTempSetting(), QwSopTempSetting.class);
//
//                            // 计算当前天数属于第几天，第一天就是 startLocalDate，后续递增
//                            int intervalDay = (int) (daysBetween / tempGap); // 获取间隔天数的序号
//
//                            // 判断间隔天数是否存在对应的内容
//                            if (intervalDay < qwSopTempSettings.size()) {
//                                // 获取对应间隔天数的内容
//                                QwSopTempSetting selectedSetting = qwSopTempSettings.get(intervalDay);
//                                List<QwSopTempSetting.Content> content = selectedSetting.getContent();
//
//                                // 遍历内容，找到所有接近目标时间的记录
//                                for (QwSopTempSetting.Content element : content) {
//
//                                    // 将 element 的时间转换为 LocalDateTime（假设时间格式是 "HH:mm"）
//                                    LocalTime elementLocalTime = LocalTime.parse(element.getTime());
//                                    LocalDateTime elementDateTime = LocalDateTime.of(currentDateTime.toLocalDate(), elementLocalTime);
//
//                                    // 动态调整 elementDateTime 的日期
//                                    if (elementLocalTime.isBefore(currentDateTime.toLocalTime())) {
//                                        elementDateTime = elementDateTime.plusDays(1);
//                                    }
//
//                                    // 定义时间范围：当前时间 +20 分钟 到 +40 分钟
//                                    LocalDateTime startRange = currentDateTime.plusMinutes(20); // 2024-12-16 23:40
//                                    LocalDateTime endRange = startRange.plusMinutes(20);        // 2024-12-17 00:00
//
//                                    // 跨天逻辑修正：仅当 startRange 的时间晚于 endRange 的时间时调整
//                                    if (startRange.toLocalTime().isAfter(endRange.toLocalTime())
//                                            && startRange.toLocalDate().equals(endRange.toLocalDate())) {
//                                        endRange = endRange.plusDays(1); // 将 endRange 调整为第二天
//                                    }
//
//                                    // 检查 elementTimeStr 是否在范围内
//                                    if (!elementDateTime.isBefore(startRange) && elementDateTime.isBefore(endRange)) {
//
//                                        // 如果时间差在目标范围内，更新记录
//                                        // 组合年月日和element的时间
//                                        LocalTime elementTime = LocalTime.parse(element.getTime()); // 例如 "16:20"
//
//                                        // 计算目标日期：startTime + (intervalDay * tempGap) 天
//                                        // 第一条数据是 startLocalDate，之后的日期按 tempGap 递增
//                                        LocalDate targetDate = startLocalDate.plusDays(intervalDay * tempGap);
//
//                                        // 将 targetDate 和 elementTime 组合成 LocalDateTime
//                                        LocalDateTime dateTime = LocalDateTime.of(targetDate, elementTime);
//
//                                        // 将 LocalDateTime 转换为 Date
//                                        Date sendTime = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
//
//                                        // 查询营期里的客
//                                        SopUserLogsInfo userLogsInfo=new SopUserLogsInfo();
//                                        userLogsInfo.setSopId(log.getSopId());
//                                        userLogsInfo.setUserLogsId(log.getId());
//
//                                        List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoList(userLogsInfo);
////                                        List<SopUserLogsInfo> sopUserLogsInfos = sopUserLogsInfoMapper.selectSopUserLogsInfoListBySopId(log.getSopId(),log.getId());
//                                        insertSopUserLogs(sopUserLogsInfos,log,sendTime,ruleTimeVO,element);
//
//                                    }
//                                }
//
//                            }

                    });


                }


            }





        }

        // 遍历分组数据
//        for (Map.Entry<String, List<SopUserLogsVo>> entry : stringListMap.entrySet()) {
//            String key = entry.getKey();
//
//            QwSopRuleTimeVO ruleTimeVO = sopMapper.selectQwSopByClickHouseId(key);
//
//            //如果为空，则任务删除了 ，则全部营期删除
//            if (ruleTimeVO == null) {
//                sopUserLogsMapper.deleteSopUserLogsBySopId(key);
//            }else if (ruleTimeVO.getStatus()==0){
//                SopUserLogs sopUserLogs = new SopUserLogs();
//                sopUserLogs.setSopId(key);
//                sopUserLogs.setStatus(2);
//                sopUserLogsMapper.updateSopUserLogsByStatus(sopUserLogs);
//            }else {
//
//                //如果模板停用了，也改这个营期记录
//                if (ruleTimeVO.getTempStatus().equals("0")){
//                    SopUserLogs sopUserLogs = new SopUserLogs();
//                    sopUserLogs.setSopId(key);
//                    sopUserLogs.setStatus(2);
//                    sopUserLogsMapper.updateSopUserLogsByStatus(sopUserLogs);
//                }else {
//
//                    // 分组的 key
//                    List<SopUserLogsVo> userLogsVos = entry.getValue(); // 分组对应的日志列表
//
//
//                    // 处理日志数据
//                    userLogsVos.forEach(log -> {
//
//                        //获取企业微信接口的客户列表
//                        QwExternalContactListResult listResult = qwApiService.getExternalcontactList(log.getQwUserId(), log.getCorpId());
//
//                        if (listResult.getErrcode()!=0) {
//                            logger.error("selectSopUserLogsListByTime获取企业微信接口的客户列表失败: {}", listResult.getErrmsg());
//                        }else {
//
//                            // 获取开始时间和间隔天数
//                            Date startTime = null; // 假设 startTime 是 java.util.Date 类型
//                            try {
//                                startTime = formatter.parse(log.getStartTime());
//                            } catch (ParseException e) {
//                                logger.error("selectSopUserLogsListByTime解析开始时间失败: {}", e.getMessage());
//                            }
//
//                            Integer tempGap = ruleTimeVO.getTempGap();
//
//                            // 将 startTime 转换为 LocalDate
//                            LocalDate startLocalDate = startTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
//
//                            // 获取当前时间
//                            LocalDate currentDate = LocalDate.now(); // 假设当前日期是 2021-11-27
//
//                            LocalTime currentTime = LocalTime.now(); // 获取当前时间
//
//                            // 计算当前时间距离开始时间的天数差
//                            long daysBetween = ChronoUnit.DAYS.between(startLocalDate, currentDate);
//
//                            // 获取 QwSopTempSetting 内容
//                            List<QwSopTempSetting> qwSopTempSettings = JSON.parseArray(ruleTimeVO.getTempSetting(), QwSopTempSetting.class);
//
//                            // 计算当前天数属于第几天，第一天就是 startLocalDate，后续递增
//                            int intervalDay = (int) (daysBetween / tempGap); // 获取间隔天数的序号
//
//                            // 判断间隔天数是否存在对应的内容
//                            if (intervalDay < qwSopTempSettings.size()) {
//                                // 获取对应间隔天数的内容
//                                QwSopTempSetting selectedSetting = qwSopTempSettings.get(intervalDay);
//                                List<QwSopTempSetting.Content> content = selectedSetting.getContent();
//
//                                // 计算当前时间加一个小时后的目标时间
//                                LocalTime targetTime = currentTime.plusHours(1);
//
//
//                                // 遍历内容，找到所有接近目标时间的记录
//                                for (QwSopTempSetting.Content element : content) {
//
//
//                                    String timeStr = element.getTime(); // 假设 time 是 "HH:mm" 格式
//                                    LocalTime elementTimeStr = LocalTime.parse(timeStr); // 将 timeStr 转换为 LocalTime
//
//                                    // 计算当前 element 的时间和目标时间的时间差
//                                    long timeDifference = ChronoUnit.MINUTES.between(currentTime, elementTimeStr);
//
//                                    // 如果时间差在目标范围内，更新记录
//                                    if (timeDifference >= 0 && timeDifference < 60) {
////                            if (timeDifference <= 60) {
//                                        // 组合年月日和element的时间
//                                        String time = element.getTime(); // 例如 "16:20"
//                                        LocalTime elementTime = LocalTime.parse(time);
//
//                                        // 计算目标日期：startTime + (intervalDay * tempGap) 天
//                                        // 第一条数据是 startLocalDate，之后的日期按 tempGap 递增
//                                        LocalDate targetDate = startLocalDate.plusDays(intervalDay * tempGap);
//
//                                        // 将 targetDate 和 elementTime 组合成 LocalDateTime
//                                        LocalDateTime dateTime = LocalDateTime.of(targetDate, elementTime);
//
//                                        // 将 LocalDateTime 转换为 Date
//                                        Date sendTime = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
//
//
//                                        if (listResult.getErrcode() == 0) {
//                                            //成员的客户
//                                            List<String> externalUserid = listResult.getExternal_userid();
//                                            // 营期里的客户（转换为可修改集合）
//                                            List<String> externalContactId = new ArrayList<>(log.getExternalContactId());
//                                            // 过滤 externalContactId 中不匹配的元素
//                                            externalContactId.removeIf(contactId -> {
//                                                String actualUserId = contactId.trim().split("\\|")[0];// 按 | 分隔
//                                                return externalUserid.stream().noneMatch(externalUser -> externalUser.trim().equals(actualUserId.trim()));
//                                            });
//
//                                            // 如果 externalContactId 已经与 externalUserid 一致，则不需要更新
//                                            if (!externalContactId.isEmpty() && externalContactId.size() != log.getExternalContactId().size()) {
//                                                // 更新营期客户列表(去空格T_T)
//                                                SopUserLogsUpdateList userLogsList = new SopUserLogsUpdateList();
//                                                userLogsList.setId(log.getId());
//                                                userLogsList.setExternalContactId(externalContactId.stream()
//                                                        .map(String::trim)
//                                                        .toArray(String[]::new));
//                                                sopUserLogsMapper.updateSopUserLogsByexternalContactId(userLogsList);
//
//                                                insertSopUserLogs(externalContactId,log,sendTime,ruleTimeVO,element);
//                                            } else {
//                                                // 如果没有变化，跳过更新
//                                                insertSopUserLogs(log.getExternalContactId(),log,sendTime,ruleTimeVO,element);
//                                            }
//
//                                        }
//                                    }
//                                }
//
//                            }
//
//                        }
//
//
//
//
//                    });
//
//
//                }
//
//
//            }
//
//
//
//
//
//        }
    }

    private void insertSopUserLogs(List<SopUserLogsInfo> list,SopUserLogsVo log,Date sendTime,QwSopRuleTimeVO ruleTimeVO,QwSopTempSetting.Content element) {

        QwSopLogs sopLogs = new QwSopLogs();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        int type = element.getType();
        Long courseId = element.getCourseId();
        Long videoId = element.getVideoId();

        list.forEach(contactId -> {

            sopLogs.setSendTime(sdf.format(sendTime));
            sopLogs.setQwUserid(log.getQwUserId());
            sopLogs.setCorpId(log.getCorpId());
            sopLogs.setLogType(ruleTimeVO.getType());
            sopLogs.setSendType(ruleTimeVO.getSendType());
            sopLogs.setSendStatus(3L);
            sopLogs.setReceivingStatus(0L);

            String[] userKey = log.getUserId().split("\\|");
            String qwUserId = userKey[0].trim();
            String companyUserId = userKey[1].trim();
            String companyId = userKey[2].trim();
            sopLogs.setCompanyId(Long.valueOf(companyId));
            sopLogs.setSopId(log.getSopId());

            sopLogs.setExternalUserId(contactId.getExternalContactId());
            sopLogs.setExternalUserName(contactId.getExternalUserName());
            sopLogs.setFsUserId(contactId.getFsUserId());

//                                             消息类型：普通 课程 订单
            switch (type) {
                case 1: // 普通消息
                    sopLogs.setContentJson(JSON.toJSONString(element));

                    // 处理 log
                    qwSopLogsService.insertQwSopLogs(sopLogs);
                    break;
                case 2: // 课程消息
//                    QwSopTempSetting.Content clone = element.clone();
//
//                    for (QwSopTempSetting.Content.Setting item : clone.getSetting()) {
//                        // 如果有添加
//                        if (item.getIsBindUrl() != null && item.getIsBindUrl().equals("1")) {
//                            FsCourseLinkCreateParam param = new FsCourseLinkCreateParam();
//                            param.setVideoId((long) videoId);
//                            param.setQwUserId(String.valueOf(qwUserId));
//                            param.setDays(item.getExpiresDays());
//                            param.setCorpId(log.getCorpId());
//                            param.setCourseId(Long.valueOf(courseId));
//                            param.setCompanyUserId(Long.valueOf(companyUserId));
//                            param.setCompanyId(Long.valueOf(companyId));
//                            param.setQwExternalId(Long.valueOf(externalId));
//                            param.setSendTime(sendTime);
//
//                            // 短链
//                            R linkUrl = iFsCourseLinkService.createLinkUrl(param);
//
//                            // 如果是课程 且 消息类型是链接类型
//                            if (item.getContentType().equals("3")) {
//                                item.setLinkUrl(linkUrl.get("url").toString());
//                            } else {
//                                item.setValue(item.getValue() + "\n" + linkUrl.get("url"));
//                            }
//
//
//                            //如果要推短链-则必须要绑定了小程序-才会有观看记录
//                            if (sopLogs.getFsUserId() != null) {
//
//                                FsCourseWatchLog watchLog = new FsCourseWatchLog();
//                                watchLog.setUserId(sopLogs.getFsUserId());
//                                watchLog.setVideoId(Long.valueOf(videoId));
//                                watchLog.setQwExternalContactId(Long.valueOf(externalId));
//                                watchLog.setSendType(2);
//                                watchLog.setQwUserId(String.valueOf(qwUserId));
//                                watchLog.setCourseId(Long.valueOf(courseId));
//                                watchLog.setCompanyUserId(Long.valueOf(companyUserId));
//                                watchLog.setCompanyId(Long.valueOf(companyId));
//
//
//                                List<FsCourseWatchLog> fsCourseWatchLogs = fsCourseWatchLogMapper.selectFsCourseWatchLogList(watchLog);
//                                if (fsCourseWatchLogs.isEmpty()) {
//                                    watchLog.setLogType(3);
//                                    watchLog.setCreateTime(new Date());
//                                    fsCourseWatchLogMapper.insertFsCourseWatchLog(watchLog);
//                                }
//
//                            }
//                        }
//                    }
//                    sopLogs.setContentJson(JSON.toJSONString(clone));

                    QwSopTempSetting.Content contentClone = JSON.parseObject(JSON.toJSONString(element), QwSopTempSetting.Content.class);

                    for (QwSopTempSetting.Content.Setting item : contentClone.getSetting()) {
                        // 如果有添加
                        if (item.getIsBindUrl() != null && item.getIsBindUrl().equals("1")) {
                            FsCourseLinkCreateParam param = new FsCourseLinkCreateParam();
                            param.setVideoId((long) videoId);
                            param.setQwUserId(String.valueOf(qwUserId));
//                            param.setDays(item.getExpiresDays());
                            param.setCorpId(log.getCorpId());
                            param.setCourseId(Long.valueOf(courseId));
                            param.setCompanyUserId(Long.valueOf(companyUserId));
                            param.setCompanyId(Long.valueOf(companyId));
                            param.setQwExternalId(contactId.getExternalId());
                            param.setSendTime(sendTime);

                            // 短链
                            R linkUrl = iFsCourseLinkService.createLinkUrl(param);

                            // 如果是课程 且 消息类型是链接类型
                            if (item.getContentType().equals("3")) {
                                item.setLinkUrl(linkUrl.get("url").toString());
                            } else {
                                item.setValue(item.getValue() + "\n" + linkUrl.get("url"));
                            }


                            //如果要推短链-则必须要绑定了小程序-才会有观看记录
                            if (sopLogs.getFsUserId() != null && sopLogs.getFsUserId()!= 0 ) {

                                FsCourseWatchLog watchLog = new FsCourseWatchLog();
                                watchLog.setUserId(sopLogs.getFsUserId());
                                watchLog.setVideoId(Long.valueOf(videoId));
                                watchLog.setQwExternalContactId(contactId.getExternalId());
                                watchLog.setSendType(2);
                                watchLog.setQwUserId(String.valueOf(qwUserId));
                                watchLog.setCourseId(Long.valueOf(courseId));
                                watchLog.setCompanyUserId(Long.valueOf(companyUserId));
                                watchLog.setCompanyId(Long.valueOf(companyId));


                                List<FsCourseWatchLog> fsCourseWatchLogs = fsCourseWatchLogMapper.selectFsCourseWatchLogList(watchLog);
                                if (fsCourseWatchLogs.isEmpty()) {
                                    watchLog.setLogType(3);
                                    watchLog.setCreateTime(new Date());
                                    fsCourseWatchLogMapper.insertFsCourseWatchLog(watchLog);
                                }

                            }
                        }
                    }

                    sopLogs.setContentJson(JSON.toJSONString(contentClone));

                    // 处理 log
                    qwSopLogsService.insertQwSopLogs(sopLogs);
                    break;
                case 3: // 订单消息
                    sopLogs.setContentJson(JSON.toJSONString(element));
                    // 处理 log
                    qwSopLogsService.insertQwSopLogs(sopLogs);
                    break;
                case 4: // 普通消息
                    sopLogs.setContentJson(JSON.toJSONString(element));

                    // 处理 log
                    qwSopLogsService.insertQwSopLogs(sopLogs);
                    break;
                default:
                    break;
            }

        });
    }

    @Override
    public String selectSopUserLogsByUnionSopId(SopUserLogs sopUserLogs) {
        return sopUserLogsMapper.selectSopUserLogsByUnionSopId(sopUserLogs);
    }

    @Override
    public List<SopUserLogs> selectSopUserLogsList(SopUserLogs sopUserLogs) {
        return sopUserLogsMapper.selectSopUserLogsList(sopUserLogs);
    }

    @Override
    public List<SopUserLogsVO> selectSopUserLogsList(SopUserLogsParam param) {
        List<SopUserLogsVO> list = sopUserLogsMapper.selectSopUserLogsListByParam(param);
        List<String> chatIdList = PubFun.listToNewList(list, SopUserLogsVO::getChatId);
        if(!chatIdList.isEmpty()){
            List<QwGroupChat> qwGroupChatList = qwGroupChatService.selectQwGroupChatByChatIds(chatIdList.toArray(new String[0]));
            Map<String, QwGroupChat> groupChatMap = PubFun.listToMapByGroupObject(qwGroupChatList, QwGroupChat::getChatId);
            list.stream().filter(e -> groupChatMap.containsKey(e.getChatId())).forEach(e -> {
                e.setChatName(groupChatMap.get(e.getChatId()).getName());
            });
        }
        return list;
    }

    @Override
    public String selectSopUserLogsByUpdate(SopUserLogs sopUserLogs) {
        return sopUserLogsMapper.selectSopUserLogsByUpdate(sopUserLogs);
    }

    /**
     * 删除营期中的相关客户
     */

    @Override
    public void deleteSopUserLogsByContactId(String qwUserId, String cropId, String externalContactId) {

    }

    @Override
    public void sendQwBySop() {

//        String json = "[{\"sopId\":\"51c47a57-c476-43ad-97ec-e3e7bb0e932e\",\"sopLogsId\":\"458b5561-b604-4049-a643-dca9fc64473c\",\"sopLogsInfoId\":\"7c8782ff-a0ab-4fb2-9a1a-d916f61f7df0\",\"externalId\":4,\"createTime\":\"2024-12-19\",\"qwUserId\":\"YuZhongXin\",\"corpId\":\"ww7bdf13931a6442d6\",\"fsUserId\":4048622184,\"externalUserName\":\"Le vent\",\"minConversionDay\":1,\"maxConversionDay\":2,\"minSend\":0,\"maxSend\":0},{\"sopId\":\"51c47a57-c476-43ad-97ec-e3e7bb0e932e\",\"sopLogsId\":\"458b5561-b604-4049-a643-dca9fc64473c\",\"sopLogsInfoId\":\"35253af7-0c63-4543-ab11-eb50744d334d\",\"externalId\":3,\"createTime\":\"2024-12-19\",\"qwUserId\":\"YuZhongXin\",\"corpId\":\"ww7bdf13931a6442d6\",\"fsUserId\":null,\"externalUserName\":\"Nik\",\"minConversionDay\":1,\"maxConversionDay\":2,\"minSend\":0,\"maxSend\":0},{\"sopId\":\"51c47a57-c476-43ad-97ec-e3e7bb0e932e\",\"sopLogsId\":\"458b5561-b604-4049-a643-dca9fc64473c\",\"sopLogsInfoId\":\"67ac8724-290a-4f76-bce7-c71dec17302d\",\"externalId\":5,\"createTime\":\"2024-12-19\",\"qwUserId\":\"YuZhongXin\",\"corpId\":\"ww7bdf13931a6442d6\",\"fsUserId\":null,\"externalUserName\":\"付宇豪｜金牌私域顾问\",\"minConversionDay\":1,\"maxConversionDay\":2,\"minSend\":0,\"maxSend\":0},{\"sopId\":\"51c47a57-c476-43ad-97ec-e3e7bb0e932e\",\"sopLogsId\":\"458b5561-b604-4049-a643-dca9fc64473c\",\"sopLogsInfoId\":\"8685cac8-1953-4216-a8b4-899c1dbffa24\",\"externalId\":2,\"createTime\":\"2024-12-19\",\"qwUserId\":\"YuZhongXin\",\"corpId\":\"ww7bdf13931a6442d6\",\"fsUserId\":null,\"externalUserName\":\"庄旭\",\"minConversionDay\":1,\"maxConversionDay\":2,\"minSend\":0,\"maxSend\":0}]";
//        List<SopUserLogsInfoVo> list = JSON.parseArray(json, SopUserLogsInfoVo.class);
//        LocalDate now = LocalDate.now();
        // 查询用户数据（今天之前的数据）
        List<SopUserLogsInfoVo> list = sopUserLogsMapper.selectSopUserLogsInfoListByTime();
        list.forEach(e -> e.setCreateDate(DateUtil.stringToLocalDate(e.getCreateTime())));
        log.info("进入回调:{}", list.stream().map(SopUserLogsInfoVo::getCreateDate).distinct().collect(Collectors.toList()));
        // 客户分组key生成规则
        Function<SopUserLogsInfoVo, String> getKey = e -> e.getExternalId() + "-" + e.getSopId();
        // 根据客户ID和sop任务ID分组，得到一共发了几天
        Map<String, Long> userMap = list.stream().collect(Collectors.groupingBy(getKey, Collectors.counting()));
        // 根据客户筛选并得到发送消息实体
        BiFunction<List<SopUserLogsInfoVo>, Integer, List<CrmMsg>> toFun = (arr, type) -> {
            // 根据分组获得是达到了哪个条件（minConversionDay，maxConversionDay）
            List<SopUserLogsInfoVo> newList = arr.stream().filter(e -> (type == 0 ? e.getMinSend() : e.getMaxSend()) == 0 && userMap.getOrDefault(getKey.apply(e), 0L) >= e.getMinConversionDay()).collect(Collectors.toList());
            // 然后根据企微分组
            Map<String, List<SopUserLogsInfoVo>> resultMap = PubFun.listToMapByGroupList(newList, SopUserLogsInfoVo::getQwUserId);
            // 得到企微用户ID
            Set<String> qwUserIdList = resultMap.keySet();
            // 查出可发送的企微用户数据
            List<QwUser> qwUsers = qwUserMapper.selectList(new QueryWrapper<QwUser>().in("qw_user_id", qwUserIdList).isNotNull("company_user_id"));
            // 组装消息数据
            return qwUsers.stream().filter(e -> resultMap.containsKey(e.getQwUserId())).map(e -> {
                // 这个企微用户有哪些客户
                List<SopUserLogsInfoVo> voList = resultMap.get(e.getQwUserId());
                // 获取单挑数据后面使用公共数据
                SopUserLogsInfoVo one = voList.get(0);
                // 获取名称
                String names = voList.stream().peek(v -> {
                    // 标识那些是发送了，发送了到时候要更新状态不会查出数据进入此方法
                    if(type == 0){
                        v.setMinIsSend(true);
                    }else{
                        v.setMaxIsSend(true);
                    }
                }).map(SopUserLogsInfoVo::getExternalUserName).collect(Collectors.joining("，"));
                // 组装数据
                CrmMsg crmMsg = new CrmMsg();
                crmMsg.setMsgType(4);
                crmMsg.setTitle("客户转换");
                crmMsg.setContent("你的客户" + names + "今天是第 " + (type == 0 ? one.getMinConversionDay() : one.getMaxConversionDay()) + " 天了，请重点关注");
                crmMsg.setCompanyId(e.getCompanyId());
                crmMsg.setCompanyUserId(e.getCompanyUserId());
                return crmMsg;
            }).collect(Collectors.toList());
        };
        // 插入消息列表
        List<CrmMsg> msgList = new ArrayList<>();
        // 小转换消息
        List<CrmMsg> minList = toFun.apply(list, 0);
        // 打转换消息
        List<CrmMsg> maxList = toFun.apply(list, 1);
        // 不为空添加
        if(!minList.isEmpty()){
            msgList.addAll(minList);
        }
        // 不为空添加
        if(!maxList.isEmpty()){
            msgList.addAll(maxList);
        }
        msgList.forEach(crmMsgMapper::insertCrmMsg);
        // 需要更新的转换数据
        List<SopUserLogsInfoVo> updateMinList = list.stream().filter(SopUserLogsInfoVo::isMinIsSend).collect(Collectors.toList());
        List<SopUserLogsInfoVo> updateMaxList = list.stream().filter(SopUserLogsInfoVo::isMaxIsSend).collect(Collectors.toList());
        // 更新
        updateMinList.forEach(e -> sopMapper.updateMinSendStatus(e.getSopId()));
        updateMaxList.forEach(e -> sopMapper.updateMaxSendStatus(e.getSopId()));

    }

    @Override
    public List<SopUserLogsVO> selectSopUserLogsListBySopId(SopUserLogs userLogs) {
        List<SopUserLogsVO> sopUserLogsInfoVos = sopUserLogsMapper.selectSopUserLogsListBySopId(userLogs);
        return sopUserLogsInfoVos;
    }

    /**
     * 查询sopUserLogs
     *
     * @param id sopUserLogs主键
     * @return sopUserLogs
     */
    @Override
    public SopUserLogs selectSopUserLogsById(String id)
    {
        return sopUserLogsMapper.selectSopUserLogsById(id);
    }

    @Override
    public R repairSopUserLogs(SopUserLogs param) {

        QwUser qwUser = qwUserMapper.selectQwUserByCorpIdAndUserId(param.getCorpId(), param.getQwUserId());
        if (qwUser != null) {
            Long companyUserId = qwUser.getCompanyUserId();
            Long companyId = qwUser.getCompanyId();
            if (companyUserId==null) {
                return R.error("用户未绑定销售，请先绑定");
            }
            if (companyId==null) {
                return R.error("用户未绑定销售公司，请先绑定");
            }

            param.setUserId(qwUser.getId()+"|"+companyUserId+"|"+companyId);
            sopUserLogsMapper.updateSopUserLogsByCompanyInfo(param);
        }

        return R.ok();
    }

    @Override
    public void repairSopUserLogsTimer() {
        try {
            List<SopUserLogs> sopUserLogs = sopUserLogsMapper.repairSopUserLogsList();

            Map<String, QwUser> qwUserMap = sopUserLogs.stream()
                    .collect(Collectors.toMap(
                            item -> item.getCorpId() + "|" + item.getQwUserId()+"|"+item.getId(),
                            item -> Optional.ofNullable(qwUserMapper.selectQwUserByCorpIdAndUserId(item.getCorpId(), item.getQwUserId()))
                                    .orElse(new QwUser())
                    ));
            List<String> errorMessages = new ArrayList<>();

            sopUserLogs.forEach(item -> {
                String key = item.getCorpId() + "|" + item.getQwUserId()+"|"+item.getId();
                QwUser qwUser = qwUserMap.get(key);

                if (qwUser != null && qwUser.getId()!=null) {
                    Long companyUserId = qwUser.getCompanyUserId();
                    Long companyId = qwUser.getCompanyId();

                    if (companyUserId == null) {
                        errorMessages.add("用户 " + item.getQwUserId() +"|"+item.getCorpId()+ " 未绑定销售，请先绑定");
                        return;
                    }
                    if (companyId == null) {
                        errorMessages.add("用户 " + item.getQwUserId()+"|" +item.getCorpId()+ " 未绑定销售公司，请先绑定");
                        return;
                    }

                    // 设置 userId 并更新记录
                    item.setUserId(String.join("|",
                            String.valueOf(qwUser.getId()),
                            String.valueOf(companyUserId),
                            String.valueOf(companyId)
                    ));

                    sopUserLogsMapper.updateSopUserLogsByCompanyInfo(item);
                } else {
                    errorMessages.add("用户 " + item.getQwUserId() + " 未找到对应的 QwUser 信息");
                }
            });

            if (!errorMessages.isEmpty()) {
                logger.error("定时处理 营期异常的数据 出错："+String.join("；", errorMessages));
            }
        }catch (Exception e){
            logger.error("定时处理 营期异常的数据 出错2："+e.getMessage());
        }


    }

    @Override
    public R ratingUserLogs(String sopId) {

        String json = configService.selectConfigByKey("qwRating:config");
        QwRatingConfig config = JSON.parseObject(json, QwRatingConfig.class);

        List<SopUserLogs> sopUserLogs = sopUserLogsMapper.meetsTheRatingByUserInfoBySopId(sopId);
        sopUserLogs.stream().forEach(item->{

            Integer countDays = item.getCountDays();

           Integer sopTemIdNum=redisCache.getCacheObject("sop-tempId:"+item.getSopTempId());

           if (sopTemIdNum==null){
               sopTemIdNum= qwSopTempDayService.getDayNumByIdLimitOne(item.getSopTempId());
               redisCache.setCacheObject("sop-tempId:"+item.getSopTempId(),sopTemIdNum,3, TimeUnit.HOURS);
           }


            //如果当前营期的伦次中，模板天数不足。则不评级
            if (sopTemIdNum< countDays){
                logger.info("当前营期的伦次中，模板天数不足。不评级："+item.getSopTempId()+"|sopId："+item.getSopId());
                return;
            }

            //查询营期中的客户
            List<SopUserLogsInfo> sopUserLogsInfosList = sopUserLogsInfoMapper.selectSopUserLogsInfoListBySopId(item.getSopId(), item.getId());
            if (sopUserLogsInfosList != null && !sopUserLogsInfosList.isEmpty()) {

                List<QwExternalContact> batchQwExternalContact=new ArrayList<>();

                sopUserLogsInfosList.forEach(logsInfo -> {
                    try {
                        Long externalId = logsInfo.getExternalId();
                        if (externalId != null) {

                            List<QwRatingVO> ratingVOS = fsCourseWatchLogMapper
                                    .selectFsCourseWatchLogByExtIdRating(externalId, config.getLevelDay());

                            if (ratingVOS.isEmpty()) {
                                log.error("没有看课记录不评级。则不评级："+externalId);
                                return ;
                            }

                            int scoreLevel = getScoreLevel(ratingVOS, config);
                            int latestTime = getLatestTime(ratingVOS);
                            int levelUpFall = ratingVOS.get(0).getLevelType() != null
                                    ? getLevelUpFall(scoreLevel, ratingVOS.get(0).getLevelType())
                                    : 3;

                            QwExternalContact externalContact = new QwExternalContact();
                            externalContact.setId(externalId);
                            externalContact.setLevel(scoreLevel);
                            externalContact.setLastWatchTime(latestTime);
                            externalContact.setLevelType(levelUpFall);

                            batchQwExternalContact.add(externalContact);




                        }
                    }catch (Exception e){
                        logger.error("计算用户积分异常，用户id：{}", logsInfo, e);
                    }
                });

                batchUpdateQwExternalContact(batchQwExternalContact);

            }else {
                logger.error("当前营期没有客户-sopId：{},营期id:{}", item.getSopId(), item.getId());
            }
        });


        return R.ok();
    }

    //批量更新
    private void batchUpdateQwExternalContact(List<QwExternalContact> notInExternalUseridList) {
        // 定义批量插入的大小
        int batchSize = 300;

        // 循环处理外部用户 ID，每次处理批量大小的子集
        for (int i = 0; i < notInExternalUseridList.size(); i += batchSize) {

            int endIndex = Math.min(i + batchSize, notInExternalUseridList.size());
            List<QwExternalContact> batchList = notInExternalUseridList.subList(i, endIndex);  // 获取当前批次的子集

            // 直接使用批次数据进行批量更新，不需要额外的 List
            try {
                qwExternalContactMapper.batchUpdateQwExternalContact(batchList);
                logger.info("成功更新了评级批次数据，起始索引为:{},数量：{} ", i,batchList.size());
            } catch (Exception e) {
                // 记录异常日志，方便后续排查问题
                logger.error("批量更新数据时发生异常，处理的批次起始索引为: " + i, e);
            }
        }
    }

    //评级
    public int getScoreLevel(List<QwRatingVO> qwRatingVOS, QwRatingConfig config) {

        AtomicDouble watchCount= new AtomicDouble();

        qwRatingVOS.forEach(vo -> {
            // 计算 watchDuration 除以 allDuration，并保留2位小数，四舍五入
            BigDecimal watchDuration = new BigDecimal(vo.getWatchDuration());
            BigDecimal allDuration = new BigDecimal(vo.getAllDuration());

            BigDecimal ratio = watchDuration.divide(allDuration, 3, RoundingMode.DOWN);

            // 将结果四舍五入后加到 watchCount
            watchCount.addAndGet(ratio.doubleValue());
        });


        // 计算 watchCount 除以 allSize 的结果并四舍五入为整数
        BigDecimal result = new BigDecimal(watchCount.get())
                .divide(new BigDecimal(qwRatingVOS.size()), 3, RoundingMode.DOWN);

        // 将结果乘以 100
        BigDecimal resultMultiplied = result.multiply(new BigDecimal(100));

        // 四舍五入到整数
        BigDecimal roundedResult = resultMultiplied.setScale(0, RoundingMode.HALF_UP);

        // 转换为 int 类型
        int score = roundedResult.intValue();

        if (score >= config.getALevelMin()) {
            return 1; // A 等级
        } else if (score >= config.getBLevelMin() && score < config.getBLevelMax()) {
            return 2; // B 等级
        } else if (score >= config.getCLevelMin() && score < config.getCLevelMax()) {
            return 3; // C 等级
        } else if (score >= config.getDLevelMin() && score < config.getDLevelMax()) {
            return 4; // D 等级
        } else {
            throw new IllegalArgumentException("分数不在任何等级范围内: " + score);
        }
    }
    //升降等级
    public int getLevelUpFall(int scoreLevel,int levelUpFall){

        if (scoreLevel > levelUpFall) {
            return  1;//升级
        }else if (scoreLevel < levelUpFall) {
            return  2;//降级
        }else {
            return  3;//不变
        }
    }

    //计算最晚看课时间
    public int getLatestTime(List<QwRatingVO> qwRatingVOS){

        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("HHmm");

        // 用于存储提取出的时分
        List<String> timeOnlyList = new ArrayList<>();

        qwRatingVOS.forEach(vos->{

            String finishTime = vos.getFinishTime();

            if (!StringUtil.strIsNullOrEmpty(finishTime)){
                LocalTime localTime = LocalTime.parse(finishTime, formatter);
                String formattedTime = localTime.format(outputFormatter);
                timeOnlyList.add(formattedTime);
            }

        });
        String latestTime=null;
        if (!timeOnlyList.isEmpty()){
            latestTime  = Collections.max(timeOnlyList);
        }else {
            latestTime = "0";
        }


        return Integer.parseInt(latestTime);
    }



}
