package com.tzld.piaoquan.recommend.server.service.rank.strategy;


import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
import com.tzld.piaoquan.recommend.server.model.Video;
import com.tzld.piaoquan.recommend.server.service.rank.RankParam;
import com.tzld.piaoquan.recommend.server.service.rank.RankResult;
import com.tzld.piaoquan.recommend.server.service.rank.RankService;
import com.tzld.piaoquan.recommend.server.service.rank.extractor.RankExtractorItemTags;
import com.tzld.piaoquan.recommend.server.service.rank.processor.RankProcessorBoost;
import com.tzld.piaoquan.recommend.server.service.rank.processor.RankProcessorDensity;
import com.tzld.piaoquan.recommend.server.service.rank.processor.RankProcessorInsert;
import com.tzld.piaoquan.recommend.server.service.rank.processor.RankProcessorTagFilter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Service;

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

/**
 * @author zhangbo sunxiaoyi
 * @desc 后处理规则 + roc池flow池的合并
 * 后处理参考文档：https://w42nne6hzg.feishu.cn/wiki/MYaGwCnF1iTFXUkSddAcA6CanFe
 * roc池flow池的合并 文档： 暂无
 */
@Service
@Slf4j
public class RankStrategy4Density extends RankService {
    @ApolloJsonValue("${RankStrategy4DensityFilterV2:}")
    private Map<String,Map<String, Map<String, String>>> filterRules = new HashMap<>();


    @Override
    public RankResult mergeAndSort(RankParam param, List<Video> rovVideos, List<Video> flowVideos) {

        //1 兜底策略，rov池子不足时，用冷启池填补。直接返回。
        if (CollectionUtils.isEmpty(rovVideos)) {
            if (param.getSize() < flowVideos.size()) {
                return new RankResult(flowVideos.subList(0, param.getSize()));
            } else {
                return new RankResult(flowVideos);
            }
        }

        //2 根据实验号解析阿波罗参数。
        String abCode = param.getAbCode();
        Map<String, Map<String, String>> rulesMap = this.filterRules.getOrDefault(abCode, new HashMap<>(0));

        //3 标签读取
        if (rulesMap != null && !rulesMap.isEmpty()){
            RankExtractorItemTags extractorItemTags = new RankExtractorItemTags(this.redisTemplate);
            extractorItemTags.processor(rovVideos, flowVideos);
        }
        //6 合并结果时间卡控
        if (rulesMap != null && !rulesMap.isEmpty()){
            RankProcessorTagFilter.processor(rovVideos, flowVideos, rulesMap);
        }

        //4 rov池提权功能
        RankProcessorBoost.boostByTag(rovVideos, rulesMap);

        //5 rov池强插功能
        RankProcessorInsert.insertByTag(param, rovVideos, rulesMap);

        //7 流量池按比例强插
        List<Video> result = new ArrayList<>();
        for (int i = 0; i < param.getTopK() && i < rovVideos.size(); i++) {
            result.add(rovVideos.get(i));
        }
        double flowPoolP = getFlowPoolP(param);
        int flowPoolIndex = 0;
        int rovPoolIndex = param.getTopK();
        for (int i = 0; i < param.getSize() - param.getTopK(); i++) {
            double rand = RandomUtils.nextDouble(0, 1);
            if (rand < flowPoolP) {
                if (flowPoolIndex < flowVideos.size()) {
                    result.add(flowVideos.get(flowPoolIndex++));
                } else {
                    break;
                }
            } else {
                if (rovPoolIndex < rovVideos.size()) {
                    result.add(rovVideos.get(rovPoolIndex++));
                } else {
                    break;
                }
            }
        }
        if (rovPoolIndex >= rovVideos.size()) {
            for (int i = flowPoolIndex; i < flowVideos.size() && result.size() < param.getSize(); i++) {
                result.add(flowVideos.get(i));
            }
        }
        if (flowPoolIndex >= flowVideos.size()) {
            for (int i = rovPoolIndex; i < rovVideos.size() && result.size() < param.getSize(); i++) {
                result.add(rovVideos.get(i));
            }
        }

        //8 合并结果密度控制
        Map<String, Integer> densityRules = new HashMap<>();
        if (rulesMap != null && !rulesMap.isEmpty()) {
            for (Map.Entry<String, Map<String, String>> entry : rulesMap.entrySet()) {
                String key = entry.getKey();
                Map<String, String> value = entry.getValue();
                if (value.containsKey("density")) {
                    densityRules.put(key, Integer.valueOf(value.get("density")));
                }
            }
        }
        Set<Long> videosSet = result.stream().map(Video::getVideoId).collect(Collectors.toSet());
        List<Video> rovRecallRankNew = rovVideos.stream().filter(r -> !videosSet.contains(r.getVideoId())).collect(Collectors.toList());
        List<Video> flowPoolRankNew = flowVideos.stream().filter(r -> !videosSet.contains(r.getVideoId())).collect(Collectors.toList());
        List<Video> resultWithDensity = RankProcessorDensity.mergeDensityControl(result,
                rovRecallRankNew, flowPoolRankNew, densityRules);

        return new RankResult(resultWithDensity);
    }

}




//    public Video getTestVideo(Long id, String s){
//        Video a1 = new Video();
//        a1.setVideoId(id);
//        a1.setFlowPool(s);
//        return a1;
//    }

// 1 读取多样性密度控制规则------------------
//        String appType = String.valueOf(param.getAppType());
//        String ruleStr = this.redisTemplate.opsForValue().get("TAGS_FILTER_RULE_V1_JSON");
//        Map<String, Integer> densityRules = new HashMap<>();
//        if (ruleStr != null){
//            Map<String, Map<String, Object>> ruleOrigin = JSONUtils.fromJson(ruleStr,
//                    new TypeToken<Map<String, Map<String, Object>>>() {},
//                    Collections.emptyMap());
//            for (Map.Entry<String, Map<String, Object>> entry : ruleOrigin.entrySet()){
//                String k = entry.getKey();
//                if (!entry.getValue().containsKey(appType)){
//                    continue;
//                }
//                JSONObject jb = (JSONObject) entry.getValue().get(appType);
//                try{
//                    if (jb.containsKey("density") && jb.get("density") instanceof Integer){
//                        densityRules.put(k, jb.getInteger("density"));
//                    }
//                }catch (Exception e){
//                    log.error("parse densityRules is wrong:", e);
//                }
//            }
//        }