package com.tzld.piaoquan.ad.engine.service.score.scorer;


import com.tzld.piaoquan.ad.engine.commons.score.BaseLRV2ModelScorer;
import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
import com.tzld.piaoquan.ad.engine.commons.score.ScorerConfigInfo;
import com.tzld.piaoquan.ad.engine.commons.score.model.LRV2Model;
import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRankItem;
import com.tzld.piaoquan.recommend.feature.domain.ad.base.UserAdFeature;
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 VlogRovLRScorer extends BaseLRV2ModelScorer {

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


    public VlogRovLRScorer(ScorerConfigInfo configInfo) {
        super(configInfo);
    }

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

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

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

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

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

        return result;
    }

    private List<AdRankItem> rankByJava(final Map<String, String> sceneFeatureMap,
                                        final Map<String, String> userFeatureMap,
                                        final List<AdRankItem> items) {
        long startTime = System.currentTimeMillis();
        LRV2Model model = (LRV2Model) this.getModel();
        // 所有都参与打分，按照ctr排序
        multipleCtrScore(items, userFeatureMap, sceneFeatureMap, model);

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

        Collections.sort(items);

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

    private void multipleCtrScore(final List<AdRankItem> items,
                                  final Map<String, String> userFeatureMap,
                                  final Map<String, String> sceneFeatureMap,
                                  final LRV2Model 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("ctr exception: [{}] [{}]", items.get(fIndex).getAdId(), 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(),
                            ExceptionUtils.getFullStackTrace(e));
                }
            }
        }
    }

    public double calcScore(final LRV2Model lrModel,
                            final AdRankItem item,
                            final Map<String, String> userFeatureMap,
                            final Map<String, String> sceneFeatureMap) {


        Map<String, String> featureMap = new HashMap<>();
        if (MapUtils.isNotEmpty(item.getFeatureMap())) {
            featureMap.putAll(item.getFeatureMap());
        }
        if (MapUtils.isNotEmpty(userFeatureMap)) {
            featureMap.putAll(userFeatureMap);
        }
        if (MapUtils.isNotEmpty(sceneFeatureMap)) {
            featureMap.putAll(sceneFeatureMap);
        }

        double pro = 0.0;
        if (MapUtils.isNotEmpty(featureMap)) {
            try {
                pro = lrModel.score(featureMap);
                // LOGGER.info("fea : {}, score:{}", JSONUtils.toJson(featureMap), pro);
            } catch (Exception e) {
                LOGGER.error("score error for doc={} exception={}", item.getVideoId(), ExceptionUtils.getFullStackTrace(e));
            }
        }
        item.getScoreMap().put("ctcvrScore", pro);
        item.setLrScore(pro);
        return pro;
    }
}