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

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.recall.FilterParamFactory;
import com.tzld.piaoquan.recommend.server.service.recall.RecallParam;
import com.tzld.piaoquan.recommend.server.service.recall.RecallStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

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

@Component
@Slf4j
public class CityRovnRecallStrategy implements RecallStrategy {
    private final String CLASS_NAME = this.getClass().getSimpleName();
    @Autowired
    private FilterService filterService;
    @Autowired
    @Qualifier("redisTemplate")
    public RedisTemplate<String, String> redisTemplate;

    public static final String PUSH_FROM = "city_rovn";
    public static final String redisKeyPrefix = "city_rovn_recall";

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

    @Override
    public List<Video> recall(RecallParam param) {
        List<Video> videosResult = new ArrayList<>();
        try {
            String city = param.getCity().replaceAll("市$", "");
            String redisKey = String.format("%s:%s", redisKeyPrefix, city);
            String redisValue = redisTemplate.opsForValue().get(redisKey);
            if (null == redisValue || redisValue.isEmpty()) {
                return videosResult;
            }
            Pair<List<Long>, Map<Long, Double>> pair = parsePair(redisValue, param.getVideoId(), 100);
            fillVideoResult(param, pair, videosResult);
        } catch (Exception e) {
            log.error("recall is wrong in {}, error={}", CLASS_NAME, e);
        }
        return videosResult;
    }

    private Pair<List<Long>, Map<Long, Double>> parsePair(String data, long headVid, int size) {
        List<Long> idsList = new ArrayList<>();
        Map<Long, Double> scoresMap = new HashMap<>();
        if (!StringUtils.isBlank(data)) {
            String[] cells = data.split("\t");
            if (2 == cells.length) {
                List<Long> ids = Arrays.stream(cells[0].split(",")).map(Long::valueOf).collect(Collectors.toList());
                List<Double> scores = Arrays.stream(cells[1].split(",")).map(Double::valueOf).collect(Collectors.toList());
                if (!ids.isEmpty() && ids.size() == scores.size()) {
                    int minSize = Math.min(size, ids.size());
                    for (int i = 0; i < minSize; ++i) {
                        long id = ids.get(i);
                        double score = scores.get(i);
                        if (headVid != id && !scoresMap.containsKey(id)) {
                            idsList.add(id);
                            scoresMap.put(id, score);
                        }
                    }
                }
            }
        }
        return new MutablePair<>(idsList, scoresMap);
    }

    private void fillVideoResult(RecallParam param, Pair<List<Long>, Map<Long, Double>> pair, List<Video> videosResult) {
        if (null != pair) {
            List<Long> ids = pair.getLeft();
            Map<Long, Double> scoresMap = pair.getRight();
            if (null != ids && null != scoresMap && !ids.isEmpty()) {
                FilterParam filterParam = FilterParamFactory.create(param, ids);
                FilterResult filterResult = filterService.filter(filterParam);
                if (null != filterResult && CollectionUtils.isNotEmpty(filterResult.getVideoIds())) {
                    filterResult.getVideoIds().forEach(vid -> {
                        Video video = new Video();
                        video.setVideoId(vid);
                        video.setRovScore(scoresMap.getOrDefault(vid, 0D));
                        video.setPushFrom(pushFrom());
                        videosResult.add(video);
                    });
                }
            }
        }
    }
}
