package com.fs.live.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import com.fs.common.core.page.PageRequest;
import com.fs.common.exception.base.BaseException;
import com.fs.company.mapper.CompanyMapper;
import com.fs.core.config.WxMaConfiguration;
import com.fs.his.domain.FsStoreProduct;
import com.fs.his.domain.FsUser;
import com.fs.his.mapper.FsUserMapper;
import com.fs.hisStore.domain.FsStoreProductScrm;
import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
import com.fs.live.dto.TemplateMessageSendRequestDTO;
import com.fs.live.enums.MiniAppNotifyTaskStatusEnum;
import com.fs.live.vo.LiveVo;
import com.fs.common.constant.LiveKeysConstant;
import com.fs.common.core.domain.R;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.utils.DateUtils;
import com.fs.common.utils.StringUtils;
import com.fs.common.utils.bean.BeanUtils;
import com.fs.live.domain.*;
import com.fs.live.mapper.*;
import com.fs.live.param.LiveReplayParam;
import com.fs.live.service.*;
import com.fs.live.utils.ProcessManager;
import com.fs.live.vo.*;
import com.fs.system.domain.SysConfig;
import com.fs.system.service.ISysConfigService;
import com.github.pagehelper.PageInfo;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.fs.common.utils.sign.Md5Utils;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 直播Service业务层处理
 *
 * @author fs
 * @date 2025-01-17
 */
@Service
@AllArgsConstructor
@Slf4j
public class LiveServiceImpl implements ILiveService
{

    private final ILiveVideoService liveVideoService;
    private final ILiveDataService liveDataService;

    @Autowired
    private ISysConfigService sysConfigService;


    @Autowired
    private ProcessManager processManager;

    @Autowired
    private ILiveGoodsService liveGoodsService;

    @Autowired
    private ILiveRedConfService liveRedConfService;

    @Autowired
    private ILiveLotteryConfService liveLotteryConfService;

    @Autowired
    private ILiveAutoTaskService liveAutoTaskService;
    @Autowired
    private LiveLotteryProductConfMapper liveLotteryProductConfMapper;
    @Autowired
    private FsStoreProductScrmMapper fsStoreProductScrmMapper;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private LiveMapper baseMapper;
    @Autowired
    private LiveMiniprogramSubNotifyTaskMapper liveMiniprogramSubNotifyTaskMapper;
    @Autowired
    private LiveCouponIssueMapper liveCouponIssueMapper;
    @Autowired
    private FsUserMapper fsUserMapper;
    @Autowired
    private CompanyMapper companyMapper;
    @Autowired
    private LiveCouponMapper liveCouponMapper;

    private static String TOKEN_VALID_CODE = "40001";

    private static volatile Integer version = 0;



    /**
     * 查询直播
     *
     * @param liveId 直播主键
     * @return 直播
     */
    @Override
    public Live selectLiveByLiveId(Long liveId){
        Live byId = baseMapper.selectLiveByLiveId(liveId);
        List<LiveVideo> videos = liveVideoService.listByLiveId(liveId, 1);
        if(!videos.isEmpty()){
            LiveVideo liveVideo = videos.get(0);
            byId.setVideoUrl(liveVideo.getVideoUrl());
            byId.setDuration(liveVideo.getDuration());
            byId.setVideoId(liveVideo.getVideoId());
            byId.setVideoType(liveVideo.getVideoType());
        }
        return byId;
    }

    @Override
    public LiveConfigVo currentActivities(Long liveId) {
        LiveGoodsVo liveGoodsVo = liveGoodsService.showGoods(liveId);
        List<LiveRedConf> liveRedConfs = liveRedConfService.selectActivedRed(liveId);
        List<LiveLotteryConfVo> liveLotteryConfs = liveLotteryConfService.selectActivedLottery(liveId);
        List<Long> lotteryIds = liveLotteryConfs.stream().map(LiveLotteryConfVo::getLotteryId).collect(Collectors.toList());
        if (!lotteryIds.isEmpty()) {
            List<LiveLotteryProductListVo> products = liveLotteryProductConfMapper.selectLiveLotteryProductConfByLotteryIds(lotteryIds);
            for (LiveLotteryConfVo liveLotteryConf : liveLotteryConfs) {
                liveLotteryConf.setProducts(products.stream().filter(product -> product.getLotteryId().equals(liveLotteryConf.getLotteryId())).collect(Collectors.toList()));
            }
        }
        return LiveConfigVo.builder().liveRedConfs(liveRedConfs).liveLotteryConfs(liveLotteryConfs).liveGoodsVo(liveGoodsVo).build();
    }

    @Override
    public List<Live> selectNoEndLiveList() {
        return baseMapper.selectNoEndLiveList();
    }

    @Override
    public void updateBatchById(List<Live> list) {
        baseMapper.updateLiveList(list);
    }

    @Override
    public void updateStatusAndTimeBatchById(List<Live> list) {
        baseMapper.updateStatusAndTimeBatchById(list);
    }

    @Override
    public List<Live> asyncToCache() {
        // 同步直播间数据到缓存
        List<Live> list = liveList();
        log.info("开始同步直播间数据到缓存，共{}条数据", list.size());
        ThreadUtil.execute(()->{
            // 清空原有的 ZSet 数据
            redisCache.deleteObject(LiveKeysConstant.LIVE_HOME_PAGE_LIST);
            for (Live live : list) {
                redisCache.zSetAdd(LiveKeysConstant.LIVE_HOME_PAGE_LIST, JSON.toJSONString(live), live.getCreateTime().getTime());
                redisCache.expire(LiveKeysConstant.LIVE_HOME_PAGE_LIST, LiveKeysConstant.LIVE_HOME_PAGE_LIST_EXPIRE, TimeUnit.SECONDS);
            }
            log.info("直播间数据同步到缓存完成");
        });
        return list;
    }

