package com.fs.course.service.impl;

import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.utils.DateUtils;
import com.fs.common.utils.DictUtils;
import com.fs.company.cache.ICompanyCacheService;
import com.fs.company.cache.ICompanyUserCacheService;
import com.fs.company.domain.Company;
import com.fs.company.domain.CompanyUser;
import com.fs.course.config.CourseConfig;
import com.fs.course.domain.FsCourseFinishTemp;
import com.fs.course.domain.FsCourseWatchLog;
import com.fs.course.domain.FsUserCourse;
import com.fs.course.domain.FsUserCourseVideo;
import com.fs.course.mapper.*;
import com.fs.course.param.*;
import com.fs.course.service.IFsCourseWatchLogService;
import com.fs.course.service.cache.IFsUserCourseVideoCacheService;
import com.fs.course.vo.*;
import com.fs.his.config.FsSysConfig;
import com.fs.his.utils.ConfigUtil;
import com.fs.qw.Bean.MsgBean;
import com.fs.qw.cache.IQwExternalContactCacheService;
import com.fs.qw.cache.IQwUserCacheService;
import com.fs.qw.domain.QwExternalContact;
import com.fs.qw.domain.QwUser;
import com.fs.qw.domain.QwWatchLog;
import com.fs.qw.mapper.QwExternalContactMapper;
import com.fs.qw.mapper.QwUserMapper;
import com.fs.qw.mapper.QwWatchLogMapper;
import com.fs.qw.param.SendSopParamDetails;
import com.fs.qw.vo.QwSopCourseFinishTempSetting;
import com.fs.qw.vo.QwSopTempSetting;
import com.fs.sop.domain.QwSopLogs;
import com.fs.sop.mapper.SopUserLogsMapper;
import com.fs.store.domain.FsUser;
import com.fs.store.service.IFsUserService;
import com.fs.store.service.cache.IFsUserCacheService;
import com.fs.store.service.cache.IFsUserCourseCacheService;
import com.fs.system.service.ISysConfigService;
import com.hc.openapi.tool.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;

/**
 * 短链课程看课记录Service业务层处理
 *
 * @author fs
 * @date 2024-09-18
 */
