package com.tzld.piaoquan.api.service.strategy.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tzld.piaoquan.api.common.enums.ReplyStrategyServiceEnum;
import com.tzld.piaoquan.api.component.TouLiuHttpClient;
import com.tzld.piaoquan.api.dao.mapper.AlgGhAutoreplyVideoRankDataMapper;
import com.tzld.piaoquan.api.model.bo.*;
import com.tzld.piaoquan.api.model.po.AlgGhAutoreplyVideoRankData;
import com.tzld.piaoquan.api.model.po.AlgGhAutoreplyVideoRankDataExample;
import com.tzld.piaoquan.api.model.vo.VideoCharacteristicVO;
import com.tzld.piaoquan.api.service.VideoMultiService;
import com.tzld.piaoquan.api.service.strategy.ReplyStrategyService;
import com.tzld.piaoquan.growth.common.common.enums.StrategyStatusEnum;
import com.tzld.piaoquan.growth.common.dao.mapper.CgiReplyBucketDataMapper;
import com.tzld.piaoquan.growth.common.dao.mapper.ext.CgiReplyBucketDataMapperExt;
import com.tzld.piaoquan.growth.common.model.po.CgiReplyBucketData;
import com.tzld.piaoquan.growth.common.model.po.CgiReplyBucketDataExample;
import com.tzld.piaoquan.growth.common.utils.MessageUtil;
import com.tzld.piaoquan.growth.common.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class WeComPushMessageStrategyV1 implements ReplyStrategyService {

    /**
     * 实验分桶数量
     */
//    private static final Integer bucketNum = 10;

    /**
     * 分桶实验策略，key为策略，arr为对应桶
     * {"base":[0,1,2,3],"stg0909-base":[4,5],"stg0909-explore1":[6,7,8],"stg0909-explore2":[9]}
     * {"stg0909-base":[5,6],"stg0909-explore1":[7],"stg0909-explore2":[8,9]}
     */
    private static final String bucketStrategyConfig = "{\"we-com-base\":[0,1,2,3,4,5,6],\"we-com-explore1\":[7],\"we-com-explore2\":[8,9]}";


    private static final String manualConfig = "{\"we-com-manual\":[0,1,2,3,4,5,6,7,8,9]}";
    /**
     * 自动回复使用小程序Id
     */
    private static final String SMALL_APP_Id = "wxbdd2a2e93d9a6e25";

    private static final String CDN_URL = "https://rescdn.piaoquantv.com/";

    @Value("${small_page_url}")
    private String GET_SMALL_PAGE_URL;

    @Autowired
    private AlgGhAutoreplyVideoRankDataMapper algGhAutoreplyVideoRankDataMapper;
    @Autowired
    private CgiReplyBucketDataMapper cgiReplyBucketDataMapper;
    @Autowired
    private CgiReplyBucketDataMapperExt cgiReplyBucketDataMapperExt;
    @Autowired
    private TouLiuHttpClient touLiuHttpClient;
    @Autowired
    private VideoMultiService videoMultiService;

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public ReplyBucketData getResult(BucketDataParam bucketDataParam) {
        log.info("WeComPushMessageStrategyV1 start");
        // 0 获取策略key
        JSONObject configJsonObject;
        if (Objects.equals(StrategyStatusEnum.DEFAULT.status, bucketDataParam.getStrategyStatus())) {
            configJsonObject = JSON.parseObject(manualConfig);
        } else {
            configJsonObject = JSON.parseObject(bucketStrategyConfig);
        }
        Set<String> keyedSet = configJsonObject.keySet();
        // 加锁，防止并发请求重复触发数据生成（readStrategyOrderSmallData + setSmallPageUrl + insertSmallData）
        String LOCK_KEY = "getResult:" + bucketDataParam.getGhId();
        boolean locked = redisUtils.tryLockWithRetry(LOCK_KEY, "1", 60, 10, 1000);
        if (!locked) {
            log.warn("WeComPushMessageStrategyV1 getResult 获取锁失败，直接读取已有数据，ghId：" + bucketDataParam.getGhId());
            return getReplyBucketData(configJsonObject, keyedSet, bucketDataParam.getGhId());
        }
        try {
            // 1 处理文章--算法引擎--排序文章数据
//            getWenzhangData();
            // 2 处理小程序--读取离线数据表--获取策略排序小程序数据
            List<CgiReplyBucketData> smallDataCgiReplyList = readStrategyOrderSmallData(keyedSet, bucketDataParam);
            // 2.1 获取小程序落地页地址 http调用
            smallDataCgiReplyList = setSmallPageUrl(smallDataCgiReplyList);
            log.info(JSON.toJSONString(smallDataCgiReplyList));
            // 3 入库读表
            insertSmallData(smallDataCgiReplyList, keyedSet, bucketDataParam);
        } finally {
            redisUtils.del(LOCK_KEY);
        }
        // 4 组装分桶数据
        return getReplyBucketData(configJsonObject, keyedSet, bucketDataParam.getGhId());
    }

    private ReplyBucketData getReplyBucketData(JSONObject bucketStrategyConfigJsonObject, Set<String> keyedSet, String ghId) {
        // 策略小程序数据
        ReplyBucketData replyBucketData = new ReplyBucketData();
        List<GroupData> groupDataList = new ArrayList<>();
        for (String key : keyedSet) {
            CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
            cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andStrategyEqualTo(key).andGhIdEqualTo(ghId);
            cgiReplyBucketDataExample.setOrderByClause("sort");
            List<CgiReplyBucketData> cgiReplyBucketData = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExample);
            if (CollectionUtils.isEmpty(cgiReplyBucketData)) {
                CgiReplyBucketDataExample cgiReplyBucketDataExampleNull = new CgiReplyBucketDataExample();
                cgiReplyBucketDataExampleNull.createCriteria().andIsDeleteEqualTo(0).andStrategyEqualTo(key).andGhIdEqualTo("default");
                cgiReplyBucketDataExampleNull.setOrderByClause("sort");
                cgiReplyBucketData = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExampleNull);
            }
            log.info("cgiReplyBucketData={}", cgiReplyBucketData);
            if (CollectionUtils.isEmpty(cgiReplyBucketData)) {
                log.error("getReplyBucketData get data is null,key:" + key);
                return null;
            }

            List<Integer> groupList = bucketStrategyConfigJsonObject.getJSONArray(key).toJavaList(Integer.class);
            for (Integer group : groupList) {
                GroupData groupData = new GroupData();
                groupData.setGroupIndex(group);
                List<MsgData> msgDataList = new ArrayList<>();
                for (CgiReplyBucketData cgiReplyBucketDatum : cgiReplyBucketData) {
                    MsgData msgData = new MsgData();
                    BeanUtils.copyProperties(cgiReplyBucketDatum, msgData);
                    if (cgiReplyBucketDatum.getMsgType().equals(1)) {
                        msgData.setMiniAppId(SMALL_APP_Id);
                    }
                    msgDataList.add(msgData);
                }
                groupData.setMsgDataList(msgDataList);
                groupDataList.add(groupData);
            }
        }
        // groupDataList排序
        replyBucketData.setGroupList(groupDataList.stream().sorted(Comparator.comparingInt(GroupData::getGroupIndex)).collect(Collectors.toList()));
        return replyBucketData;
    }


    private void insertSmallData(List<CgiReplyBucketData> smallDataCgiReplyList, Set<String> keyedSet, BucketDataParam bucketDataParam) {
        if (CollectionUtils.isEmpty(smallDataCgiReplyList)) {
            return;
        }
        // 循环获取锁，锁30s，每次重试间隔 1s，最多重试10次
        String LOCK_KEY = "insertSmallData:" + bucketDataParam.getGhId();
        boolean locked = redisUtils.tryLockWithRetry(LOCK_KEY, "1", 30, 10, 1000);
        // 未获取到锁，直接返回，避免重复入库
        if (!locked) {
            log.error("WeComPushMessageStrategyV1 insertSmallData 获取锁失败，放弃入库，ghId：" + bucketDataParam.getGhId());
            return;
        }
        try {
            for (String key : keyedSet) {
                if ("base".equals(key)) {
                    continue;
                }
                List<CgiReplyBucketData> collect = smallDataCgiReplyList.stream()
                        .filter(x -> x.getStrategy().equals(key))
                        .filter(x -> x.getGhId().equals(bucketDataParam.getGhId()))
                        .collect(Collectors.toList());
                if (CollectionUtils.isEmpty(collect)) {
                    log.info("WeComPushMessageStrategyV1 insertSmallData 数据未变更跳过，key：{}，ghId：{}，data:{}",
                            key, bucketDataParam.getGhId(), JSON.toJSONString(smallDataCgiReplyList));
                    continue;
                }
                CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
                cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andMsgTypeEqualTo(1).andStrategyEqualTo(key).andGhIdEqualTo(bucketDataParam.getGhId());
                List<CgiReplyBucketData> cgiReplyBucketData1 = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExample);
                Map<Long, CgiReplyBucketData> cgiReplyBucketDataMap = cgiReplyBucketData1.stream()
                        .collect(Collectors.toMap(CgiReplyBucketData::getMiniVideoId, x -> x, (a, b) -> b));
                // 锁内二次校验：若已存在相同视频ID的 is_delete=0 记录，说明并发请求已处理，跳过本次入库
                if (!CollectionUtils.isEmpty(cgiReplyBucketData1) && cgiReplyBucketData1.size() == collect.size()) {
                    Set<Long> existVideoIds = cgiReplyBucketDataMap.keySet();
                    Set<Long> toInsertVideoIds = collect.stream()
                            .map(x -> x.getMiniVideoId() != null ? x.getMiniVideoId() : MessageUtil.getVideoId(x.getMiniPagePath()))
                            .filter(Objects::nonNull)
                            .collect(Collectors.toSet());
                    if (existVideoIds.equals(toInsertVideoIds)) {
                        log.info("WeComPushMessageStrategyV1 insertSmallData 锁内二次校验：数据已被并发请求处理，跳过，key：{}，ghId：{}",
                                key, bucketDataParam.getGhId());
                        continue;
                    }
                }
                // 清上个版本的策略数据
                cgiReplyBucketDataMapperExt.updateDeleteStatus(bucketDataParam.getGhId(), key, 1, 1);
                // 入库
                for (CgiReplyBucketData cgiReplyBucketData : collect) {
                    CgiReplyBucketData oldData = cgiReplyBucketDataMap.get(cgiReplyBucketData.getMiniVideoId());
                    if (Objects.isNull(oldData)) {
                        oldData = cgiReplyBucketDataMapperExt.getOldCgiReplyData(cgiReplyBucketData.getMiniVideoId());
                    }
                    if (Objects.nonNull(oldData)) {
                        if (StringUtils.isEmpty(cgiReplyBucketData.getTitle())) {
                            cgiReplyBucketData.setTitle(oldData.getTitle());
                        }
                        if (StringUtils.isEmpty(cgiReplyBucketData.getCoverUrl())) {
                            cgiReplyBucketData.setCoverUrl(oldData.getCoverUrl());
                        }
                    }
                    String pageUrl = videoMultiService.setVideoMultiTitleCoverPagePath(cgiReplyBucketData.getMiniVideoId(),
                            cgiReplyBucketData.getMiniPagePath(), cgiReplyBucketData.getTitle(), cgiReplyBucketData.getCoverUrl());
                    cgiReplyBucketData.setMiniPagePath(pageUrl);
                    cgiReplyBucketData.setRootSourceId(MessageUtil.getRootSourceId(cgiReplyBucketData.getMiniPagePath()));
                    cgiReplyBucketDataMapper.insertSelective(cgiReplyBucketData);
                    String redisKey = "auto_reply_video_detail_" + cgiReplyBucketData.getRootSourceId();
                    VideoCharacteristicVO vo = new VideoCharacteristicVO();
                    vo.setGhId(cgiReplyBucketData.getGhId());
                    vo.setName(bucketDataParam.getAccountName());
                    vo.setVideoId(cgiReplyBucketData.getMiniVideoId());
                    vo.setTitle(cgiReplyBucketData.getTitle());
                    vo.setCover(cgiReplyBucketData.getCoverUrl());
                    redisUtils.set(redisKey, JSONObject.toJSONString(vo), 3L * 24 * 60 * 60);
                }
            }
        } finally {
            redisUtils.del(LOCK_KEY);
        }
    }

    private List<CgiReplyBucketData> setSmallPageUrl(List<CgiReplyBucketData> smallDataCgiReplyList) {
        if (CollectionUtils.isEmpty(smallDataCgiReplyList)) {
            return smallDataCgiReplyList;
        }
        Set<String> keys = smallDataCgiReplyList.stream().map(x -> x.getGhId() + "&" + x.getMiniVideoId() + "&" + x.getSort()).collect(Collectors.toSet());
        Map<String, SmallPageUrlDetail> keyPageUrl = new HashMap<>();
        // gh-id + videoId + sort 复用同一page_url及落地页id
        for (String key : keys) {
            String[] keyArr = key.split("&");
            String ghId = keyArr[0];
            String videoId = keyArr[1];
            String sort = keyArr[2];
            // 查询库里是否存在，如果存在即复用
            CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
            cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andMiniVideoIdEqualTo(Long.valueOf(videoId)).andGhIdEqualTo(ghId);
            List<CgiReplyBucketData> cgiReplyBucketData = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExample);
            SmallPageUrlDetail smallPageUrlDetail = new SmallPageUrlDetail();
            if (CollectionUtils.isEmpty(cgiReplyBucketData)) {
                // 库里不存在，调用新生成
                String response = touLiuHttpClient.sendAdFlowAddRequest(GET_SMALL_PAGE_URL, videoId, "touliu",
                        "tencentqw", "自动", "企微", "即时欢迎语", "位置" + sort, ghId, null);
                JSONObject jsonObject = JSON.parseObject(response);
                if (jsonObject.getInteger("code").equals(0)) {
                    smallPageUrlDetail = jsonObject.getObject("data", SmallPageUrlDetail.class);
                    keyPageUrl.put(key, smallPageUrlDetail);
                } else {
                    log.error("httpClientService get page url error,response:" + response);
                    throw new RuntimeException("httpClientService get page url error");
                }
            } else {
                // 复用
                CgiReplyBucketData cgiReplyBucketData1 = cgiReplyBucketData.get(0);
                smallPageUrlDetail.setId(cgiReplyBucketData1.getPagePathUrlId());
                smallPageUrlDetail.setUrl(cgiReplyBucketData1.getMiniPagePath());
            }
            keyPageUrl.put(key, smallPageUrlDetail);
        }
        // 处理数据
        for (CgiReplyBucketData cgiReplyBucketData : smallDataCgiReplyList) {
            String key = cgiReplyBucketData.getGhId() + "&" + cgiReplyBucketData.getMiniVideoId() + "&" + cgiReplyBucketData.getSort();
            SmallPageUrlDetail smallPageUrlDetail = keyPageUrl.get(key);
            if (Objects.isNull(smallPageUrlDetail)) {
                log.error("setSmallPageUrl get map url is null" + JSON.toJSONString(keyPageUrl));
                throw new RuntimeException("setSmallPageUrl get map url is null");
            }
            cgiReplyBucketData.setPagePathUrlId(smallPageUrlDetail.getId());
            cgiReplyBucketData.setMiniPagePath(smallPageUrlDetail.getUrl());
            cgiReplyBucketData.setRootSourceId(MessageUtil.getRootSourceId(smallPageUrlDetail.getUrl()));
        }
        return smallDataCgiReplyList;
    }

    private List<CgiReplyBucketData> readStrategyOrderSmallData(Set<String> keyedSet, BucketDataParam bucketDataParam) {
        List<CgiReplyBucketData> result = new ArrayList<>();
        for (String key : keyedSet) {
            if ("base".equals(key)) {
                // base作为人工控制
                continue;
            }
            if (Objects.equals(StrategyStatusEnum.DEFAULT.status, bucketDataParam.getStrategyStatus())) {
                if (CollectionUtils.isEmpty(bucketDataParam.getVideos())) {
                    return null;
                }
                CgiReplyBucketDataExample countExample = new CgiReplyBucketDataExample();
                countExample.createCriteria().andIsDeleteEqualTo(0).andStrategyEqualTo(key)
                        .andGhIdEqualTo(bucketDataParam.getGhId());
                long allCount = cgiReplyBucketDataMapper.countByExample(countExample);
                List<CgiReplyBucketData> existCgiReplyBucketDataList = new ArrayList<>();
                if (allCount == bucketDataParam.getVideos().size()) {
                    for (int i = 0; i < bucketDataParam.getVideos().size(); i++) {
                        int sort = i + 1;
                        Long videoId = bucketDataParam.getVideos().get(i);
                        CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
                        cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andStrategyEqualTo(key)
                                .andGhIdEqualTo(bucketDataParam.getGhId()).andMiniVideoIdEqualTo(videoId).andSortEqualTo(sort);
                        List<CgiReplyBucketData> list = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExample);
                        if (!CollectionUtils.isEmpty(list)) {
                            existCgiReplyBucketDataList.addAll(list);
                        }
                    }
                    if (existCgiReplyBucketDataList.size() == bucketDataParam.getVideos().size()) {
                        // 当前与历史条数一致且视频Id一致，不更新
                        continue;
                    }
                }
                String batchId = UUID.randomUUID().toString();
                int totalSize = bucketDataParam.getVideos().size();
                Map<Long, VideoDetail> videoDetailMap = touLiuHttpClient.getVideoDetailRequest(bucketDataParam.getVideos());
                for (int i = 0; i < bucketDataParam.getVideos().size(); i++) {
                    int sort = i + 1;
                    Long videoId = bucketDataParam.getVideos().get(i);
                    CgiReplyBucketData cgiReplyBucketData = new CgiReplyBucketData();
                    cgiReplyBucketData.setStrategy(key);
                    cgiReplyBucketData.setSort(sort);
                    cgiReplyBucketData.setBatch(batchId);
                    cgiReplyBucketData.setBatchSort(totalSize - i);
                    cgiReplyBucketData.setGhId(bucketDataParam.getGhId());
                    cgiReplyBucketData.setMsgType(1);
                    VideoDetail videoDetail = videoDetailMap.get(videoId);
                    if (videoDetail != null && StringUtils.isNotEmpty(videoDetail.getCover())) {
                        cgiReplyBucketData.setCoverUrl(videoDetail.getCover());
                    }
                    if (videoDetail != null && StringUtils.isNotEmpty(videoDetail.getTitle())) {
                        cgiReplyBucketData.setTitle(videoDetail.getTitle());
                    }
                    cgiReplyBucketData.setMiniAppId(SMALL_APP_Id);
                    cgiReplyBucketData.setMiniVideoId(videoId);
                    result.add(cgiReplyBucketData);
                }
                //result = ReplyStrategyService.checkCgiReplyBucketDataDiff(result, existCgiReplyBucketDataList);
            } else {
                // 获取最新dt的策略
                String dtVersion = algGhAutoreplyVideoRankDataMapper.selectLatestDtVersionByStrategyKeyAndGhId(key, bucketDataParam.getGhId());
                // 判断当前的dtVersion是否已经处理过了
                CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
                cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andStrategyDtEqualTo(dtVersion).andStrategyEqualTo(key).andGhIdEqualTo(bucketDataParam.getGhId());
                long count = cgiReplyBucketDataMapper.countByExample(cgiReplyBucketDataExample);
                if (count != 0) {
                    // 说明已处理过该dtVersion数据
                    continue;
                }
                // 获取最新dt数据
                List<AlgGhAutoreplyVideoRankData> dtVersionStrategyData = getDtVersionStrategyData(key, dtVersion, bucketDataParam.getGhId());
                List<Long> videoIds = dtVersionStrategyData.stream().map(AlgGhAutoreplyVideoRankData::getVideoId).collect(Collectors.toList());
                Map<Long, VideoDetail> videoDetailMap = touLiuHttpClient.getVideoDetailRequest(videoIds);
                String batchId = UUID.randomUUID().toString();
                int totalSize = dtVersionStrategyData.size();
                for (int i = 0; i < dtVersionStrategyData.size(); i++) {
                    AlgGhAutoreplyVideoRankData x = dtVersionStrategyData.get(i);
                    CgiReplyBucketData cgiReplyBucketData = new CgiReplyBucketData();
                    cgiReplyBucketData.setStrategy(key);
                    cgiReplyBucketData.setSort(x.getSort());
                    cgiReplyBucketData.setBatch(batchId);
                    cgiReplyBucketData.setBatchSort(totalSize - i);
                    cgiReplyBucketData.setStrategyDt(x.getDtVersion());
                    cgiReplyBucketData.setGhId(x.getGhId());
                    cgiReplyBucketData.setMsgType(1);
                    cgiReplyBucketData.setTitle(x.getTitle());
                    VideoDetail videoDetail = videoDetailMap.get(x.getVideoId());
                    if (videoDetail != null && StringUtils.isNotEmpty(videoDetail.getCover())) {
                        cgiReplyBucketData.setCoverUrl(videoDetail.getCover());
                    } else {
                        cgiReplyBucketData.setCoverUrl(CDN_URL + x.getCoverUrl());
                    }
                    cgiReplyBucketData.setMiniAppId(SMALL_APP_Id);
                    cgiReplyBucketData.setMiniVideoId(x.getVideoId());
                    result.add(cgiReplyBucketData);
                }
            }
        }
        // 获取最新数据版本
        return CollectionUtils.isEmpty(result) ? null : result;
    }

    private List<AlgGhAutoreplyVideoRankData> getDtVersionStrategyData(String key, String dtVersion, String ghId) {
        AlgGhAutoreplyVideoRankDataExample algGhAutoreplyVideoRankDataExample = new AlgGhAutoreplyVideoRankDataExample();
        algGhAutoreplyVideoRankDataExample.createCriteria().andIsDeleteEqualTo(0).andDtVersionEqualTo(dtVersion).andStrategyKeyEqualTo(key).andGhIdEqualTo(ghId);
        return algGhAutoreplyVideoRankDataMapper.selectByExample(algGhAutoreplyVideoRankDataExample);
    }

    @Override
    public Boolean support(ReplyStrategyServiceEnum key) {
        return ReplyStrategyServiceEnum.WE_COM_PUSH_MESSAGE_STRATEGY_V1.equals(key);
    }
}