    @Override
    public LiveVo asyncToCacheLiveDetail(Long id) {
        LocalDateTime now = LocalDateTime.now();
		Live live = selectLiveByLiveId(id);
		if(ObjectUtil.isEmpty(live)) {
            return null;
        }
		// Long storeId = liveGoodsService.getStoreIdByLiveId(live.getLiveId());
		LiveVo liveVo = new LiveVo();
        // liveVo.setStoreId(storeId);
		BeanUtils.copyProperties(live, liveVo);
		liveVo.setNowDuration(200L);
        LiveVideo liveVideo = liveVideoService.selectLiveVideoByLiveIdAndType(id, 3);
        if (liveVideo != null) {
            liveVo.setPreviewUrl(liveVideo.getVideoUrl());
            liveVo.setPreviewVideoType(liveVideo.getVideoType());
            liveVo.setPreviewVideoId(liveVideo.getVideoId());
        }
		if(live.getStatus() == 2){
			long seconds = live.getStartTime().until(now, ChronoUnit.SECONDS);
			liveVo.setNowDuration(seconds);
		}
        ThreadUtil.execute(()->{
            log.info("同步直播间详情数据到缓存{}", id);
            redisCache.deleteObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_DETAIL, live.getLiveId()));
            redisCache.setCacheObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_DETAIL, live.getLiveId()), liveVo,LiveKeysConstant.LIVE_HOME_PAGE_DETAIL_EXPIRE, TimeUnit.SECONDS);
            log.info("直播间数据同步到缓存完成");
        });

        return liveVo;
    }

    @Override
    public LiveConfigVo asyncToCacheLiveConfig(Long liveId) {
        LiveConfigVo liveConfigVo = currentActivities(liveId);
        ThreadUtil.execute(()->{
            log.info("同步配置信息到缓存{}", liveConfigVo);
            redisCache.deleteObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG, liveId,liveId));
            redisCache.setCacheObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_DETAIL, liveId), liveConfigVo,LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_EXPIRE, TimeUnit.SECONDS);
            log.info("直播间数据同步到缓存完成");
        });
        return liveConfigVo;
    }

    @Override
    public List<Live> liveCompanyList(Long companyId) {
        return baseMapper.liveCompanyList(companyId);
    }

    @Override
    public R subNotifyLive(HashMap<String, Object> param) {
        LiveMiniprogramSubNotifyTask notifyTask = new LiveMiniprogramSubNotifyTask();
        notifyTask.setPage("/pages_course/living?liveId=" + param.get("liveId"));
        notifyTask.setTaskName("直播间预约提醒");
        notifyTask.setTemplateId((String) param.get("templateId"));
        FsUser fsUser = fsUserMapper.selectFsUserById(Long.valueOf((Integer) param.get("userId")));
        String maOpenId = fsUser.getMaOpenId();
        notifyTask.setTouser(maOpenId);
        notifyTask.setPage(String.valueOf(1));

        notifyTask.setCreateTime(LocalDateTime.now());
        // 状态等待执行
        notifyTask.setStatus(MiniAppNotifyTaskStatusEnum.WAITING.getValue());
        notifyTask.setRetryCount(0);
        notifyTask.setMaxRetries(3);
        Map<String, TemplateMessageSendRequestDTO.TemplateDataValue> data = new HashMap<>();
        HashMap<String,String> mapData = (HashMap<String, String>) param.get("data");
        // 定义日期时间格式器，注意与字符串格式完全匹配
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        notifyTask.setUpdateTime(LocalDateTime.parse(mapData.get("date7"), formatter).minusSeconds(5));
        mapData.forEach((k,v)->{
            TemplateMessageSendRequestDTO.TemplateDataValue dataValue = new TemplateMessageSendRequestDTO.TemplateDataValue();
            dataValue.setValue(mapData.get(k));
            data.put(k, dataValue);
        });
        notifyTask.setData(JSON.toJSONString(data));
//        return R.ok("success");
        liveMiniprogramSubNotifyTaskMapper.insert(notifyTask);

        return R.ok("success");
    }

    @Override
    public Integer updateLiveIsAudit(Live live) {
        return baseMapper.updateLiveIsAudit(live);
    }



    /**
     * 查询企业直播
     * @param liveId            直播ID
     * @param companyId         企业ID
     * @param companyUserId     企业用户ID
     * @return Live
     */
    @Override
    public Live selectLiveByLiveIdAndCompanyIdAndCompanyUserId(Long liveId, Long companyId, Long companyUserId){
//        Live byId = baseMapper.selectLiveByLiveIdAndCompanyIdAndCompanyUserId(liveId, companyId, companyUserId);
        Live byId = baseMapper.selectLiveByLiveIdAndCompanyId(liveId, companyId);
        if(byId == null){
            log.error("查询直播失败，未找到该直播ID :{}  companyId :{}  companyUserId :{}", liveId, companyId, companyUserId);
            return null ;
        }
        List<LiveVideo> videos = liveVideoService.listByLiveId(liveId, 1);
        if(!videos.isEmpty()){
            LiveVideo liveVideo = videos.get(0);
            byId.setVideoUrl(liveVideo.getVideoUrl());
            byId.setDuration(liveVideo.getDuration());
        }
        return byId;
    }

    /**
     * 查询直播列表
     *
     * @param live 直播
     * @return 直播
     */
    @Override
    public List<Live> selectLiveList(Live live)
    {
        return baseMapper.selectLiveList(live);
    }


    /**
     * 查询直播列表
     *
     * @param
     * @return 直播
     */
    @Override
    public List<Live> selectLiveToStudent(String liveName,String status,String startTime,String finishTime)
    {
        return baseMapper.selectLiveToStudent(liveName,status,startTime,finishTime);
    }

    /**
     * 新增直播
     *
     * @param live 直播
     * @return 结果
     */
    @Override
    public int insertLive(Live live){


        if(live.getLiveType() == 2 && StringUtils.isEmpty(live.getVideoUrl())) {
            throw new BaseException("录播必须上传视屏");
        }
        live.setCreateTime(DateUtils.getNowDate());
        live.setIsDel(0);
        live.setIsAudit(0);
        live.setStartTime(live.getStartTime().withSecond(1));
        live.setStatus(1);
        int save = baseMapper.insertLive(live);
        // 录播 才有视频
        LiveVideo liveVideo = new LiveVideo();
        liveVideo.setLiveId(live.getLiveId());
        liveVideo.setVideoUrl(live.getVideoUrl());
        liveVideo.setVideoType(1);
        liveVideo.setDuration(live.getDuration());
        liveVideo.setFileSize(live.getFileSize());
        liveVideoService.insertLiveVideo(liveVideo);

        //新增直播数据表
        LiveData liveData = new LiveData();
        liveData.setLiveId(live.getLiveId());
        liveData.setPageViews(0L);
        liveData.setUniqueVisitors(0L);
        liveData.setTotalViews(0L);
        liveData.setUniqueViewers(0L);
        liveData.setPeakConcurrentViewers(0L);
        liveData.setLikes(0L);
        liveData.setFavouriteNum(0L);
        liveData.setFollowNum(0L);
        liveDataService.insertLiveData(liveData);
        return save > 0 ? 1 : 0;
    }

    @Override
    @Transactional
    public int updateLiveEntity(Live live) {
        return baseMapper.updateLive( live);
    }

    @Override
    public void updateGlobalVisible(long liveId, Integer status) {
        baseMapper.updateGlobalVisible(liveId, status);
    }

    @Override
    public String getGotoWxAppLiveLink(String linkStr, String appId) {
        CloseableHttpClient client = null;
        try {
            client = HttpClients.createDefault();
            String[] split = linkStr.split("\\?");
            if (split.length == 2 && split[0].length() > 0 && split[1].length() > 0) {
                //处理页面路径
                String pageUrl = split[0];
                if (pageUrl.startsWith("/")) {
                    pageUrl = pageUrl.substring(1);
                }
                //处理参数
                String query = split[1];
                query = URLEncoder.encode(query, StandardCharsets.UTF_8.toString());
//                String json = configService.selectConfigByKey("course.config");
//                CourseConfig config = JSON.parseObject(json, CourseConfig.class);
//                String miniprogramAppid = config.getMiniprogramAppid();
//                if (StringUtils.isBlank(miniprogramAppid)) {
//                    return "未配置点播小程序id";
//                }
                //获取微信token
                final WxMaService wxService = WxMaConfiguration.getMaService(appId);
                String token = wxService.getAccessToken();
                log.info("小程序TOKEN值-------->刷新前TOKEN：{}", token);
                HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/generate_urllink?access_token=" + token);
                JSONObject bodyObj = new JSONObject();
                bodyObj.put("path", pageUrl);
                bodyObj.put("query", query);
                log.info("微信小程序请求参数打印：{}", bodyObj.toJSONString());
                StringEntity entity = new StringEntity(bodyObj.toJSONString(),"UTF-8");
                httpPost.setEntity(entity);
                httpPost.setHeader("Content-type", "application/json");
                httpPost.setHeader("cache-control","max-age=0");
                HttpEntity response = client.execute(httpPost).getEntity();
                String responseString = EntityUtils.toString(response);
                log.info("微信小程序接口响应数据:{}", responseString);
                JSONObject jsonObject = JSONObject.parseObject(responseString);

                if(TOKEN_VALID_CODE.equals(jsonObject.getString("errcode"))){
                    Integer curVersion =  Integer.valueOf(version);
                    synchronized (TOKEN_VALID_CODE){
                        if(curVersion.equals(version)){
                            log.info("小程序TOKEN：40001进入强制刷新-------->刷新前TOKEN：{}", token);
                            wxService.getAccessToken(true);
                            version = version.equals(Integer.MAX_VALUE) ? 0 : curVersion + 1;
                            log.info("小程序TOKEN：40001进入强制刷新-------->刷新后TOKEN：{}", wxService.getAccessToken());
                        }
                        return getGotoWxAppLiveLink(linkStr,appId);
                    }
                }

                if(null != jsonObject && !jsonObject.isEmpty() && jsonObject.containsKey("url_link")){
                    return jsonObject.getString("url_link");
                }
            } else {
                return "页面链接错误，获取失败";
            }

        } catch (WxErrorException e) {
            throw new RuntimeException(e);
        } catch (ClientProtocolException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return "";
    }



    @Override
    public R liveListAll(PageRequest pageRequest) {
        int start = (pageRequest.getCurrentPage() - 1) * pageRequest.getPageSize();
        int end = pageRequest.getCurrentPage() * pageRequest.getPageSize() - 1;
        List<Live> lives = baseMapper.liveListAll();

        // 对结果进行分页处理
        List<Live> pageLives = lives.stream()
                .skip(start)
                .limit(pageRequest.getPageSize())
                .collect(Collectors.toList());

        PageInfo<Live> result = new PageInfo<>(pageLives);
        result.setTotal(lives.size());
        return R.ok().put("data", result);
    }

    @Override
    public List<Live> listToLiveNoEnd(Live live) {
        return baseMapper.listToLiveNoEnd(live);
    }

    /**
     * 修改直播
     *
     * @param live 直播
     * @return 结果
     */
    @Override
    public int updateLive(Live live){
        live.setUpdateTime(DateUtils.getNowDate());
        List<LiveVideo> videos = liveVideoService.listByLiveId(live.getLiveId(), 1);
        if(!videos.isEmpty()){
            LiveVideo liveVideo = videos.get(0);
            liveVideo.setVideoUrl(live.getVideoUrl());
            liveVideo.setUpdateTime(DateUtils.getNowDate());
            liveVideo.setDuration(live.getDuration());
            liveVideo.setFileSize(live.getFileSize());
            liveVideoService.updateLiveVideo(liveVideo);
        }
        if (StringUtils.isNotEmpty(live.getLiveConfig())) {
            LiveReplayParam liveReplayParam = JSON.parseObject(live.getLiveConfig(), LiveReplayParam.class);
            if (liveReplayParam.getIsPlaybackOpen()) {
                liveReplayParam.setStartTime(live.getFinishTime());
                if ("permanent".equals(liveReplayParam.getValidityType())) {
                    liveReplayParam.setFinishTime(null);
                } else if ("days".equals(liveReplayParam.getValidityType())) {
                    liveReplayParam.setFinishTime(live.getFinishTime().plusDays(liveReplayParam.getValidDays()));
                }
                live.setLiveType(3);
                live.setStatus(4);
            } else {
                live.setLiveType(1);
                live.setStatus(3);
            }
            live.setLiveConfig(JSON.toJSONString(liveReplayParam));

        }
        int result = baseMapper.updateLive(live);
        liveAutoTaskService.recalcLiveAutoTask(live);

        return result;
    }

    /**
     * 批量删除直播
     *
     * @param liveIds 需要删除的直播主键
     * @return 结果
     */
    @Override
    public int deleteLiveByLiveIds(Long[] liveIds){
        return baseMapper.deleteLiveByLiveIds(liveIds);
    }

    /**
     * 删除直播信息
     *
     * @param liveId 直播主键
     * @return 结果
     */
    @Override
    public int deleteLiveByLiveId(Long liveId)
    {
        return baseMapper.deleteLiveByLiveId(liveId);
    }

    @Override
    public List<Live> liveList() {
        List<Long> companyIds = companyMapper.selectLiveShowCompanyId();
        List<Live> livesList = baseMapper.liveShowList(companyIds);
        try {
            // 查询准备开播的直播间
            livesList.addAll(baseMapper.selectLiveShowReadyStartLiveList(companyIds));
        }catch (Exception e){
            log.error("查询准备开播的直播间异常", e);
        }
        return livesList;
    }

    @Override
    public R createLiveRoom(Long liveId) {
        Live live = this.selectLiveByLiveId(liveId);
        if (live == null) {
            return R.error("直播不存在");
        }

        if(live.getStatus() == 2){
            return R.ok().put("rtmpUrl", live.getRtmpUrl());
        }
        if (live.getIsAudit() != 1) {
            return R.error("您未通过审核");
        }
        if (live.getIsShow() != 1) {
            return R.error("您未拥有直播权限");
        }
        SysConfig sysConfig = sysConfigService.selectConfigByConfigKey("living.config");
        Map<String, String> livingConfigMap = JSON.parseObject(sysConfig.getConfigValue(), Map.class);
        if (livingConfigMap == null || livingConfigMap.isEmpty()) {
            return R.error("缺失直播配置");
        }
        String rtmpPushUrl = generateRtmpPushUrl(livingConfigMap.get("domain"), livingConfigMap.get("app"), liveId.toString());
        String hlvPlayUrl = generateHlvPlayUrl(livingConfigMap.get("http"), livingConfigMap.get("app"), liveId.toString());
        live.setRtmpUrl(rtmpPushUrl);
        live.setFlvHlsUrl(hlvPlayUrl);
        live.setStartTime(LocalDateTime.now());
        live.setStatus(2);
        live.setLiveType(1);
        this.updateLive(live);
        LiveData liveData = new LiveData();
        liveData.setLiveId(liveId);
        liveData.setPageViews(0L);
        liveData.setUniqueVisitors(0L);
        liveData.setTotalViews(0L);
        liveData.setUniqueViewers(0L);
        liveData.setPeakConcurrentViewers(0L);
        liveData.setLikes(0L);
        liveData.setFavouriteNum(0L);
        liveData.setFollowNum(0L);
        liveDataService.insertLiveData(liveData);

        return R.ok().put("rtmpUrl", rtmpPushUrl);
    }

    @Override
    public R getLiveRoom(String liveId) {
        Live live = this.selectLiveByLiveId(Long.valueOf(liveId));
        if (live == null) {
            return R.error("未查询到直播间");
        }
        if (live.getStatus() != 2 && live.getLiveType() == 1) {
            return R.error("直播未开始");
        }
        return R.ok().put("livingUrl", live.getFlvHlsUrl()).put("liveStatus", live.getStatus());
    }

    @Override
    public R checkLiving(Map<String, String> payload) {
        if (payload == null || payload.isEmpty() || !payload.containsKey("userId") || !payload.containsKey("companyId")) {
            return R.error("系统错误，请联系管理员");
        }
        //
//        FsUserTalent fsUserTalent = fsUserTalentService.selectTalentByUserId(payload.get("userId"));
//        if (fsUserTalent == null) {
//            return R.error("您还未成为达人，无法开启直播!");
//        }
//        Live live = baseMapper.selectLiveByUserIdAndCompanyId(String.valueOf(fsUserTalent.getTalentId()), payload.get("companyId"));
        Live live = baseMapper.selectLiveByTalentId("42");
        if (live == null) {
            return R.error("您未拥有直播权限!");
        }
        if (live.getIsAudit() == 0) {
            return R.error("直播间审核中!");
        }


        return R.ok().put("data", live);
    }

    @Override
    public R closeLiving(Map<String, String> payload) {
        if (!payload.containsKey("liveId")) {
            return R.error("直播间id缺失");
        }
        if (!payload.containsKey("userId")) {
            return R.error("关闭直播异常，请联系管理员！");
        }
//        FsUserTalent fsUserTalent = fsUserTalentService.selectTalentByUserId(payload.get("userId"));
        //
        Live live = baseMapper.selectLiveByTalentId("42");
        live.setStatus(1);
        live.setFinishTime(LocalDateTime.now());
        live.setLiveType(2);
        baseMapper.updateLive(live);
        return R.ok();
    }
    /**
     * 循环播放
     * @param live 直播
     * @return R
     */
    @Override
    public R startLoopPlay(Live live) {
        try {
            Live curLive = baseMapper.selectLiveByLiveId(live.getLiveId());
            if (curLive == null) {
                return R.error("直播间不存在");
            }
            LiveVideo curLiveVideo = liveVideoService.selectLiveVideoByLiveId(live.getLiveId());
            if (curLiveVideo == null) {
                return R.error("录播不存在");
            }
            Date now = new Date();

            // 生成唯一的流密钥
            String streamKey = "stream_" + live.getLiveId() + "_" + System.currentTimeMillis();

            // 构建FFmpeg推流命令
            String ffmpegCmd = buildFFmpegCommand(curLiveVideo.getVideoUrl(), streamKey);

            // 启动推流进程
            Process process = processManager.startProcess(ffmpegCmd);

            // 保存流信息到数据库
            curLive.setLiveType(2);
            curLive.setRtmpUrl("rtmp://your-srs-server/live/" + streamKey);
            curLive.setUpdateTime(now);


            baseMapper.updateLive(curLive);

            return R.ok();
        } catch (Exception e) {
            return R.error("启动推流失败: " + e.getMessage());
        }
    }
    /**
     * 停止循环播放
     * @param live 直播
     * @return R
     */
    @Override
    public R stopLoopPlay(Live live) {
        try {
            Live curLive = baseMapper.selectLiveByLiveId(live.getLiveId());
            if (curLive == null) {
                return R.error("直播间不存在");
            }
            // 停止推流进程
            boolean stopped = processManager.stopProcess(curLive.getRemark());
            if (!stopped) {
                return R.error("停止播放失败");
            }
            // 更新流状态
            curLive.setStatus(2);
            curLive.setUpdateTime(new Date());
            baseMapper.updateLive(curLive);

            return R.ok();
        } catch (Exception e) {
            return R.error("停止播放失败: " + e.getMessage());
        }
    }

    @Override
    public R handleShelfOrUn(LiveListVo listVo) {
        int updatedCount = baseMapper.updateBatchLiveList(listVo);
        if (updatedCount > 0) {
            return R.ok("操作成功");
        }
        return R.error();
    }

    @Override
    public R handleDeleteSelected(LiveListVo listVo) {
        int deleteCount = baseMapper.deleteBatchLiveList(listVo);
        if (deleteCount > 0) {
            return R.ok("操作成功");
        }
        return R.error("删除失败");
    }

    @Override
    public R finishLive(Live live) {
        Live exist = baseMapper.selectLiveByLiveId(live.getLiveId());
        if (exist == null) {
            return R.error("直播间不存在");
        }
        if (exist.getStatus() == 3) {
            return R.error("直播已结束");
        }

        exist.setStatus(3);
        exist.setFinishTime(LocalDateTime.now());
        exist.setUpdateTime(new Date());

        baseMapper.updateLive(exist);

        return R.ok();
    }

    @Override
    public R startLive(Live live) {
        Live exist = baseMapper.selectLiveByLiveId(live.getLiveId());
        if (exist == null) {
            return R.error("直播间不存在");
        }
        if (exist.getStatus() == 2) {
            return R.error("直播已开始");
        }
        if (exist.getIsAudit() != 1) {
            return R.error("您未通过审核");
        }
        if (exist.getIsShow() != 1) {
            return R.error("直播已下架");
        }
        String rtmpPushUrl = generateRtmpPushUrl("rtmp://200149.push.tlivecloud.com", "live", exist.getLiveId().toString());
        String hlvPlayUrl = generateHlvPlayUrl("https://live.test.ifeiyu100.com", "live", exist.getLiveId().toString());
        Date now = new Date();
        exist.setRtmpUrl(rtmpPushUrl);
        exist.setFlvHlsUrl(hlvPlayUrl);

        exist.setStatus(2);
        exist.setUpdateTime(now);
        exist.setFinishTime( null);
        exist.setStartTime(LocalDateTime.now());
        baseMapper.updateLive(exist);
        List<LiveAutoTask> liveAutoTasks = liveAutoTaskService.selectNoActivedByLiveId(exist.getLiveId(), new Date());
        liveAutoTasks.forEach(liveAutoTask -> {
            liveAutoTask.setCreateTime(null);
            liveAutoTask.setUpdateTime(null);
            redisCache.redisTemplate.opsForZSet().add("live:auto_task:" + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
            redisCache.redisTemplate.expire("live:auto_task:"+live.getLiveId(), 1, TimeUnit.DAYS);
        });


        return R.ok();
    }

    @Override
    public R handleShelfOrUnAdmin(LiveListVo listVo) {
        baseMapper.handleShelfOrUnAdmin(listVo);
        return R.ok();
    }

    @Override
    public R handleDeleteSelectedAdmin(LiveListVo listVo) {
        baseMapper.handleDeleteSelectedAdmin(listVo);
        return R.ok();
    }

    @Override
    @Transactional
    public R copyLive(Live live) {
        Long existLiveId = live.getLiveId();
        Date now = new Date();
        Live exist = baseMapper.selectLiveByLiveId(existLiveId);
        if (exist == null) {
            return R.error("直播间不存在");
        }

        // 1. 复制直播间基础信息
        Long newLiveId = copyLiveBasicInfo(exist, live, now);

        // 2. 创建直播数据
        createLiveData(newLiveId);

        // 3. 复制直播视频
        copyLiveVideos(existLiveId, newLiveId, now);

        // 4. 获取所有自动化任务并按类型分组
        List<LiveAutoTask> allTasks = liveAutoTaskService.selectLiveAutoTaskByLiveId(existLiveId);
        Map<Long, List<LiveAutoTask>> tasksByType = allTasks.stream()
                .collect(Collectors.groupingBy(LiveAutoTask::getTaskType));

        // 5. 复制弹幕任务（taskType=3）
        copyBarrageTasks(tasksByType.get(3L), newLiveId, now);

        // 6. 复制红包配置及任务（taskType=2）
        Map<Long, Long> redIdMapping = copyRedConfs(existLiveId, newLiveId, tasksByType.get(2L), now);

        // 7. 复制抽奖配置及任务（taskType=4）
        Map<Long, Long> lotteryIdMapping = copyLotteryConfs(existLiveId, newLiveId, tasksByType.get(4L), now);

        // 8. 复制商品、商品任务、上下架任务及优惠券关系
        Map<Long, Long> goodsIdMapping = copyGoodsAndTasks(existLiveId, newLiveId, live,
                tasksByType.get(1L), tasksByType.get(6L), now);

        // 9. 复制优惠券关系
        copyCouponRelations(existLiveId, newLiveId, goodsIdMapping);

        // 10. 复制优惠券自动化任务（taskType=5）
        copyCouponTasks(tasksByType.get(5L), newLiveId, goodsIdMapping, now);

        return R.ok("复制成功");
    }

    /**
     * 复制直播间基础信息
     */
    private Long copyLiveBasicInfo(Live exist, Live live, Date now) {
        Live liveEntity = new Live();
        BeanUtils.copyBeanProp(liveEntity, exist);
        liveEntity.setLiveId(null);
        liveEntity.setCompanyId(live.getCompanyId());
        liveEntity.setCompanyUserId(live.getCompanyUserId());
        liveEntity.setRtmpUrl(null);
        liveEntity.setFlvHlsUrl(null);
        liveEntity.setFinishTime(null);
        liveEntity.setIsAudit(0);
        liveEntity.setStatus(1);
        liveEntity.setCreateTime(now);
        baseMapper.insertLive(liveEntity);
        if (liveEntity.getLiveId() == null) {
            throw new RuntimeException("插入直播间异常");
        }
        return liveEntity.getLiveId();
    }

    /**
     * 创建直播数据
     */
    private void createLiveData(Long liveId) {
        LiveData liveData = new LiveData();
        liveData.setLiveId(liveId);
        liveData.setPageViews(0L);
        liveData.setUniqueVisitors(0L);
        liveData.setTotalViews(0L);
        liveData.setUniqueViewers(0L);
        liveData.setPeakConcurrentViewers(0L);
        liveData.setLikes(0L);
        liveData.setFavouriteNum(0L);
        liveData.setFollowNum(0L);
        liveDataService.insertLiveData(liveData);
    }

    /**
     * 复制直播视频
     */
    private void copyLiveVideos(Long existLiveId, Long newLiveId, Date now) {
        List<LiveVideo> liveVideos = liveVideoService.selectLiveVideosByLiveId(existLiveId);
        for (LiveVideo liveVideo : liveVideos) {
            LiveVideo videoEntity = new LiveVideo();
            BeanUtils.copyBeanProp(videoEntity, liveVideo);
            videoEntity.setVideoId(null);
            videoEntity.setLiveId(newLiveId);
            videoEntity.setCreateTime(now);
            liveVideoService.insertLiveVideo(videoEntity);
        }
    }

    /**
     * 复制弹幕任务（taskType=3）
     */
    private void copyBarrageTasks(List<LiveAutoTask> tasks, Long newLiveId, Date now) {
        if (tasks == null || tasks.isEmpty()) {
            return;
        }
        List<LiveAutoTask> addList = new ArrayList<>();
        for (LiveAutoTask task : tasks) {
            LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now, null);
            addList.add(newTask);
            if (addList.size() >= 100) {
                liveAutoTaskService.batchInsertLiveAutoTask(addList);
                addList.clear();
            }
        }
        if (!addList.isEmpty()) {
            liveAutoTaskService.batchInsertLiveAutoTask(addList);
        }
    }

    /**
     * 复制红包配置及任务
     */
    private Map<Long, Long> copyRedConfs(Long existLiveId, Long newLiveId,
                                         List<LiveAutoTask> redTasks, Date now) {
        Map<Long, Long> redIdMapping = new HashMap<>();
        if (redTasks == null) {
            redTasks = Collections.emptyList();
        }
        Map<Long, LiveAutoTask> redTaskMap = redTasks.stream()
                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "redId"),
                        Function.identity(), (existing, replacement) -> existing));

        List<LiveRedConf> liveRedConfs = liveRedConfService.selectByLiveId(existLiveId);
        for (LiveRedConf liveRedConf : liveRedConfs) {
            LiveRedConf newRedConf = new LiveRedConf();
            BeanUtils.copyBeanProp(newRedConf, liveRedConf);
            newRedConf.setRedId(null);
            newRedConf.setLiveId(newLiveId);
            newRedConf.setRedStatus(0L);
            newRedConf.setCreateTime(now);
            newRedConf.setTotalSend(0L);
            liveRedConfService.insertLiveRedConf(newRedConf);

            redIdMapping.put(liveRedConf.getRedId(), newRedConf.getRedId());

            LiveAutoTask task = redTaskMap.get(liveRedConf.getRedId());
            if (task != null) {
                LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now,
                        JSON.toJSONString(newRedConf));
                liveAutoTaskService.directInsertLiveAutoTask(newTask);
            }
        }
        return redIdMapping;
    }

    /**
     * 复制抽奖配置及任务
     */
    private Map<Long, Long> copyLotteryConfs(Long existLiveId, Long newLiveId,
                                             List<LiveAutoTask> lotteryTasks, Date now) {
        Map<Long, Long> lotteryIdMapping = new HashMap<>();
        if (lotteryTasks == null) {
            lotteryTasks = Collections.emptyList();
        }
        Map<Long, LiveAutoTask> lotteryTaskMap = lotteryTasks.stream()
                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "lotteryId"),
                        Function.identity(), (existing, replacement) -> existing));

        List<LiveLotteryConf> liveLotteryConfs = liveLotteryConfService.selectByLiveId(existLiveId);
        if (!liveLotteryConfs.isEmpty()) {
            List<Long> lotteryIds = liveLotteryConfs.stream()
                    .map(LiveLotteryConf::getLotteryId).collect(Collectors.toList());
            List<LiveLotteryProductConf> products = liveLotteryProductConfMapper.selectEntityByIds(lotteryIds);
            Map<Long, List<LiveLotteryProductConf>> productsByLotteryId = products.stream()
                    .collect(Collectors.groupingBy(LiveLotteryProductConf::getLotteryId));

            for (LiveLotteryConf liveLotteryConf : liveLotteryConfs) {
                LiveLotteryConf newLotteryConf = new LiveLotteryConf();
                BeanUtils.copyBeanProp(newLotteryConf, liveLotteryConf);
                newLotteryConf.setLotteryId(null);
                newLotteryConf.setLiveId(newLiveId);
                newLotteryConf.setLotteryStatus(String.valueOf(0));
                newLotteryConf.setCreateTime(now);
                newLotteryConf.setUpdateTime(now);
                liveLotteryConfService.insertLiveLotteryConf(newLotteryConf);

                lotteryIdMapping.put(liveLotteryConf.getLotteryId(), newLotteryConf.getLotteryId());

                // 复制奖品
                List<LiveLotteryProductConf> lotteryProducts = productsByLotteryId.get(liveLotteryConf.getLotteryId());
                if (lotteryProducts != null) {
                    for (LiveLotteryProductConf product : lotteryProducts) {
                        LiveLotteryProductConf newProduct = new LiveLotteryProductConf();
                        BeanUtils.copyBeanProp(newProduct, product);
                        newProduct.setId(null);
                        newProduct.setLotteryId(newLotteryConf.getLotteryId());
                        liveLotteryProductConfMapper.insertLiveLotteryProductConf(newProduct);
                    }
                }

                LiveAutoTask task = lotteryTaskMap.get(liveLotteryConf.getLotteryId());
                if (task != null) {
                    LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now,
                            JSON.toJSONString(newLotteryConf));
                    liveAutoTaskService.directInsertLiveAutoTask(newTask);
                }
            }
        }
        return lotteryIdMapping;
    }

    /**
     * 复制商品、商品任务、上下架任务
     */
    private Map<Long, Long> copyGoodsAndTasks(Long existLiveId, Long newLiveId, Live live,
                                              List<LiveAutoTask> goodsTasks, List<LiveAutoTask> shelfTasks, Date now) {
        Map<Long, Long> goodsIdMapping = new HashMap<>();
        if (goodsTasks == null) {
            goodsTasks = Collections.emptyList();
        }
        if (shelfTasks == null) {
            shelfTasks = Collections.emptyList();
        }

        Map<Long, LiveAutoTask> goodsTaskMap = goodsTasks.stream()
                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "goodsId"),
                        Function.identity(), (existing, replacement) -> existing));
        Map<Long, LiveAutoTask> shelfTaskMap = shelfTasks.stream()
                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "goodsId"),
                        Function.identity(), (existing, replacement) -> existing));

        LiveGoods queryParam = new LiveGoods();
        queryParam.setLiveId(existLiveId);
        List<LiveGoodsVo> goodsList = liveGoodsService.selectProductListByLiveId(queryParam);

        if (!goodsList.isEmpty()) {
            List<Long> goodsProductIds = goodsList.stream()
                    .map(LiveGoodsVo::getProductId).collect(Collectors.toList());
            Map<Long, FsStoreProductScrm> goodsMap = fsStoreProductScrmMapper
                    .selectFsStoreProductByProductIds(goodsProductIds).stream()
                    .collect(Collectors.toMap(FsStoreProductScrm::getProductId, Function.identity()));

            for (LiveGoodsVo liveGoods : goodsList) {
                LiveGoods newGoods = new LiveGoods();
                BeanUtils.copyBeanProp(newGoods, liveGoods);
                newGoods.setGoodsId(null);
                newGoods.setLiveId(newLiveId);
                newGoods.setCreateTime(now);
                newGoods.setIsShow(false);
                newGoods.setCompanyId(live.getCompanyId());
                newGoods.setCompanyUserId(live.getCompanyUserId());
                newGoods.setStock(goodsMap.containsKey(liveGoods.getProductId())
                        ? goodsMap.get(liveGoods.getProductId()).getStock() : 0);
                liveGoodsService.insertLiveGoods(newGoods);

                goodsIdMapping.put(liveGoods.getGoodsId(), newGoods.getGoodsId());

                // 复制商品推送任务（taskType=1）
                LiveAutoTask goodsTask = goodsTaskMap.get(liveGoods.getGoodsId());
                if (goodsTask != null) {
                    // 使用新的goodsId创建LiveGoodsVo对象
                    LiveGoodsVo newGoodsVo = new LiveGoodsVo();
                    BeanUtils.copyBeanProp(newGoodsVo, liveGoods);
                    newGoodsVo.setGoodsId(newGoods.getGoodsId());
                    LiveAutoTask newTask = createAutoTaskEntity(goodsTask, newLiveId, now,
                            JSON.toJSONString(newGoodsVo));
                    liveAutoTaskService.directInsertLiveAutoTask(newTask);
                }

                // 复制上下架任务（taskType=6）
                LiveAutoTask shelfTask = shelfTaskMap.get(liveGoods.getGoodsId());
                if (shelfTask != null) {
                    JSONObject contentJson = JSON.parseObject(shelfTask.getContent());
                    contentJson.put("goodsId", newGoods.getGoodsId());
                    LiveAutoTask newTask = createAutoTaskEntity(shelfTask, newLiveId, now,
                            contentJson.toJSONString());
                    liveAutoTaskService.directInsertLiveAutoTask(newTask);
                }
            }
        }
        return goodsIdMapping;
    }

    /**
     * 复制优惠券关系
     */
    private void copyCouponRelations(Long existLiveId, Long newLiveId, Map<Long, Long> goodsIdMapping) {
        List<LiveCouponIssueRelation> relations = liveCouponIssueMapper.selectRelationByLiveId(existLiveId);
        for (LiveCouponIssueRelation relation : relations) {
            LiveCouponIssueRelation newRelation = new LiveCouponIssueRelation();
            BeanUtils.copyBeanProp(newRelation, relation);
            newRelation.setLiveId(newLiveId);
            newRelation.setIsShow(0);
            // 更新goodsId映射
            if (relation.getGoodsId() != null && goodsIdMapping.containsKey(relation.getGoodsId())) {
                newRelation.setGoodsId(goodsIdMapping.get(relation.getGoodsId()));
            }
            liveCouponIssueMapper.insertLiveCouponIssueRelation(newRelation);
        }
    }

    /**
     * 复制优惠券自动化任务（taskType=5）
     */
    private void copyCouponTasks(List<LiveAutoTask> couponTasks, Long newLiveId,
                                 Map<Long, Long> goodsIdMapping, Date now) {
        if (couponTasks == null || couponTasks.isEmpty()) {
            return;
        }
        for (LiveAutoTask task : couponTasks) {
            try {
                LiveCoupon liveCoupon = JSON.parseObject(task.getContent(), LiveCoupon.class);
                if (liveCoupon != null && liveCoupon.getGoodsId() != null) {
                    // 更新content中的goodsId为新的goodsId
                    Long newGoodsId = goodsIdMapping.get(liveCoupon.getGoodsId());
                    if (newGoodsId != null) {
                        liveCoupon.setGoodsId(newGoodsId);
                        LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now,
                                JSON.toJSONString(liveCoupon));
                        liveAutoTaskService.directInsertLiveAutoTask(newTask);
                    }
                }
            } catch (Exception e) {
                log.error("复制优惠券自动化任务失败，taskId: {}", task.getId(), e);
            }
        }
    }

    /**
     * 创建自动化任务实体
     */
    private LiveAutoTask createAutoTaskEntity(LiveAutoTask source, Long newLiveId, Date now, String content) {
        LiveAutoTask newTask = new LiveAutoTask();
        BeanUtils.copyBeanProp(newTask, source);
        newTask.setId(null);
        newTask.setLiveId(newLiveId);
        newTask.setCreateTime(now);
        newTask.setUpdateTime(now);
        newTask.setFinishStatus(0L);
        if (content != null) {
            newTask.setContent(content);
        }
        return newTask;
    }

    private Long parseIdFromContent(String content, String key) {
        // 假设content是JSON字符串，使用FastJSON解析
        JSONObject jsonObject = JSON.parseObject(content);
        return jsonObject.getLong(key);
    }



    /**
     * 构建FFmpeg推流命令
     */
    private String buildFFmpegCommand(String videoPath, String streamKey) {
        return String.format(
                "ffmpeg -re -stream_loop -1 -i \"%s\" -c:v libx264 -preset ultrafast -b:v 1000k " +
                        "-c:a aac -b:a 128k -f flv rtmp://your-srs-server/live/%s",
                videoPath, streamKey
        );
    }

    /**
     * 身份证信息验证 阿里商品购买接口
     * @param payload
     * @return
     */
    @Override
    public R verifyIdInfo(Map<String, String> payload) {
        if (!payload.containsKey("idCardUrl")) {
            return R.error("身份证信息缺失");
        }
        Live live = baseMapper.selectLiveByLiveId(Long.valueOf(payload.get("liveId")));
        if (live == null) {
            return R.error("直播间不存在");
        }
        String url = "https://idcardside.market.alicloudapi.com/idcard_ocr";
        String appCode = "0a01beb5322e49ca802423fe29df9582";
        try {
            Map<String, String> params = new HashMap<>();
            //image 和 url 二选一 ，选择其中一个时，请注释另外一个参数,默认使用image
            params.put("url", payload.get("idCardUrl"));
//            params.put("image", "data:image/jpeg;base64," + base64String);
            String result = postForm(appCode, url, params);
            JSONObject jsonObject = JSONObject.parseObject(result);
            if (200 == jsonObject.getIntValue("code") && jsonObject.getBooleanValue("success")) {
                live.setIdCardUrl(payload.get("idCardUrl"));
                baseMapper.updateLive(live);
                return R.ok();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return R.error("身份证信息校验异常");
        }
        return R.error("身份证信息校验失败");
    }

    /** 上面方法的数据解析
     * 用到的HTTP工具包：okhttp 3.13.1
     * <dependency>
     * <groupId>com.squareup.okhttp3</groupId>
     * <artifactId>okhttp</artifactId>
     * <version>3.13.1</version>
     * </dependency>
     */
    public static String postForm(String appCode, String url, Map<String, String> params) throws IOException {
        OkHttpClient client = new OkHttpClient.Builder().build();
        FormBody.Builder formbuilder = new FormBody.Builder();
        Iterator<String> it = params.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            formbuilder.add(key, params.get(key));
        }
        FormBody body = formbuilder.build();
        Request request = new Request.Builder().url(url).addHeader("Authorization", "APPCODE " + appCode).post(body).build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }


    public static String generateRtmpPushUrl(String domain, String app, String liveId) {
        Date now = new Date();
        now.setTime(now.getTime() + 24 * 3600 * 1000);

        return domain +
                "/" +
                app +
                "/" +
                liveId +
                "?" +
                "txSecret=" +
                Md5Utils.hash("a61df75ae15271d7ef922f308e61e92e" + liveId + to16Hex(now)) +
                "&" +
                "txTime=" +
                to16Hex(now);
    }

    /**
     * 生成 FLV 播放地址（用于前端播放）
     */
    public static String generateHlvPlayUrl(String domain, String app, String liveId) {
        Date now = new Date();
        now.setTime(now.getTime() + 24 * 3600 * 1000);

        return domain +
                "/" +
                app +
                "/" +
                liveId +
                ".flv?" +
                "txSecret=" +
                Md5Utils.hash("NiMfMYG5HQxZQ5bk88ri" + liveId + to16Hex(now)) +
                "&" +
                "txTime=" +
                to16Hex(now);
    }

    /**
     * 将传入的时间转换为 16进制
     *
     * @param date
     * @return
     */
    public static String to16Hex(Date date) {
        Long aLong = date.getTime() / 1000;
        String hexString = Long.toHexString(aLong);
        return hexString.toUpperCase();
    }
}
