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

import com.tzld.piaoquan.recommend.server.common.RedisKeyConstants;
import com.tzld.piaoquan.recommend.server.model.Video;
import com.tzld.piaoquan.recommend.server.service.filter.FilterParam;
import com.tzld.piaoquan.recommend.server.service.filter.FilterResult;
import com.tzld.piaoquan.recommend.server.service.filter.FilterService;
import com.tzld.piaoquan.recommend.server.service.flowpool.FlowPoolConfigService;
import com.tzld.piaoquan.recommend.server.service.recall.FilterParamFactory;
import com.tzld.piaoquan.recommend.server.service.recall.RecallParam;
import com.tzld.piaoquan.recommend.server.util.RecallUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.util.*;

@Slf4j
@Component
public class DouHotFlowPoolRecallStrategy extends AbstractFlowPoolWithLevelRecallStrategy {

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private FilterService filterService;

    @Autowired
    private FlowPoolConfigService flowPoolConfigService;

    @Value("${dou.hot.recall.video.count:20}")
    private Integer douHotRecallVideoCnt;

    public static final String PUSH_FROM = "recall_strategy_hotspot";

    @Override
    Pair<String, String> flowPoolKeyAndLevel(RecallParam param) {

        Map<String, Double> douHotFLowPoolWithLevel = flowPoolConfigService.getDouHotFLowPoolWithLevel();
        if (MapUtils.isEmpty(douHotFLowPoolWithLevel)) {
            return Pair.of("", "");
        }

        double totalWeight = 0;
        NavigableMap<Double, String> levelMap = new TreeMap<>();
        for (Map.Entry<String, Double> entry : douHotFLowPoolWithLevel.entrySet()) {
            String redisKey = String.format(RedisKeyConstants.DouHot.ITEM_REDIS_KEY_FORMAT, RecallUtils.douHotProvinceConvert(param.getProvince()), entry.getKey());
            if (redisTemplate.hasKey(redisKey)) {
                totalWeight += entry.getValue();
                levelMap.put(totalWeight, entry.getKey());
            }
        }

        if (MapUtils.isEmpty(levelMap)) {
            return Pair.of("", "");
        }

        double randomValue = Math.random() * totalWeight;
        Map.Entry<Double, String> chooseEntry = levelMap.higherEntry(randomValue);
        if (Objects.isNull(chooseEntry)) {
            return Pair.of("", "");
        }

        String redisKey = String.format(RedisKeyConstants.DouHot.ITEM_REDIS_KEY_FORMAT, RecallUtils.douHotProvinceConvert(param.getProvince()), chooseEntry.getValue());

        return Pair.of(redisKey, chooseEntry.getValue());
    }

    @Override
    public List<Video> recall(RecallParam param) {
        if (StringUtils.isBlank(param.getProvince())) {
            return Collections.emptyList();
        }

        Pair<String, String> weightPair = this.flowPoolKeyAndLevel(param);
        if (Objects.isNull(weightPair)) {
            return Collections.emptyList();
        }

        String redisKey = weightPair.getLeft();
        String level = weightPair.getRight();
        // log.info("[DouHot recall redisKey] {}", redisKey);
        Set<ZSetOperations.TypedTuple<String>> redisValues = redisTemplate.opsForZSet().rangeWithScores(redisKey, 0, 10000000);


        if (CollectionUtils.isEmpty(redisValues)) {
            // log.error("[DouHot recall is empty]. redisKey: {}", redisKey);
            return Collections.emptyList();
        }

        Map<Long, String> videoIdAndFlowPoolMap = new HashMap<>(redisValues.size());

        for (ZSetOperations.TypedTuple<String> redisValue : redisValues) {
            String value = redisValue.getValue();
            if (StringUtils.isBlank(value)) {
                continue;
            }
            String[] split = value.split("-");
            if (split.length != 2) {
                continue;
            }
            videoIdAndFlowPoolMap.put(Long.valueOf(split[0]), split[1]);
        }

        FilterParam filterParam = FilterParamFactory.create(param, new ArrayList<>(videoIdAndFlowPoolMap.keySet()));
        FilterResult filterResult = filterService.filter(filterParam);

        // 对视频随机打断
        Collections.shuffle(filterResult.getVideoIds());

        // 截取前20个
        int size = Math.min(filterResult.getVideoIds().size(), douHotRecallVideoCnt);
        List<Long> subList = filterResult.getVideoIds().subList(0, size);
        List<Video> videos = new ArrayList<>(subList.size());
        for (Long videoId : subList) {
            Video video = new Video();
            video.setVideoId(videoId);
            video.setFlowPool(videoIdAndFlowPoolMap.get(videoId));
            video.setPushFrom(pushFrom());
            video.setLevel(level);
            videos.add(video);
        }

        return videos;
    }

    @Override
    public String pushFrom() {
        return PUSH_FROM;
    }
}