@Service
public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMapper, FsCourseWatchLog> implements IFsCourseWatchLogService
{
    private static final Logger log = LoggerFactory.getLogger(FsCourseWatchLogServiceImpl.class);
    @Autowired
    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
    @Autowired
    private SopUserLogsMapper sopUserLogsMapper;

    @Autowired
    private FsUserCourseVideoMapper fsUserCourseVideoMapper;
    @Autowired
    private FsUserCourseMapper fsUserCourseMapper;
    @Autowired
    private FsCourseFinishTempMapper fsCourseFinishTempMapper;
    @Autowired
    private QwExternalContactMapper qwExternalContactMapper;
    @Autowired
    RedisCache redisCache;
    @Autowired
    private QwWatchLogMapper qwWatchLogMapper;
    @Autowired
    private FsUserCourseVideoMapper courseVideoMapper;
    @Autowired
    private ConfigUtil configUtil;

    @Autowired
    private ICompanyUserCacheService companyUserCacheService;

    @Autowired
    private IFsUserService fsUserService;

    @Autowired
    private IFsUserCourseCacheService fsUserCourseCacheService;

    @Autowired
    private IFsUserCourseVideoCacheService fsUserCourseVideoCacheService;

    @Autowired
    private IFsUserCacheService fsUserCacheService;

    @Autowired
    private ICompanyCacheService companyCacheService;

    @Autowired
    private IQwUserCacheService qwUserCacheService;

    @Autowired
    private IQwExternalContactCacheService qwExternalContactCacheService;

    @Autowired
    private HyWatchLogMapper hyWatchLogMapper;

    /**
     * 查询短链课程看课记录
     *
     * @param logId 短链课程看课记录主键
     * @return 短链课程看课记录
     */
    @Override
    public FsCourseWatchLog selectFsCourseWatchLogByLogId(Long logId)
    {
        return fsCourseWatchLogMapper.selectFsCourseWatchLogByLogId(logId);
    }

    /**
     * 查询短链课程看课记录列表
     *
     * @param fsCourseWatchLog 短链课程看课记录
     * @return 短链课程看课记录
     */
    @Override
    public List<FsCourseWatchLog> selectFsCourseWatchLogList(FsCourseWatchLog fsCourseWatchLog)
    {
        return fsCourseWatchLogMapper.selectFsCourseWatchLogList(fsCourseWatchLog);
    }

    /**
     * 查询短链课程看课记录
     *
     * @param param 短链课程看课记录
     * @return 短链课程看课记录
     */
    @Override
    public FsCourseWatchLog selectFsCourseWatchLog(FsCourseWatchLogParam param) {
        return fsCourseWatchLogMapper.selectFsCourseWatchLog(param);
    }

    /**
     * 新增短链课程看课记录
     *
     * @param fsCourseWatchLog 短链课程看课记录
     * @return 结果
     */
    @Override
    public int insertFsCourseWatchLog(FsCourseWatchLog fsCourseWatchLog)
    {
        fsCourseWatchLog.setCreateTime(DateUtils.getNowDate());
        return fsCourseWatchLogMapper.insertFsCourseWatchLog(fsCourseWatchLog);
    }

    /**
     * 修改短链课程看课记录
     *
     * @param fsCourseWatchLog 短链课程看课记录
     * @return 结果
     */
    @Override
    public int updateFsCourseWatchLog(FsCourseWatchLog fsCourseWatchLog)
    {
        fsCourseWatchLog.setUpdateTime(DateUtils.getNowDate());
        return fsCourseWatchLogMapper.updateFsCourseWatchLog(fsCourseWatchLog);
    }

    /**
     * 批量删除短链课程看课记录
     *
     * @param logIds 需要删除的短链课程看课记录主键
     * @return 结果
     */
    @Override
    public int deleteFsCourseWatchLogByLogIds(Long[] logIds)
    {
        return fsCourseWatchLogMapper.deleteFsCourseWatchLogByLogIds(logIds);
    }

    /**
     * 删除短链课程看课记录信息
     *
     * @param logId 短链课程看课记录主键
     * @return 结果
     */
    @Override
    public int deleteFsCourseWatchLogByLogId(Long logId)
    {
        return fsCourseWatchLogMapper.deleteFsCourseWatchLogByLogId(logId);
    }

    @Override
    public FsCourseWatchLog getWatchCourseVideo(Long userId, Long videoId, String qwUserId,Long externalId) {
        return fsCourseWatchLogMapper.getWatchCourseVideo(userId,videoId,qwUserId,externalId);
    }

    @Override
    public FsCourseWatchLog getWatchCourseVideoH5(Long videoId, String qwUserId, Long externalId) {
        return fsCourseWatchLogMapper.getWatchCourseLogVideoBySop(videoId,qwUserId,externalId);
    }

    /**
     * 根据条件查询数量
     * @param params    参数
     * @return  count
     */
    @Override
    public int countByMap(Map<String, Object> params) {
        return baseMapper.countByMap(params);
    }

    @Override
    public FsCourseWatchLog getWatchCourseLogVideoBySop(Long videoId,String qwUserId,Long externalId )
    {
        return fsCourseWatchLogMapper.getWatchCourseLogVideoBySop(videoId,qwUserId,externalId);
    }


    @Override
    public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVO(FsCourseWatchLogListParam param) {
        if(ObjectUtils.isNull(param.getPageNum())){
            param.setPageNum(1L);
        }
        if(ObjectUtils.isNull(param.getPageSize())){
            param.setPageSize(10L);
        }
        List<FsCourseWatchLogListVO> list = fsCourseWatchLogMapper.selectFsCourseWatchLogListVO(param);
        for (FsCourseWatchLogListVO item : list) {
            // 项目
            if(ObjectUtils.isNotNull(item.getProject())) {
                String sysCourseProject = DictUtils.getDictLabel("sys_course_project", String.valueOf(item.getProject()));
                if(StringUtils.isNotBlank(sysCourseProject)){
                    item.setProjectName(sysCourseProject);
                }
            }
            // 用户名
            if(ObjectUtils.isNotNull(item.getUserId())) {
                FsUser fsUser = fsUserCacheService.selectFsUserById(item.getUserId());
                if(ObjectUtils.isNotNull(fsUser)){
                    item.setUserName(String.format("%s_%d",fsUser.getNickname(),fsUser.getUserId()));
                    item.setFsNickName(fsUser.getNickname());
                    item.setFsAvatar(fsUser.getAvatar());
                }
            }
            // 公司名
            if(ObjectUtils.isNotNull(item.getCompanyId())){
                Company company = companyCacheService.selectCompanyById(Long.valueOf(item.getCompanyId()));
                if(ObjectUtils.isNotNull(company)){
                    item.setCompanyName(String.format("%s_%d", company.getCompanyName(), company.getCompanyId()));
                }
            }

            // 销售名
            if(ObjectUtils.isNotNull(item.getCompanyUserId())){
                CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(item.getCompanyUserId());
                if(ObjectUtils.isNotNull(companyUser)){
                    item.setCompanyUserName(String.format("%s_%d", companyUser.getNickName(), companyUser.getUserId()));
                }
            }

            // 课程
            if(ObjectUtils.isNotNull(item.getCourseId())){
                FsUserCourse course = fsUserCourseCacheService.selectFsUserCourseByCourseId(item.getCourseId());
                if(ObjectUtils.isNotNull(course)){
                    item.setCourseName(course.getCourseName());
                }
            }
            // 小节
            if(ObjectUtils.isNotNull(item.getVideoId())){
                FsUserCourseVideo fsUserCourseVideo = fsUserCourseVideoCacheService.selectFsUserCourseVideoByVideoId(item.getVideoId());
                if(ObjectUtils.isNotNull(fsUserCourseVideo)){
                    item.setVideoName(fsUserCourseVideo.getTitle());
                }
            }

        }
        return list;
    }

    @Override
    public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVONew(FsCourseWatchLogListParam param) {
        if(ObjectUtils.isNull(param.getPageNum())){
            param.setPageNum(1L);
        }
        if(ObjectUtils.isNull(param.getPageSize())){
            param.setPageSize(10L);
        }
        List<FsCourseWatchLogListVO> list = fsCourseWatchLogMapper.selectFsCourseWatchLogListVONew(param);
        for (FsCourseWatchLogListVO item : list) {
            // 项目
            if(ObjectUtils.isNotNull(item.getProject())) {
                String sysCourseProject = DictUtils.getDictLabel("sys_course_project", String.valueOf(item.getProject()));
                if(StringUtils.isNotBlank(sysCourseProject)){
                    item.setProjectName(sysCourseProject);
                }
            }
            // 用户名
            if(ObjectUtils.isNotNull(item.getUserId())) {
                FsUser fsUser = fsUserCacheService.selectFsUserById(item.getUserId());
                if(ObjectUtils.isNotNull(fsUser)){
                    item.setUserName(String.format("%s_%d",fsUser.getNickname(),fsUser.getUserId()));
                    item.setFsNickName(fsUser.getNickname());
                    item.setFsAvatar(fsUser.getAvatar());
                }
            }
            // 公司名
            if(ObjectUtils.isNotNull(item.getCompanyId())){
                Company company = companyCacheService.selectCompanyById(Long.valueOf(item.getCompanyId()));
                if(ObjectUtils.isNotNull(company)){
                    item.setCompanyName(String.format("%s_%d", company.getCompanyName(), company.getCompanyId()));
                }
            }

            // 销售名
            if(ObjectUtils.isNotNull(item.getCompanyUserId())){
                CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(item.getCompanyUserId());
                if(ObjectUtils.isNotNull(companyUser)){
                    item.setCompanyUserName(String.format("%s_%d", companyUser.getNickName(), companyUser.getUserId()));
                }
            }

            // 课程
            if(ObjectUtils.isNotNull(item.getCourseId())){
                FsUserCourse course = fsUserCourseCacheService.selectFsUserCourseByCourseId(item.getCourseId());
                if(ObjectUtils.isNotNull(course)){
                    item.setCourseName(course.getCourseName());
                }
            }
            // 小节
            if(ObjectUtils.isNotNull(item.getVideoId())){
                FsUserCourseVideo fsUserCourseVideo = fsUserCourseVideoCacheService.selectFsUserCourseVideoByVideoId(item.getVideoId());
                if(ObjectUtils.isNotNull(fsUserCourseVideo)){
                    item.setVideoName(fsUserCourseVideo.getTitle());
                }
            }

            // 企微用户名
            if(ObjectUtils.isNotNull(item.getQwUserId())){
                String qwUserName = qwUserCacheService.queryQwUserNameByUserId(item.getQwUserId());
                if(StringUtils.isNotBlank(qwUserName)){
                    item.setQwUserName(qwUserName);
                }
            }

            // 企微外部联系人
            if(ObjectUtils.isNotNull(item.getQwExternalContactId())){
                String qwExternalContactName = qwExternalContactCacheService.selectQwExternalContactById(item.getQwExternalContactId());
                if(StringUtils.isNotBlank(qwExternalContactName)){
                    item.setExternalUserName(qwExternalContactName);
                }
            }

        }
        return list;
    }

    @Override
    public Long selectFsCourseWatchLogListVOCount(FsCourseWatchLogListParam param) {

        return fsCourseWatchLogMapper.selectFsCourseWatchLogListVONewCount(param);
    }

    @Override
    public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVOExport(FsCourseWatchLogListParam param) {
        return fsCourseWatchLogMapper.selectFsCourseWatchLogListVO(param);
    }

    @Override
    public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListByParam(FsCourseWatchLogListParam param) {

        if (param != null){
            if (param.getScheduleStartTime() != null && param.getScheduleEndTime() != null){
                List<String> sopUserLogsVOS = sopUserLogsMapper.selectSopUserLogsByDate(param.getScheduleStartTime(), param.getScheduleEndTime());
                param.setSopIds(sopUserLogsVOS);
            }
        }
        List<FsCourseWatchLogListVO> fsCourseWatchLogListVOS = fsCourseWatchLogMapper.selectFsCourseWatchLogListByParam(param);


        return fsCourseWatchLogListVOS ;
    }

    @Autowired
    private QwUserMapper qwUserMapper;

    @Override
    public void testFinishMsg() {
        FsCourseWatchLog finishLog = fsCourseWatchLogMapper.selectFsCourseWatchLogByLogId(341170L);
        QwUser qwUser = qwUserMapper.selectQwUserById(Long.parseLong(finishLog.getQwUserId()));
        QwExternalContact externalContact = qwExternalContactMapper.selectQwExternalContactById(finishLog.getQwExternalContactId());
        FsCourseFinishTemp finishTemp = fsCourseFinishTempMapper.selectFsCourseFinishTempByCompanyUserId(finishLog.getCompanyUserId(),finishLog.getVideoId());
        QwSopCourseFinishTempSetting setting = new QwSopCourseFinishTempSetting();
        if (finishTemp!=null){
            //        List<QwSopCourseFinishTempSetting> tempSettings = JSON.parseArray(finishTemp.getSetting(), QwSopCourseFinishTempSetting.class);
            // 设置时间格式
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            // 获取当前时间
            LocalDateTime currentTime = LocalDateTime.now();
            // 添加三分钟
            LocalDateTime newTime = currentTime.plusMinutes(3);
            // 格式化为字符串
            String newTimeString = newTime.format(formatter);
            QwSopLogs sopLogs = new QwSopLogs();
            sopLogs.setSendTime(newTimeString);
            sopLogs.setQwUserid(externalContact.getUserId());
            sopLogs.setCorpId(externalContact.getCorpId());
            sopLogs.setLogType(2);
            sopLogs.setSendType(3);
            sopLogs.setSendStatus(1L);
            sopLogs.setReceivingStatus(1L);
            sopLogs.setSort(3);
            sopLogs.setCompanyId(finishLog.getCompanyId());
            sopLogs.setSopId(finishLog.getSopId());
            sopLogs.setExternalUserId(externalContact.getExternalUserId());
            sopLogs.setExternalUserName(externalContact.getName());
            sopLogs.setFsUserId(finishLog.getUserId());
            List<QwSopCourseFinishTempSetting.Setting> list = JSONArray.parseArray(finishTemp.getSetting(),QwSopCourseFinishTempSetting.Setting.class);
            setting.setSetting(list);
            sopLogs.setContentJson(JSON.toJSONString(setting));

            QwSopTempSetting.Content content = JSON.parseObject(sopLogs.getContentJson(), QwSopTempSetting.Content.class);


            //封装消息体
            SendSopParamDetails sopParamDetailsB=new SendSopParamDetails();
            sopParamDetailsB.setSopLogId(sopLogs.getId());
            sopParamDetailsB.setExternalUserId(sopLogs.getExternalUserId());
            sopParamDetailsB.setExternalUserName(sopLogs.getExternalUserName());
            sopParamDetailsB.setQwUserid(sopLogs.getQwUserid());
            sopParamDetailsB.setSetting(content.getSetting());


//            //设置为已发送并更新
//            finishLog.setSendFinishMsg(1);
//            fsCourseWatchLogMapper.updateFsCourseWatchLog(finishLog);
            sendSocket("sendMsg",JSONObject.toJSONString(sopParamDetailsB),qwUser.getAppKey());

        }

    }

    @Override
    public List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVO(FsCourseWatchLogStatisticsListParam param) {
        return fsCourseWatchLogMapper.selectFsCourseWatchLogStatisticsListVO(param);
    }

    @Override
    public List<FsCourseWatchLogStatisticsListVO> selectFsCourseWatchLogStatisticsListVONew(FsCourseWatchLogStatisticsListParam param) {
        List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogMapper.selectFsCourseWatchLogStatisticsListVONew(param);
        for (FsCourseWatchLogStatisticsListVO item : list) {
            // 项目名
            if(ObjectUtils.isNotNull(item.getProject())){
                String sysCourseProject = DictUtils.getDictLabel("sys_course_project", String.valueOf(item.getProject()));
                if(StringUtils.isNotBlank(sysCourseProject)){
                    item.setProjectName(sysCourseProject);
                }
            }
            // 课程名
            if(ObjectUtils.isNotNull(item.getCourseId())) {
                String courseName = fsUserCourseCacheService.selectCourseNameByCourseId(item.getCourseId());
                if(ObjectUtils.isNotNull(courseName)){
                    item.setCourseName(courseName);
                }
            }
            // 小节名
            if(ObjectUtils.isNotNull(item.getVideoId())) {
                FsUserCourseVideo fsUserCourseVideo = fsUserCourseVideoCacheService.selectFsUserCourseVideoByVideoId(item.getVideoId());
                if(ObjectUtils.isNotNull(fsUserCourseVideo)){
                    item.setVideoName(fsUserCourseVideo.getTitle());
                }
            }
            // 用户名
            if(ObjectUtils.isNotNull(item.getUserId())) {
                FsUser fsUser = fsUserCacheService.selectFsUserById(item.getUserId());
                if(ObjectUtils.isNotNull(fsUser)){
                    item.setUserName(String.format("%s_%d",fsUser.getNickname(),fsUser.getUserId()));
                }
            }
            // 销售名
            if(ObjectUtils.isNotNull(item.getCompanyUserId())) {
                CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(item.getCompanyUserId());
                if(ObjectUtils.isNotNull(companyUser)){
                    item.setCompanyUserName(String.format("%s_%d", companyUser.getUserName(), companyUser.getUserId()));
                }
            }
        }
        return list;
    }

    @Override
    public Long selectFsCourseWatchLogStatisticsListVONewCount(FsCourseWatchLogStatisticsListParam param) {
        return fsCourseWatchLogMapper.selectFsCourseWatchLogStatisticsListVONewCount(param);
    }


    public void sendSocket(String cmd,String message,String appKey){
        MsgBean msgBean=new MsgBean();
        msgBean.setCmd(cmd);
        msgBean.setData(message);
        msgBean.setKey(appKey);
        JSONObject params=new JSONObject();
        params.put("id",appKey);
        params.put("message",JSONObject.toJSONString(msgBean));
        FsSysConfig config = configUtil.getSysConfig();
        String domainName = config.getHookUrl();
        HttpRequest.post(domainName+"/app/qwmsg/receiveMsg")
                .body(JSONObject.toJSONString(params),"application/json;charset=UTF-8")
                .execute().body();
    }

    @Autowired
    private ISysConfigService configService;

    @Override
    public void scheduleBatchUpdateToDatabase() {
        log.info("开始更新看课时长，检查完课>>>>>>");
        //读取所有的key
        Collection<String> keys = redisCache.keys("h5user:watch:duration:*");
        //读取看课配置
        String json = configService.selectConfigByKey("course.config");
        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);

        List<FsCourseWatchLog> logs = new ArrayList<>();
        for (String key : keys) {
            //取key中数据
            String[] parts = key.split(":");
            Long qwUserId = Long.parseLong(parts[3]);
            Long externalId = Long.parseLong(parts[4]);
            Long videoId = Long.parseLong(parts[5]);
            String durationStr = redisCache.getCacheObject(key);
            if(durationStr==null){
                log.error("key中数据为null:{}",key);
                continue;  // 如果 Redis 中没有记录，跳过
            }
            Long duration = Long.valueOf(durationStr);

            FsCourseWatchLog watchLog = new FsCourseWatchLog();
            watchLog.setVideoId(videoId);
            watchLog.setQwUserId(qwUserId.toString());
            watchLog.setQwExternalContactId(externalId);
            watchLog.setDuration(duration);

            //取对应视频的时长
            Long videoDuration = getVideoDuration(videoId);
            if (videoDuration != null && videoDuration != 0) {
                //判断是否完课
                long percentage = (duration * 100 / videoDuration);
                if (percentage >= config.getAnswerRate()) {
                    watchLog.setLogType(2); // 设置状态为“已完成”
                    watchLog.setFinishTime(new Date());
                    String heartbeatKey ="h5user:watch:heartbeat:" + qwUserId+ ":" + externalId + ":" + videoId;
                    // 完课删除心跳记录
                    redisCache.deleteObject(heartbeatKey);
                    // 完课删除看课时长记录
                    redisCache.deleteObject(key);
                }
            }
            //集合中增加
            logs.add(watchLog);
        }
        batchUpdateFsCourseWatchLog(logs,100);
    }

    @Override
    public void checkWatchStatus() {
        log.info("开始更新看课中断记录>>>>>");
        // 从 Redis 中获取所有正在看课的用户记录
        Collection<String> keys = redisCache.keys("h5user:watch:heartbeat:*");
        LocalDateTime now = LocalDateTime.now();
        List<FsCourseWatchLog> logs = new ArrayList<>();
        for (String key : keys) {
            FsCourseWatchLog watchLog = new FsCourseWatchLog();
            String[] parts = key.split(":");
            Long qwUserId = Long.parseLong(parts[3]);
            Long externalId = Long.parseLong(parts[4]);
            Long videoId = Long.parseLong(parts[5]);
            // 获取最后心跳时间
            String lastHeartbeatStr = redisCache.getCacheObject(key);
            if (lastHeartbeatStr == null) {
                continue; // 如果 Redis 中没有记录，跳过
            }
            LocalDateTime lastHeartbeatTime = LocalDateTime.parse(lastHeartbeatStr);
            Duration duration = Duration.between(lastHeartbeatTime, now);

            watchLog.setVideoId(videoId);
            watchLog.setQwUserId(qwUserId.toString());
            watchLog.setQwExternalContactId(externalId);
            // 如果超过一分钟没有心跳，标记为“观看中断”
            if (duration.getSeconds() >= 60) {
                watchLog.setLogType(4);
                // 从 Redis 中删除该记录
                redisCache.deleteObject(key);
            }else {
                watchLog.setLogType(1);
            }
            logs.add(watchLog);
        }
        batchUpdateFsCourseWatchLog(logs,100);
    }

    @Override
    public List<FsCourseUserStatisticsListVO> selectFsCourseUserStatisticsListVO(FsCourseUserStatisticsListParam param) {
        return fsCourseWatchLogMapper.selectFsCourseUserStatisticsListVO(param);
    }

    @Override
    public List<FsCourseOverVO> selectFsCourseWatchLogOverStatisticsListVO(FsCourseOverParam param) {
        return fsCourseWatchLogMapper.selectFsCourseWatchLogOverStatisticsListVO(param);
    }

    @Override
    public List<FsCourseOverVO> selectFsCourseWatchLogOverStatisticsListVONew(FsCourseOverParam param) {
        QueryWrapper<FsCourseWatchLog> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("MIN(create_time) as createTime","company_user_id as companyUserId");
        if(param.getCompanyId() != null) {
            queryWrapper.eq("company_id", param.getCompanyId());
        }
        if(param.getSTime() != null) {
            queryWrapper.apply("DATE(create_time) >= DATE({0})", param.getSTime());
        }
        if (param.getETime() != null) {
            queryWrapper.apply("DATE(create_time) <= DATE({0})", param.getETime());
        }
        queryWrapper.groupBy("company_user_id");
        List<FsCourseWatchLog> fsCourseWatchLogs = fsCourseWatchLogMapper.selectList(queryWrapper);
        List<FsCourseOverVO> fsCourseOverVOS = new ArrayList<>();
        for (FsCourseWatchLog fsCourseWatchLog : fsCourseWatchLogs) {
            FsCourseOverVO overVO = new FsCourseOverVO();
            overVO.setCreateTime(fsCourseWatchLog.getCreateTime());
            if(fsCourseWatchLog.getCompanyUserId() != null) {
                CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(fsCourseWatchLog.getCompanyUserId());
                if (companyUser != null) {
                    overVO.setUserName(companyUser.getUserName());
                    overVO.setUserCreateTime(companyUser.getCreateTime());
                }
            }
            fsCourseOverVOS.add(overVO);
        }
        return fsCourseOverVOS;
    }


    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public void addCourseWatchLogDayNew() {

        List<FsUserCourse> courses = fsUserCourseMapper.selectFsUserCourseAllCourse();
        for (FsUserCourse course : courses) {
            Long project = course.getProject();
            List<FsUserCourseVideo> fsUserCourseVideos = fsUserCourseVideoMapper.selectVideoByCourseId(course.getCourseId());
            for (FsUserCourseVideo fsUserCourseVideo : fsUserCourseVideos) {
                try {
                    ArrayList<HyWatchLog> QwWatchLogs = new ArrayList<>();
                    List<FsQwCourseWatchLogVO> watchLogs = fsCourseWatchLogMapper.selectFsCourseBeforeMonthWatchLogByVideoId(fsUserCourseVideo.getVideoId());

                    for (FsQwCourseWatchLogVO fsCourseWatchLog : watchLogs) {

                        // 获取进线时间
                        FsUser fsUser = fsUserService.selectFsUserById(fsCourseWatchLog.getUserId());
                        if (fsUser!=null){
                            fsCourseWatchLog.setLineTime(fsUser.getCreateTime());
                        }
                        // 查询首次观看时间
                        Date date = fsCourseWatchLogMapper.queryFirstWatchDateLogByVideoId(fsCourseWatchLog.getUserId());
                        if (date!=null){
                            fsCourseWatchLog.setFirstTime(date);
                        }

                        Date firstTime = fsCourseWatchLog.getFirstTime();
                        Long day=1L;
                        if (fsCourseWatchLog.getLineTime()==null){
                            continue;
                        }
                        //不是第一次 看课程
                        if (firstTime!=null){
                            LocalDate firstLocalDate = firstTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                            LocalDate currentDate = LocalDate.now();
                            day = ChronoUnit.DAYS.between(firstLocalDate, currentDate);
                        }else {
                            //是先导课
                            if (fsUserCourseVideo.getIsFirst()!=null&&fsUserCourseVideo.getIsFirst()==1){
                                // 如果存在第一次记录就跳过
                                int count = qwWatchLogMapper.selectQwWatchLogIsFirstByUserId(fsCourseWatchLog.getUserId());
                                if (count>0){
                                    continue;
                                }
                                day=0L;
                                //不是先导课
                            }
                        }
                        HyWatchLog qwWatchLog = new HyWatchLog();
                        qwWatchLog.setExtId(fsCourseWatchLog.getQwExternalContactId());
                        qwWatchLog.setLineTime(fsCourseWatchLog.getLineTime());
                        if(fsCourseWatchLog.getQwUserId() != null) {
                            qwWatchLog.setQwUserId(Long.parseLong(fsCourseWatchLog.getQwUserId()));
                        }
                        qwWatchLog.setDay(day);
                        qwWatchLog.setStatus(fsCourseWatchLog.getLogType()==3?0:fsCourseWatchLog.getLogType()==2?2:1);
                        qwWatchLog.setProject(project);
                        qwWatchLog.setCreateTime(fsCourseWatchLog.getCreateTime());
                        qwWatchLog.setCompanyId(fsCourseWatchLog.getCompanyId());
                        qwWatchLog.setCompanyUserId(fsCourseWatchLog.getCompanyUserId());
                        qwWatchLog.setFsUserId(fsCourseWatchLog.getUserId());
                        qwWatchLog.setCourseId(fsCourseWatchLog.getCourseId());
                        qwWatchLog.setVideoId(fsCourseWatchLog.getVideoId());
                        QwWatchLogs.add(qwWatchLog);
                    }
                    if (!QwWatchLogs.isEmpty()){
                        hyWatchLogMapper.insertHyWatchLogBatch(QwWatchLogs);
                    }
                }catch (Exception e){
                    log.error("看课记录add异常:{}",course.getCourseId(),e);
                    throw new RuntimeException(e);
                }


            }
        }


    }

    @Override
    public void addCourseWatchLogDay() {

        List<FsUserCourse> courses = fsUserCourseMapper.selectFsUserCourseAllCourse();
        for (FsUserCourse course : courses) {
            Long project = course.getProject();
            List<FsUserCourseVideo> fsUserCourseVideos = fsUserCourseVideoMapper.selectVideoByCourseId(course.getCourseId());
            for (FsUserCourseVideo fsUserCourseVideo : fsUserCourseVideos) {
                try {
                    ArrayList<QwWatchLog> QwWatchLogs = new ArrayList<>();
                    List<FsQwCourseWatchLogVO> watchLogs = fsCourseWatchLogMapper.selectFsCourseWatchLogByNoDayAndVoidId(fsUserCourseVideo.getVideoId());
                    for (FsQwCourseWatchLogVO fsCourseWatchLog : watchLogs) {
                        Date firstTime = fsCourseWatchLog.getFirstTime();
                        Long day=1L;
                        if (fsCourseWatchLog.getLineTime()==null){
                            continue;
                        }
                        //不是第一次 看课程
                        if (firstTime!=null){
                            LocalDate firstLocalDate = firstTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                            LocalDate currentDate = LocalDate.now();
                            day = ChronoUnit.DAYS.between(firstLocalDate, currentDate);
                        }else {
                            //是先导课
                            if (fsUserCourseVideo.getIsFirst()!=null&&fsUserCourseVideo.getIsFirst()==1){
                                int count = qwWatchLogMapper.selectQwWatchLogIsFirst(fsCourseWatchLog.getQwExternalContactId());
                                if (count>0){
                                    continue;
                                }
                                day=0L;
                                //不是先导课
                            }else {
                                day=1L;
                                QwExternalContact qwExternalContact = new QwExternalContact();
                                qwExternalContact.setId(fsCourseWatchLog.getQwExternalContactId());
                                qwExternalContact.setFirstTime(fsCourseWatchLog.getCreateTime());
                                qwExternalContactMapper.updateQwExternalContact(qwExternalContact);
                            }
                        }
                        QwWatchLog qwWatchLog = new QwWatchLog();
                        qwWatchLog.setExtId(fsCourseWatchLog.getQwExternalContactId());
                        qwWatchLog.setLineTime(fsCourseWatchLog.getLineTime());
                        qwWatchLog.setQwUserId(Long.parseLong(fsCourseWatchLog.getQwUserId()));
                        qwWatchLog.setDay(day);
                        qwWatchLog.setStatus(fsCourseWatchLog.getLogType()==3?0:fsCourseWatchLog.getLogType()==2?2:1);
                        qwWatchLog.setProject(project);
                        qwWatchLog.setCreateTime(fsCourseWatchLog.getCreateTime());
                        QwWatchLogs.add(qwWatchLog);
                    }
                    if (!QwWatchLogs.isEmpty()){
                        qwWatchLogMapper.insertQwWatchLogBatch(QwWatchLogs);
                    }
                }catch (Exception e){
                    log.error("看课记录add异常:{}",course.getCourseId(),e);
                }


            }
        }

    }


    public Long getVideoDuration(Long videoId){
        //将视频时长也存到redis
        String videoRedisKey = "h5user:video:duration:" + videoId;
        Long videoDuration = redisCache.getCacheObject(videoRedisKey);
        if (videoDuration==null){
            FsUserCourseVideo video = courseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
            videoDuration=video.getDuration();
            redisCache.setCacheObject(videoRedisKey,video.getDuration().toString());
        }
        return videoDuration;
    }


    public void batchUpdateFsCourseWatchLog(List<FsCourseWatchLog> logs, int batchSize) {
        if (logs == null || logs.isEmpty()) {
            log.info("待更新的日志列表为空，无需处理");
            return;
        }

        // 记录总日志数量
        log.info("开始批量更新日志，总日志数量: {}", logs.size());

        // 分批处理
        for (int i = 0; i < logs.size(); i += batchSize) {
            int end = Math.min(i + batchSize, logs.size());
            List<FsCourseWatchLog> batchList = logs.subList(i, end);

            // 记录当前批次的数量
            log.info("正在更新第 {} 批日志，数量: {}", (i / batchSize) + 1, batchList.size());

            // 执行批量更新
            fsCourseWatchLogMapper.batchUpdateWatchLog(batchList);

            // 记录当前批次更新完成
            log.info("第 {} 批日志更新完成，数量: {}", (i / batchSize) + 1, batchList.size());
        }

        // 记录全部更新完成
        log.info("所有日志更新完成，总日志数量: {}", logs.size());
    }


    @Override
    public void del() {
        List<Long> logIds = fsCourseWatchLogMapper.selectFsCourseWatchLogByLastmonth();
        processDelCourseLogs(logIds);
    }

    @Override
    public FsCourseWatchLog getWatchLogByFsUser(Long videoId, Long fsUserId, Long companyUserId) {
        return fsCourseWatchLogMapper.getWatchLogByFsUser(videoId, fsUserId, companyUserId);
    }

    @Override
    public void scheduleUpdateDurationToDatabase() {
        log.info("WXH5-开始更新会员看课时长，检查完课>>>>>>");
        //读取所有的key
        Collection<String> keys = redisCache.keys("h5wxuser:watch:duration:*");

        //读取看课配置
        String json = configService.selectConfigByKey("course.config");
        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);

        List<FsCourseWatchLog> logs = new ArrayList<>();
        for (String key : keys) {
            //取key中数据
            String[] parts = key.split(":");
            Long userId = Long.parseLong(parts[3]);
            Long videoId = Long.parseLong(parts[4]);
            Long companyUserId = Long.parseLong(parts[5]);
            String durationStr = redisCache.getCacheObject(key);
            if(durationStr==null){
                log.error("key中数据为null:{}",key);
                continue;  // 如果 Redis 中没有记录，跳过
            }
            Long duration = Long.valueOf(durationStr);

            FsCourseWatchLog watchLog = new FsCourseWatchLog();
            watchLog.setVideoId(videoId);
            watchLog.setUserId(userId);
            watchLog.setCompanyUserId(companyUserId);
            watchLog.setDuration(duration);

            //取对应视频的时长
            Long videoDuration = getFsUserVideoDuration(videoId);
            if (videoDuration != null && videoDuration != 0) {
                //判断是否完课
                long percentage = (duration * 100 / videoDuration);
                if (percentage >= config.getAnswerRate()) {
                    watchLog.setLogType(2); // 设置状态为“已完成”checkFsUserWatchStatus
                    watchLog.setFinishTime(new Date());
                    String heartbeatKey ="h5wxuser:watch:heartbeat:" + userId+ ":" + videoId + ":" + companyUserId;
                    // 完课删除心跳记录
                    redisCache.deleteObject(heartbeatKey);
                    // 完课删除看课时长记录
                    redisCache.deleteObject(key);
                }
            }
            //集合中增加
            logs.add(watchLog);
        }
        batchUpdateFsUserCourseWatchLog(logs,100);
    }

    public Long getFsUserVideoDuration(Long videoId){
        //将视频时长也存到redis
        String videoRedisKey = "h5wxuser:video:duration:" + videoId;
        Long videoDuration = redisCache.getCacheObject(videoRedisKey);
        if (videoDuration==null){
            FsUserCourseVideo video = courseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
            videoDuration=video.getDuration();
            redisCache.setCacheObject(videoRedisKey,video.getDuration().toString());
        }
        return videoDuration;
    }

    @Override
    public void checkFsUserWatchStatus() {
        log.info("WXH5-开始更新会员看课中断记录>>>>>");
        // 从 Redis 中获取所有正在看课的用户记录
        Collection<String> keys = redisCache.keys("h5wxuser:watch:heartbeat:*");
        LocalDateTime now = LocalDateTime.now();
        List<FsCourseWatchLog> logs = new ArrayList<>();
        for (String key : keys) {
            FsCourseWatchLog watchLog = new FsCourseWatchLog();
            String[] parts = key.split(":");
            Long userId = Long.parseLong(parts[3]);
            Long videoId = Long.parseLong(parts[4]);
            Long companyUserId = Long.parseLong(parts[5]);
            // 获取最后心跳时间
            String lastHeartbeatStr = redisCache.getCacheObject(key);
            if (lastHeartbeatStr == null) {
                continue; // 如果 Redis 中没有记录，跳过
            }
            LocalDateTime lastHeartbeatTime = LocalDateTime.parse(lastHeartbeatStr);
            Duration duration = Duration.between(lastHeartbeatTime, now);

            watchLog.setVideoId(videoId);
            watchLog.setUserId(userId);
            watchLog.setCompanyUserId(companyUserId);
            // 如果超过一分钟没有心跳，标记为“观看中断”
            if (duration.getSeconds() >= 60) {
                watchLog.setLogType(4);
                // 从 Redis 中删除该记录
                redisCache.deleteObject(key);
            }else {
                watchLog.setLogType(1);
            }
            logs.add(watchLog);
        }
        batchUpdateFsUserCourseWatchLog(logs,100);

    }

    private void processDelCourseLogs(List<Long> logIds) {

    }

    public void batchUpdateFsUserCourseWatchLog(List<FsCourseWatchLog> logs, int batchSize) {
        if (logs == null || logs.isEmpty()) {
            log.info("待更新的日志列表为空，无需处理");
            return;
        }

        // 记录总日志数量
        log.info("开始批量更新日志，总日志数量: {}", logs.size());

        // 分批处理
        for (int i = 0; i < logs.size(); i += batchSize) {
            int end = Math.min(i + batchSize, logs.size());
            List<FsCourseWatchLog> batchList = logs.subList(i, end);

            // 记录当前批次的数量
            log.info("正在更新第 {} 批日志，数量: {}", (i / batchSize) + 1, batchList.size());

            // 执行批量更新
            fsCourseWatchLogMapper.batchUpdateFsUserWatchLog(batchList);

            // 记录当前批次更新完成
            log.info("第 {} 批日志更新完成，数量: {}", (i / batchSize) + 1, batchList.size());
        }

        // 记录全部更新完成
        log.info("所有日志更新完成，总日志数量: {}", logs.size());
    }
}
