package com.tzld.piaoquan.recommend.server.service.score;

import com.tzld.piaoquan.recommend.feature.domain.video.base.UserFeature;
import com.tzld.piaoquan.recommend.server.common.base.RankItem;
import com.tzld.piaoquan.recommend.server.service.score.model.VovH24WeightModel;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.*;

public class VovH24WeightScorer extends AbstractScorer {

    private static final int LOCAL_TIME_OUT = 150;
    private final static Logger LOGGER = LoggerFactory.getLogger(VlogRovFMScorer.class);
    private static final ExecutorService executorService = Executors.newFixedThreadPool(128);

    private static final Map<String, String> numeratorAndDenominatorMap = new HashMap<String, String>(32) {{
        put("1_vovh0分子", "1_vovh分母");
        put("2_vovh0分子", "2_vovh分母");
        put("2_vovh1分子", "2_vovh分母");
        put("3_vovh0分子", "3_vovh分母");
        put("3_vovh1分子", "3_vovh分母");
        put("3_vovh2分子", "3_vovh分母");
        put("4_vovh0分子", "4_vovh分母");
        put("4_vovh1分子", "4_vovh分母");
        put("4_vovh3分子", "4_vovh分母");
        put("7_vovh0分子", "7_vovh分母");
        put("7_vovh1分子", "7_vovh分母");
        put("7_vovh6分子", "7_vovh分母");
        put("13_vovh0分子", "13_vovh分母");
        put("13_vovh1分子", "13_vovh分母");
        put("13_vovh12分子", "13_vovh分母");
        put("25_vovh0分子", "25_vovh分母");
        put("25_vovh1分子", "25_vovh分母");
        put("25_vovh24分子", "25_vovh分母");
        put("1_vovd0分子", "1_vovd分母");
        put("2_vovd0分子", "2_vovd分母");
        put("2_vovd1分子", "2_vovd分母");
        put("3_vovd0分子", "3_vovd分母");
        put("3_vovd1分子", "3_vovd分母");
        put("3_vovd2分子", "3_vovd分母");
    }};

    public VovH24WeightScorer(ScorerConfigInfo scorerConfigInfo) {
        super(scorerConfigInfo);
    }

    @Override
    public void loadModel() {
        doLoadModel(VovH24WeightModel.class);
    }

    @Override
    public List<RankItem> scoring(ScoreParam param, UserFeature userFeature, List<RankItem> rankItems) {
        throw new NoSuchMethodError();
    }

    @Override
    public List<RankItem> scoring(final Map<String, String> sceneFeatureMap,
                                  final Map<String, String> userFeatureMap,
                                  final List<RankItem> rankItems) {
        if (CollectionUtils.isEmpty(rankItems)) {
            return rankItems;
        }

        long startTime = System.currentTimeMillis();
        VovH24WeightModel model = (VovH24WeightModel) this.getModel();
        LOGGER.debug("model size: [{}]", model.getModelSize());

        List<RankItem> result = rankByJava(
                sceneFeatureMap, userFeatureMap, rankItems
        );

        LOGGER.debug("vovh24 scorer time java items size={}, time={} ",
                result.size(), System.currentTimeMillis() - startTime);

        return result;
    }

    private List<RankItem> rankByJava(final Map<String, String> sceneFeatureMap,
                                      final Map<String, String> userFeatureMap,
                                      final List<RankItem> items) {
        long startTime = System.currentTimeMillis();
        VovH24WeightModel model = (VovH24WeightModel) this.getModel();
        LOGGER.debug("model size: [{}]", model.getModelSize());

        // 所有都参与打分，按照ctr排序
        multipleCtrScore(items, userFeatureMap, sceneFeatureMap, model);

        // debug log
        if (LOGGER.isDebugEnabled()) {
            for (RankItem item : items) {
                LOGGER.debug("before enter feeds model predict ctr score [{}] [{}]", item, item);
            }
        }

        Collections.sort(items);

        LOGGER.debug("[vovh24 scorer time java] items size={}, cost={} ",
                items.size(), System.currentTimeMillis() - startTime);
        return items;
    }

    private void multipleCtrScore(final List<RankItem> items,
                                  final Map<String, String> userFeatureMap,
                                  final Map<String, String> sceneFeatureMap,
                                  final VovH24WeightModel model) {

        List<Callable<Object>> calls = new ArrayList<Callable<Object>>();
        for (int index = 0; index < items.size(); index++) {
            final int fIndex = index;
            calls.add(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    try {
                        calcScore(model, items.get(fIndex), userFeatureMap, sceneFeatureMap);
                    } catch (Exception e) {
                        LOGGER.error("vovh24 scorer exception: [{}] [{}]", items.get(fIndex).videoId, ExceptionUtils.getFullStackTrace(e));
                    }
                    return new Object();
                }
            });
        }

        List<Future<Object>> futures = null;
        try {
            futures = executorService.invokeAll(calls, LOCAL_TIME_OUT, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            LOGGER.error("execute invoke fail: {}", ExceptionUtils.getFullStackTrace(e));
        }

        // 等待所有请求的结果返回, 超时也返回
        int cancel = 0;
        if (futures != null) {
            for (Future<Object> future : futures) {
                try {
                    if (!future.isDone() || future.isCancelled() || future.get() == null) {
                        cancel++;
                    }
                } catch (InterruptedException e) {
                    LOGGER.error("InterruptedException: ", e);
                } catch (ExecutionException e) {
                    LOGGER.error("ExecutionException {}, ", sceneFeatureMap.size(), e);
                }
            }
        }
    }

    public double calcScore(final VovH24WeightModel model,
                            final RankItem item,
                            final Map<String, String> userFeatureMap,
                            final Map<String, String> sceneFeatureMap) {

        double vovScore = 0.0;
        Map<String, String> featureMap = item.getFeatureMap();
        String weightKey = sceneFeatureMap.getOrDefault("weightKey", "");
        Map<String, Double> weightMap = model.getWeight(weightKey);
        if (MapUtils.isNotEmpty(featureMap) && MapUtils.isNotEmpty(weightMap)) {
            try {
                double numerator = 0d;
                double denominator = 0d;
                for (Map.Entry<String, String> entry : numeratorAndDenominatorMap.entrySet()) {
                    String numeratorKey = entry.getKey();
                    String denominatorKey = entry.getValue();

                    double up = Double.parseDouble(featureMap.getOrDefault(numeratorKey, "0d"));
                    double down = Double.parseDouble(featureMap.getOrDefault(denominatorKey, "0d"));
                    double weight = weightMap.getOrDefault(numeratorKey, 0d);

                    numerator += up * weight;
                    denominator += down * weight;

                }

                item.getScoresMap().put("numerator", numerator);
                item.getScoresMap().put("denominator", denominator);
                vovScore = denominator != 0.0 ? numerator / denominator : 0.0;
            } catch (Exception e) {
                LOGGER.error("vovh24 scorer error for doc={} exception={}", item.getVideoId(), ExceptionUtils.getFullStackTrace(e));
            }
        }
        item.getScoresMap().put("vovScore", vovScore);
        item.setVovScore(vovScore);
        return vovScore;
    }
}
