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

import com.google.common.collect.Lists;
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.PAIModel;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class PAIScorer extends AbstractScorer {

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


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

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

    @Override
    public List<RankItem> scoring(Map<String, Float> sceneFeatureMap, Map<String, Float> userFeatureMap, List<RankItem> rankItems) {
        if (CollectionUtils.isEmpty(rankItems)) {
            return rankItems;
        }
        long startTime = System.currentTimeMillis();
        List<RankItem> result = rankByJava(sceneFeatureMap, userFeatureMap, rankItems);

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

        return result;
    }

    private List<RankItem> rankByJava(Map<String, Float> sceneFeatureMap, Map<String, Float> userFeatureMap, List<RankItem> rankItems) {
        List<List<RankItem>> partition = Lists.partition(rankItems, BATCH_SIZE);
        List<Future<List<RankItem>>> futures = new ArrayList<>();
        for (List<RankItem> items : partition) {
            futures.add(executorService.submit(() -> {
                try {
                    batchCallScore(PAIModel.getModelInstance(), items);
                } catch (Exception e) {
                    LOGGER.error("batchCallScore error ", e);
                }
                return items;
            }));
        }

        List<RankItem> result = new ArrayList<>(rankItems.size());
        for (Future<List<RankItem>> future : futures) {
            try {
                result.addAll(future.get(LOCAL_TIME_OUT, TimeUnit.MILLISECONDS));
            } catch (Exception e) {
                LOGGER.info("executorService.submit error ", e);
            }
        }
        Collections.sort(result);
        return result;
    }

    public void batchCallScore(final PAIModel model, final List<RankItem> rankItems) {
        try {
            Map<String, List<Float>> scoreMap = model.score(rankItems);
            List<Float> returnNUvScoreList = scoreMap.get("y_return_n_uv");
            List<Float> isShareScoreList = scoreMap.get("probs_is_share");
            for (int i = 0; i < rankItems.size(); i++) {
                RankItem rankItem = rankItems.get(i);
                Float ros = returnNUvScoreList.get(i);
                Float str = isShareScoreList.get(i);
                rankItem.setScoreRov(Double.valueOf(str));
                rankItem.getScoresMap().put("NorDNNScore", Double.valueOf(ros));
            }
        } catch (Exception e) {
            LOGGER.error("pai scorer batch call score error ", e);
        }
    }
}
