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


import com.tzld.piaoquan.ad.engine.commons.score.AbstractScorer;
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.PAIModelV1;
import com.tzld.piaoquan.ad.engine.commons.score.model.PAIModelV2;
import com.tzld.piaoquan.ad.engine.commons.thread.ThreadPoolFactory;
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.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.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class PAIScorerV2 extends AbstractScorer {

    private final static Logger LOGGER = LoggerFactory.getLogger(PAIScorerV2.class);


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

    @Override
    public List<AdRankItem> scoring(final ScoreParam param,
                                    final UserAdFeature userAdFeature,
                                    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();

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

        LOGGER.debug("ctr ranker time java items size={}, time={} ", result != null ? result.size() : 0,
                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();
//        PAIModelV2 model = PAIModelV2.getModel();
//        // 所有都参与打分，按照ctr排序
//        multipleCtrScore(items, userFeatureMap, sceneFeatureMap, model);
//
//        // debug log
//        if (LOGGER.isDebugEnabled()) {
//            for (int i = 0; i < items.size(); i++) {
//                LOGGER.debug("before enter feeds model predict ctr score [{}] [{}]", items.get(i), items.get(i));
//            }
//        }
//
//        Collections.sort(items);
//
//        LOGGER.debug("ctr ranker java execute time: [{}]", System.currentTimeMillis() - startTime);
//        LOGGER.debug("[ctr ranker time java] items size={}, cost={} ", items != null ? items.size() : 0,
//                System.currentTimeMillis() - startTime);
//        return items;
//    }

    private List<AdRankItem> rankByJava(final Map<String, String> sceneFeatureMap,
                                        final Map<String, String> userFeatureMap,
                                        final List<AdRankItem> items) {
        if (items == null || items.isEmpty()) {
            return Collections.emptyList();
        }

        long startTime = System.currentTimeMillis();
        PAIModelV2 model = PAIModelV2.getModel();

        final int batchSize = 300;
        List<List<AdRankItem>> batches = new ArrayList<>();
        for (int i = 0; i < items.size(); i += batchSize) {
            batches.add(new ArrayList<>(items.subList(i, Math.min(i + batchSize, items.size()))));
        }

        ExecutorService executor = ThreadPoolFactory.score();
        List<Future<List<AdRankItem>>> futures = new ArrayList<>();

        for (List<AdRankItem> batch : batches) {
            futures.add(executor.submit(() -> {
                try {
                    multipleCtrScore(batch, userFeatureMap, sceneFeatureMap, model);
                } catch (Exception e) {
                    LOGGER.error("Error during multipleCtrScore batch execution", e);
                }
                return batch;
            }));
        }

        // 合并结果
        List<AdRankItem> merged = new ArrayList<>();
        for (Future<List<AdRankItem>> future : futures) {
            try {
                merged.addAll(future.get(400, TimeUnit.MILLISECONDS));
            } catch (Exception e) {
                LOGGER.error("Execution error in batch", e);
            }
        }

        Collections.sort(merged);

        LOGGER.debug("ctr ranker java execute time: [{}ms]", System.currentTimeMillis() - startTime);
        return merged;
    }

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

        Map<String, List<Float>> scores = model.score(items, userFeatureMap, sceneFeatureMap);
        List<Float> scanResult = scores.get("scanResult");
        List<Float> addWechatResult = scores.get("addWechatResult");
        List<Float> conversionResult = scores.get("conversionResult");
        LOGGER.debug("PAIScorer score={}", scores);
        for (int i = 0; i < items.size(); i++) {
            items.get(i).setLrScore(0.0);
            if(CollectionUtils.isNotEmpty(scanResult)){
                items.get(i).getScoreMap().put("scanScore", Double.valueOf(scanResult.get(i)));
            }
            if(CollectionUtils.isNotEmpty(addWechatResult)){
                items.get(i).getScoreMap().put("addWechatScore", Double.valueOf(addWechatResult.get(i)));
            }
            if(CollectionUtils.isNotEmpty(conversionResult)){
                items.get(i).getScoreMap().put("conversionScore", Double.valueOf(conversionResult.get(i)));
            }
        }
    }


}