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

import com.alibaba.fastjson.JSONObject;
import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
import com.tzld.piaoquan.ad.engine.commons.score.ScorerUtils;
import com.tzld.piaoquan.ad.engine.commons.thread.ThreadPoolFactory;
import com.tzld.piaoquan.ad.engine.commons.util.*;
import com.tzld.piaoquan.ad.engine.service.entity.CorrectCpaParam;
import com.tzld.piaoquan.ad.engine.service.entity.GuaranteeView;
import com.tzld.piaoquan.ad.engine.service.feature.Feature;
import com.tzld.piaoquan.ad.engine.commons.dto.AdPlatformCreativeDTO;
import com.tzld.piaoquan.ad.engine.commons.param.RankRecommendRequestParam;
import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRankItem;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.xm.Similarity;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.tzld.piaoquan.ad.engine.commons.math.Const.*;

@Slf4j
@Component
public class RankStrategyBy680 extends RankStrategyBasic {

    private Map<String, double[]> bucketsMap = new HashMap<>();

    private Map<String, Double> bucketsLen = new HashMap<>();

    @Value("${word2vec.exp:694}")
    private String word2vecExp;

    @Value("${swap.exp:772}")
    protected String excludeExp;

    @ApolloJsonValue("${rank.score.weight.680:{}}")
    private Map<String, Double> weightMap;

    @ApolloJsonValue("${rank.score.neg_sample_rate:0.04}")
    Double negSampleRate;

    @PostConstruct
    public void afterInit() {
        this.readBucketFile();
    }

    @Override
    public List<AdRankItem> adItemRank(RankRecommendRequestParam request, ScoreParam scoreParam) {

        Map<String, Double> weightParam = ObjUtil.nullOrDefault(weightMap, new HashMap<>());


        Map<Long, Double> creativeScoreCoefficient = getCreativeScoreCoefficient();
        Set<String> noApiAdVerIds = getNoApiAdVerIds();

        long ts = System.currentTimeMillis() / 1000;

        String brand = scoreParam.getRequestContext().getMachineinfoBrand();
        if (StringUtils.isNotEmpty(brand)) {
            scoreParam.getRequestContext().setMachineinfoBrand(brand + "-n");
        }

        long start = System.currentTimeMillis();
        //过滤创意
        filterRequestAdList(request, scoreParam);
        // 特征处理
        // feature1
        Feature feature = this.getFeature(scoreParam, request);
        if (feature == null) {
            log.warn("adItemRank: feature is null, skip processing. request={}", request);
            return new ArrayList<>();
        }

        Map<String, Map<String, String>> userFeature = feature.getUserFeature();
        Map<String, Map<String, String>> videoFeature = feature.getVideoFeature();
        Map<String, Map<String, Map<String, String>>> allAdVerFeature = feature.getAdVerFeature();
        Map<String, Map<String, Map<String, String>>> allCidFeature = feature.getCidFeature();
        Map<String, Map<String, Map<String, String>>> allSkuFeature = feature.getSkuFeature();
        Map<String, String> reqFeature = this.getReqFeature(scoreParam, request);


        Map<String, String> userFeatureMap = new HashMap<>();
        Map<String, String> c1Feature = userFeature.getOrDefault("alg_mid_feature_ad_action", new HashMap<>());

        List<TupleMapEntry<Tuple5>> midActionList = this.handleC1Feature(c1Feature, userFeatureMap);

        Map<String, Double> midTimeDiffMap = this.parseC1FeatureListToTimeDiffMap(midActionList, ts);
        Map<String, Double> actionStaticMap = this.parseC1FeatureListToActionStaticMap(midActionList);

        Map<String, String> d2Feature = videoFeature.getOrDefault("alg_cid_feature_vid_cf_rank", new HashMap<>());
        Map<String, String> d3Feature = videoFeature.getOrDefault("alg_vid_feature_basic_info", new HashMap<>());

        Map<String, Map<String, Double>> vidRankMaps = this.parseD2FeatureMap(d2Feature);

        Map<String, String> e1Feature = userFeature.getOrDefault("alg_mid_feature_return_tags", new HashMap<>());
        Map<String, String> e2Feature = userFeature.getOrDefault("alg_mid_feature_share_tags", new HashMap<>());

        Map<String, String> sceneFeatureMap = this.handleSceneFeature(ts);
        long time1 = System.currentTimeMillis();

        boolean isGuaranteedFlow = getIsGuaranteedFlow(scoreParam);
        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, isGuaranteedFlow);
        Map<Long, CorrectCpaParam> correctCpaMap = getCorrectCpaParamMap(request, scoreParam, reqFeature);
        List<AdRankItem> adRankItems = new ArrayList<>();
        Random random = new Random();
        List<Future<AdRankItem>> futures = new ArrayList<>();
        CountDownLatch cdl1 = new CountDownLatch(request.getAdIdList().size());
        for (AdPlatformCreativeDTO dto : request.getAdIdList()) {
            Future<AdRankItem> future = ThreadPoolFactory.feature().submit(() -> {
                AdRankItem adRankItem = new AdRankItem();
                try {
                    adRankItem.setAdId(dto.getCreativeId());
                    adRankItem.setCreativeCode(dto.getCreativeCode());
                    adRankItem.setAdVerId(dto.getAdVerId());
                    adRankItem.setVideoId(request.getVideoId());
                    adRankItem.setCpa(dto.getCpa());
                    adRankItem.setId(dto.getAdId());
                    adRankItem.setCampaignId(dto.getCampaignId());
                    adRankItem.setCpm(ObjUtil.nullOrDefault(dto.getCpm(), 90).doubleValue());
                    adRankItem.setSkuId(dto.getSkuId());
                    adRankItem.setCustomerId(dto.getCustomerId());
                    adRankItem.setProfession(dto.getProfession());
                    adRankItem.setRandom(random.nextInt(1000));
                    if (noApiAdVerIds.contains(dto.getAdVerId())) {
                        adRankItem.getExt().put("isApi", "0");
                    } else {
                        adRankItem.getExt().put("isApi", "1");
                    }

                    adRankItem.getExt().put("recallsources", dto.getRecallSources());
                    fillAdRankItemExt(adRankItem, dto);
                    adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                    adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                    String cidStr = dto.getCreativeId().toString();
                    Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
                    Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());
                    Map<String, String> b1Feature = cidFeature.getOrDefault("alg_cid_feature_basic_info", new HashMap<>());

                    Map<String, Map<String, String>> adVerFeature = allAdVerFeature.getOrDefault(dto.getAdVerId(), new HashMap<>());

                    Map<String, String> d1Feature = cidFeature.getOrDefault("alg_cid_feature_vid_cf", new HashMap<>());

                    this.handleB1Feature(b1Feature, cidFeatureMap, cidStr);
                    this.handleB2ToB5AndB8ToB9Feature(cidFeature, adVerFeature, cidFeatureMap);
                    this.handleB6ToB7Feature(cidFeature, cidFeatureMap);
                    this.handleC1UIFeature(midTimeDiffMap, actionStaticMap, cidFeatureMap, cidStr);
                    this.handleD1Feature(d1Feature, cidFeatureMap);
                    this.handleD2Feature(vidRankMaps, cidFeatureMap, cidStr);
                    return adRankItem;
                } finally {
                    cdl1.countDown();
                }
            });
            futures.add(future);
        }
        try {
            cdl1.await(300, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error("handleE1AndE2Feature and handleD3AndB1Feature wait timeout", e);
        }
        for (Future<AdRankItem> future : futures) {
            try {
                if (future.isDone()) {
                    adRankItems.add(future.get());
                }
            } catch (Exception e) {
                log.error("Feature handle error", e);
            }
        }

        long time2 = System.currentTimeMillis();
        // feature3
        CountDownLatch cdl2 = new CountDownLatch(adRankItems.size() * 2);
        for (AdRankItem item : adRankItems) {
            String cidStr = String.valueOf(item.getAdId());
            Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());
            Map<String, String> b1Feature = cidFeature.getOrDefault("alg_cid_feature_basic_info", new HashMap<>());
            String title = b1Feature.getOrDefault("cidtitle", "");
            ThreadPoolFactory.defaultPool().submit(() -> {
                try {
                    this.handleE1AndE2Feature(e1Feature, e2Feature, title, item.getFeatureMap(), scoreParam);
                } finally {
                    cdl2.countDown();
                }
            });
            ThreadPoolFactory.defaultPool().submit(() -> {
                try {
                    this.handleD3AndB1Feature(d3Feature, title, item.getFeatureMap(), scoreParam);
                } finally {
                    cdl2.countDown();
                }
            });
        }
        try {
            cdl2.await(150, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error("handleE1AndE2Feature and handleD3AndB1Feature wait timeout", e);
        }

        long time3 = System.currentTimeMillis();
        // 分桶
        userFeatureMap = this.featureBucket(userFeatureMap);
        CountDownLatch cdl4 = new CountDownLatch(adRankItems.size());
        for (AdRankItem adRankItem : adRankItems) {
            ThreadPoolFactory.feature().submit(() -> {
                try {
                    Map<String, String> featureMap = adRankItem.getFeatureMap();
                    adRankItem.setFeatureMap(this.featureBucket(featureMap));
                } finally {
                    cdl4.countDown();
                }
            });
        }
        try {
            cdl4.await(100, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error("handleE1AndE2Feature and handleD3AndB1Feature wait timeout", e);
        }
        long time4 = System.currentTimeMillis();
        // 打分排序
        // getScorerPipeline
        List<AdRankItem> result = ScorerUtils.getScorerPipeline(ScorerUtils.XGBOOST_SCORE_CONF_20240909).scoring(sceneFeatureMap, userFeatureMap, adRankItems);
        long time5 = System.currentTimeMillis();

        // calibrate score for negative sampling
        for (AdRankItem item : result) {
            double originalScore = item.getLrScore();
            double calibratedScore = originalScore / (originalScore + (1 - originalScore) / negSampleRate);
            item.setLrScore(calibratedScore);
            item.getScoreMap().put("originCtcvrScore", originalScore);
            item.getScoreMap().put("modelCtcvrScore", calibratedScore);
            item.getScoreMap().put("ctcvrScore", calibratedScore);
        }

        calculateCtcvrScore(result, request, scoreParam, null, reqFeature);
        // loop
        double cpmCoefficient = weightParam.getOrDefault("cpmCoefficient", 0.9);

        boolean isGuaranteeType = false;
        for (AdRankItem item : result) {
            double bid = item.getCpa();
            if (scoreParam.getExpCodeSet().contains(correctCpaExp1) || scoreParam.getExpCodeSet().contains(correctCpaExp2)) {
                Double correctionFactor = (Double) item.getExt().get("correctionFactor");
                item.getScoreMap().put("correctionFactor", correctionFactor);
                bid = bid * correctionFactor;
            }
            item.getScoreMap().put("ecpm", item.getLrScore() * bid * 1000);
            if (isGuaranteedFlow && item.getExt().get("isGuaranteed") != null && (boolean) item.getExt().get("isGuaranteed")) {
                isGuaranteeType = true;
            }
            double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
            double score = item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient;
            item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
            item.getScoreMap().put("cpa", item.getCpa());
            item.getScoreMap().put("cpm", item.getCpm());
            item.getScoreMap().put("bid", bid);
            item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
            item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
            item.getFeatureMap().putAll(userFeatureMap);
            item.getFeatureMap().putAll(sceneFeatureMap);
            // 没有转化回传的广告主，使用后台配置的CPM
            if (noApiAdVerIds.contains(item.getAdVerId())) {
                score = item.getCpm() * cpmCoefficient / 1000;
            }
            item.setScore(score);
        }
        result.sort(ComparatorUtil.equalsRandomComparator());

        if (CollectionUtils.isNotEmpty(result)) {
            AdRankItem top1Item = result.get(0);
            top1Item.getExt().put("isGuaranteeType", isGuaranteeType);
            putMetaFeature(top1Item, feature, reqFeature, sceneFeatureMap, request);
            top1Item.getExt().put("model", "xgb");
        }
        long time6 = System.currentTimeMillis();
        log.info("cost={}, getFeature={}, handleFeature={},  similar={}, bucketFeature={}, getScorerPipeline={}, " +
                        "other={}, adIdSize={}, adRankItemsSize={}",
                time6 - start, time1 - start, time2 - time1, time3 - time2, time4 - time3,
                time5 - time4, time6 - time5, request.getAdIdList().size(), adRankItems.size());

        return result;
    }

    private void handleB1Feature(Map<String, String> b1Feature, Map<String, String> cidFeatureMap, String cid) {
        cidFeatureMap.put("cid_" + cid, "0.1");
        // if (StringUtils.isNotBlank(b1Feature.get("adid"))) {
        //     String adId = b1Feature.get("adid");
        //     cidFeatureMap.put("adid_" + adId, idDefaultValue);
        // }
        if (StringUtils.isNotBlank(b1Feature.get("adverid"))) {
            String adVerId = b1Feature.get("adverid");
            cidFeatureMap.put("adverid_" + adVerId, "0.1");
        }
        // if (StringUtils.isNotBlank(b1Feature.get("targeting_conversion"))) {
        //     String targetingConversion = b1Feature.get("targeting_conversion");
        //     cidFeatureMap.put("targeting_conversion_" + targetingConversion, idDefaultValue);
        // }
        if (StringUtils.isNotBlank(b1Feature.get("cpa"))) {
            String cpa = b1Feature.get("cpa");
            cidFeatureMap.put("cpa", cpa);
        }
    }

    private void handleB2ToB5AndB8ToB9Feature(Map<String, Map<String, String>> c1Feature, Map<String, Map<String, String>> adVerFeature, Map<String, String> cidFeatureMap) {
        Map<String, String> b2Feature = adVerFeature.getOrDefault("alg_cid_feature_adver_action", new HashMap<>());
        Map<String, String> b3Feature = c1Feature.getOrDefault("alg_cid_feature_cid_action", new HashMap<>());
        Map<String, String> b4Feature = c1Feature.getOrDefault("alg_cid_feature_region_action", new HashMap<>());
        Map<String, String> b5Feature = c1Feature.getOrDefault("alg_cid_feature_app_action", new HashMap<>());
        Map<String, String> b8Feature = c1Feature.getOrDefault("alg_cid_feature_brand_action", new HashMap<>());
        Map<String, String> b9Feature = c1Feature.getOrDefault("alg_cid_feature_weChatVersion_action", new HashMap<>());

        List<String> timeList = Arrays.asList("1h", "2h", "3h", "6h", "12h", "1d", "3d", "7d", "yesterday", "today");
        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
                new Tuple2<>(b2Feature, "b2"),
                new Tuple2<>(b3Feature, "b3"),
                new Tuple2<>(b4Feature, "b4"),
                new Tuple2<>(b5Feature, "b5"),
                new Tuple2<>(b8Feature, "b8"),
                new Tuple2<>(b9Feature, "b9")
        );
        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
            Map<String, String> feature = tuple2.f1;
            String prefix = tuple2.f2;
            for (String time : timeList) {
                double view = Double.parseDouble(feature.getOrDefault("ad_view_" + time, "0"));
                double click = Double.parseDouble(feature.getOrDefault("ad_click_" + time, "0"));
                double conver = Double.parseDouble(feature.getOrDefault("ad_conversion_" + time, "0"));
                double income = Double.parseDouble(feature.getOrDefault("ad_income_" + time, "0"));
                double cpc = NumUtil.div(income, click);
                double ctr = NumUtil.divSmoothV2(click, view, CTR_SMOOTH_BETA_FACTOR);
                double ctcvr = NumUtil.divSmoothV2(conver, view, CTCVR_SMOOTH_BETA_FACTOR);
                double ecpm = ctr * cpc * 1000;
                cidFeatureMap.put(prefix + "_" + time + "_ctr", String.valueOf(ctr));
                cidFeatureMap.put(prefix + "_" + time + "_ctcvr", String.valueOf(ctcvr));
                cidFeatureMap.put(prefix + "_" + time + "_cvr", String.valueOf(NumUtil.divSmoothV2(conver, click, CVR_SMOOTH_BETA_FACTOR)));
                cidFeatureMap.put(prefix + "_" + time + "_conver", String.valueOf(conver));
                cidFeatureMap.put(prefix + "_" + time + "_ecpm", String.valueOf(ecpm));

                cidFeatureMap.put(prefix + "_" + time + "_click", String.valueOf(click));
                cidFeatureMap.put(prefix + "_" + time + "_conver*log(view)", String.valueOf(conver * NumUtil.log(view)));
                cidFeatureMap.put(prefix + "_" + time + "_conver*ctcvr", String.valueOf(conver * ctcvr));
            }
        }

    }

    private void handleB6ToB7Feature(Map<String, Map<String, String>> c1Feature, Map<String, String> cidFeatureMap) {
        Map<String, String> b6Feature = c1Feature.getOrDefault("alg_cid_feature_week_action", new HashMap<>());
        Map<String, String> b7Feature = c1Feature.getOrDefault("alg_cid_feature_hour_action", new HashMap<>());

        List<String> timeList = Arrays.asList("7d", "14d");
        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
                new Tuple2<>(b6Feature, "b6"),
                new Tuple2<>(b7Feature, "b7")
        );
        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
            Map<String, String> feature = tuple2.f1;
            String prefix = tuple2.f2;
            for (String time : timeList) {
                double view = Double.parseDouble(feature.getOrDefault("ad_view_" + time, "0"));
                double click = Double.parseDouble(feature.getOrDefault("ad_click_" + time, "0"));
                double conver = Double.parseDouble(feature.getOrDefault("ad_conversion_" + time, "0"));
                double income = Double.parseDouble(feature.getOrDefault("ad_income_" + time, "0"));
                double cpc = NumUtil.div(income, click);
                double ctr = NumUtil.divSmoothV2(click, view, CTR_SMOOTH_BETA_FACTOR);
                double ctcvr = NumUtil.divSmoothV2(conver, view, CTCVR_SMOOTH_BETA_FACTOR);
                double ecpm = ctr * cpc * 1000;
                cidFeatureMap.put(prefix + "_" + time + "_ctr", String.valueOf(ctr));
                cidFeatureMap.put(prefix + "_" + time + "_ctcvr", String.valueOf(ctcvr));
                cidFeatureMap.put(prefix + "_" + time + "_cvr", String.valueOf(NumUtil.divSmoothV2(conver, click, CVR_SMOOTH_BETA_FACTOR)));
                cidFeatureMap.put(prefix + "_" + time + "_conver", String.valueOf(conver));
                cidFeatureMap.put(prefix + "_" + time + "_ecpm", String.valueOf(ecpm));

                cidFeatureMap.put(prefix + "_" + time + "_click", String.valueOf(click));
                cidFeatureMap.put(prefix + "_" + time + "_conver*log(view)", String.valueOf(conver * NumUtil.log(view)));
                cidFeatureMap.put(prefix + "_" + time + "_conver*ctcvr", String.valueOf(conver * ctcvr));
            }
        }

    }

    private List<TupleMapEntry<Tuple5>> handleC1Feature(Map<String, String> c1Feature, Map<String, String> featureMap) {

        // 用户特征
        List<TupleMapEntry<Tuple5>> midActionList = new ArrayList<>();
        if (c1Feature.containsKey("action")) {
            String action = c1Feature.get("action");
            midActionList = Arrays.stream(action.split(","))
                    .map(r -> {
                        String[] rList = r.split(":");
                        Tuple5 tuple5 = new Tuple5(rList[1], rList[2], rList[3], rList[4], rList[5]);
                        return new TupleMapEntry<>(rList[0], tuple5);
                    })
                    // TODO 倒排
                    .sorted((a, b) -> Integer.compare(Integer.parseInt(b.value.f1), Integer.parseInt(a.value.f1)))
                    .collect(Collectors.toList());
        }

        double viewAll = midActionList.size();
        double clickAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f2)).sum();
        double converAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f3)).sum();
        double incomeAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f4)).sum();
        featureMap.put("viewAll", String.valueOf(viewAll));
        featureMap.put("clickAll", String.valueOf(clickAll));
        featureMap.put("converAll", String.valueOf(converAll));
        featureMap.put("incomeAll", String.valueOf(incomeAll));
        featureMap.put("ctr_all", String.valueOf(NumUtil.div(clickAll, viewAll)));
        featureMap.put("ctcvr_all", String.valueOf(NumUtil.div(converAll, viewAll)));
        featureMap.put("cvr_all", String.valueOf(NumUtil.div(clickAll, converAll)));
        featureMap.put("ecpm_all", String.valueOf(NumUtil.div(incomeAll * 1000, viewAll)));

        return midActionList;
    }

    private void handleC1UIFeature(Map<String, Double> midTimeDiffMap, Map<String, Double> midActionStatic, Map<String, String> featureMap, String cid) {
        if (midTimeDiffMap.containsKey("timediff_view_" + cid)) {
            featureMap.put("timediff_view", String.valueOf(midTimeDiffMap.getOrDefault("timediff_view_" + cid, 0.0)));
        }
        if (midTimeDiffMap.containsKey("timediff_click_" + cid)) {
            featureMap.put("timediff_click", String.valueOf(midTimeDiffMap.getOrDefault("timediff_click_" + cid, 0.0)));
        }
        if (midTimeDiffMap.containsKey("timediff_conver_" + cid)) {
            featureMap.put("timediff_conver", String.valueOf(midTimeDiffMap.getOrDefault("timediff_conver_" + cid, 0.0)));
        }
        if (midActionStatic.containsKey("actionstatic_view_" + cid)) {
            featureMap.put("actionstatic_view", String.valueOf(midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0)));
        }
        if (midActionStatic.containsKey("actionstatic_click_" + cid)) {
            featureMap.put("actionstatic_click", String.valueOf(midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0)));
        }
        if (midActionStatic.containsKey("actionstatic_conver_" + cid)) {
            featureMap.put("actionstatic_conver", String.valueOf(midActionStatic.getOrDefault("actionstatic_conver_" + cid, 0.0)));
        }
        if (midActionStatic.containsKey("actionstatic_income_" + cid)) {
            featureMap.put("actionstatic_income", String.valueOf(midActionStatic.getOrDefault("actionstatic_income_" + cid, 0.0)));
        }
        if (midActionStatic.containsKey("actionstatic_view_" + cid) && midActionStatic.containsKey("actionstatic_click_" + cid)) {
            double ctr = NumUtil.div(
                    midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0),
                    midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0)
            );
            featureMap.put("actionstatic_ctr", String.valueOf(ctr));
        }
        if (midActionStatic.containsKey("actionstatic_view_" + cid) && midActionStatic.containsKey("actionstatic_conver_" + cid)) {
            double ctcvr = NumUtil.div(midActionStatic.getOrDefault("actionstatic_conver_" + cid, 0.0), midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0));
            featureMap.put("actionstatic_ctcvr", String.valueOf(ctcvr));
        }
        if (midActionStatic.containsKey("actionstatic_conver_" + cid) && midActionStatic.containsKey("actionstatic_click_" + cid)) {
            double cvr = NumUtil.div(midActionStatic.getOrDefault("actionstatic_conver_" + cid, 0.0), midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0));
            featureMap.put("actionstatic_cvr", String.valueOf(cvr));
        }
    }

    private void handleD1Feature(Map<String, String> d1Feature, Map<String, String> featureMap) {
        for (String prefix : Arrays.asList("3h", "6h", "12h", "1d", "3d", "7d")) {
            double view = Double.parseDouble(d1Feature.getOrDefault("ad_view_" + prefix, "0"));
            double click = Double.parseDouble(d1Feature.getOrDefault("ad_click_" + prefix, "0"));
            double conver = Double.parseDouble(d1Feature.getOrDefault("ad_conversion_" + prefix, "0"));
            double income = Double.parseDouble(d1Feature.getOrDefault("ad_income_" + prefix, "0"));
            double cpc = NumUtil.div(income, click);
            double ctr = NumUtil.divSmoothV2(click, view, CTR_SMOOTH_BETA_FACTOR);
            featureMap.put("d1_feature_" + prefix + "_ctr", String.valueOf(ctr));
            featureMap.put("d1_feature_" + prefix + "_ctcvr", String.valueOf(NumUtil.divSmoothV2(conver, view, CTCVR_SMOOTH_BETA_FACTOR)));
            featureMap.put("d1_feature_" + prefix + "_cvr", String.valueOf(NumUtil.divSmoothV2(conver, click, CVR_SMOOTH_BETA_FACTOR)));
            featureMap.put("d1_feature_" + prefix + "_conver", String.valueOf(conver));
            featureMap.put("d1_feature_" + prefix + "_ecpm", String.valueOf(ctr * cpc * 1000));
        }
    }

    private void handleD2Feature(Map<String, Map<String, Double>> vidRankMaps, Map<String, String> featureMap, String cid) {
        if (MapUtils.isEmpty(vidRankMaps)) {
            return;
        }

        List<String> prefixes1 = Arrays.asList("ctr", "ctcvr", "ecpm");
        // List<String> prefixes1 = Arrays.asList("ctr", "ctcvr");
        List<String> prefixes2 = Arrays.asList("1d", "3d", "7d", "14d");

        for (String prefix1 : prefixes1) {
            for (String prefix2 : prefixes2) {
                String combinedKey = prefix1 + "_" + prefix2;
                if (vidRankMaps.containsKey(combinedKey)) {
                    Double rank = vidRankMaps.get(combinedKey).getOrDefault(cid, 0.0);
                    if (rank >= 1.0) {
                        featureMap.put("vid_rank_" + combinedKey, String.valueOf(NumUtil.div(1, rank)));
                    }
                }
            }
        }
    }

    private void handleD3AndB1Feature(Map<String, String> d3Feature, String cTitle, Map<String, String> featureMap,
                                      ScoreParam scoreParam) {
        if (MapUtils.isEmpty(d3Feature) || !d3Feature.containsKey("title") || StringUtils.isEmpty(cTitle)) {
            return;
        }
        String vTitle = d3Feature.get("title");
        double score;
        if (scoreParam.getExpCodeSet().contains(word2vecExp)) {
            score = SimilarityUtils.word2VecSimilarity(cTitle, vTitle);
        } else {
            score = Similarity.conceptSimilarity(cTitle, vTitle);
        }
        featureMap.put("ctitle_vtitle_similarity", String.valueOf(score));
    }

    private void handleE1AndE2Feature(Map<String, String> e1Feature, Map<String, String> e2Feature, String title,
                                      Map<String, String> featureMap, ScoreParam scoreParam) {
        if (StringUtils.isEmpty(title)) {
            return;
        }

        List<Tuple2<Map<String, String>, String>> tuple2List = Arrays.asList(new Tuple2<>(e1Feature, "e1"), new Tuple2<>(e2Feature, "e2"));

        List<String> tagsFieldList = Arrays.asList("tags_3d", "tags_7d", "tags_14d");
        for (Tuple2<Map<String, String>, String> tuple2 : tuple2List) {
            Map<String, String> feature = tuple2.f1;
            String prefix = tuple2.f2;
            if (MapUtils.isEmpty(feature)) {
                continue;
            }

            for (String tagsField : tagsFieldList) {
                if (StringUtils.isNotEmpty(feature.get(tagsField))) {
                    String tags = feature.get(tagsField);
                    // Double[] doubles = ExtractorUtils.funcC34567ForTags(tags, title);
                    Double[] doubles;
                    if (scoreParam.getExpCodeSet().contains(word2vecExp)) {
                        doubles = ExtractorUtils.funcC34567ForTagsNew(tags, title);
                    } else {
                        doubles = ExtractorUtils.funcC34567ForTags(tags, title);
                    }
                    featureMap.put(prefix + "_" + tagsField + "_matchnum", String.valueOf(doubles[0]));
                    featureMap.put(prefix + "_" + tagsField + "_maxscore", String.valueOf(doubles[1]));
                    featureMap.put(prefix + "_" + tagsField + "_avgscore", String.valueOf(doubles[2]));
                }
            }
        }
    }

    private Map<String, Double> parseC1FeatureListToTimeDiffMap(List<TupleMapEntry<Tuple5>> midActionList, long ts) {
        Map<String, Double> midTimeDiffMap = new HashMap<>();
        for (TupleMapEntry<Tuple5> entry : midActionList) {
            String cid = entry.key;
            double tsHistory = Double.parseDouble(entry.value.f1);
            double click = Double.parseDouble(entry.value.f2);
            double conver = Double.parseDouble(entry.value.f3);
            double d = (ts - tsHistory) / 3600 / 24;
            if (!midTimeDiffMap.containsKey("timediff_view_" + cid)) {
                midTimeDiffMap.put("timediff_view_" + cid, NumUtil.div(1, d));
            }
            if (!midTimeDiffMap.containsKey("timediff_click_" + cid) && click > 0) {
                midTimeDiffMap.put("timediff_click_" + cid, NumUtil.div(1, d));
            }
            if (!midTimeDiffMap.containsKey("timediff_conver_" + cid) && conver > 0) {
                midTimeDiffMap.put("timediff_conver_" + cid, NumUtil.div(1, d));
            }
        }
        return midTimeDiffMap;
    }

    private Map<String, Double> parseC1FeatureListToActionStaticMap(List<TupleMapEntry<Tuple5>> midActionList) {
        Map<String, Double> midActionStaticsMap = new HashMap<>();
        for (TupleMapEntry<Tuple5> entry : midActionList) {
            String cid = entry.key;
            double click = Double.parseDouble(entry.value.f2);
            double conver = Double.parseDouble(entry.value.f3);
            double income = Double.parseDouble(entry.value.f4);

            Double viewSum = midActionStaticsMap.getOrDefault("actionstatic_view_" + cid, 0.0);
            midActionStaticsMap.put("actionstatic_view_" + cid, 1 + viewSum);

            Double clickSum = midActionStaticsMap.getOrDefault("actionstatic_click_" + cid, 0.0);
            midActionStaticsMap.put("actionstatic_click_" + cid, clickSum + click);

            Double converSum = midActionStaticsMap.getOrDefault("actionstatic_conver_" + cid, 0.0);
            midActionStaticsMap.put("actionstatic_conver_" + cid, converSum + conver);

            Double incomSum = midActionStaticsMap.getOrDefault("actionstatic_income_" + cid, 0.0);
            midActionStaticsMap.put("actionstatic_income_" + cid, incomSum + income);
        }

        return midActionStaticsMap;
    }

    private Map<String, Map<String, Double>> parseD2FeatureMap(Map<String, String> d2Feature) {
        Map<String, Map<String, Double>> vidRankMaps = new HashMap<>();
        for (Map.Entry<String, String> entry : d2Feature.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            Map<String, Double> valueMap = Arrays.stream(value.split(",")).map(r -> r.split(":")).collect(Collectors.toMap(rList -> rList[0], rList -> Double.parseDouble(rList[2])));
            vidRankMaps.put(key, valueMap);
        }
        return vidRankMaps;
    }

    private void readBucketFile() {
        if (MapUtils.isNotEmpty(bucketsMap)) {
            return;
        }
        synchronized (this) {
            String bucketFile = "20250217_ad_bucket_688.txt";
            InputStream resourceStream = RankStrategyBy680.class.getClassLoader().getResourceAsStream(bucketFile);
            if (resourceStream != null) {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) {
                    Map<String, double[]> bucketsMap = new HashMap<>();
                    Map<String, Double> bucketsLen = new HashMap<>();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        // 替换空格和换行符，过滤空行
                        line = line.replace(" ", "").replaceAll("\n", "");
                        if (!line.isEmpty()) {
                            String[] rList = line.split("\t");
                            if (rList.length == 3) {
                                String key = rList[0];
                                double value1 = Double.parseDouble(rList[1]);
                                bucketsLen.put(key, value1);
                                double[] value2 = Arrays.stream(rList[2].split(",")).mapToDouble(Double::valueOf).toArray();
                                bucketsMap.put(key, value2);
                            }
                        }
                    }
                    this.bucketsMap = bucketsMap;
                    this.bucketsLen = bucketsLen;
                } catch (IOException e) {
                    log.error("something is wrong in parse bucket file: ", e);
                }
                log.info("load bucket file success: {}", bucketFile);
            } else {
                log.error("no bucket file");
            }
        }
    }

    private Map<String, String> featureBucket(Map<String, String> featureMap) {
        Map<String, String> newFeatureMap = new ConcurrentHashMap<>(featureMap.size());
        for (Map.Entry<String, String> entry : featureMap.entrySet()) {
            String name = entry.getKey();
            double score = Double.parseDouble(entry.getValue());
            // 注意：0值、不在分桶文件中的特征，会被过滤掉。
            if (score > 1E-8) {
                if (this.bucketsMap.containsKey(name) && this.bucketsLen.containsKey(name)) {
                    double[] buckets = this.bucketsMap.get(name);
                    double bucketNum = this.bucketsLen.get(name);
                    Double scoreNew = 1.0 / bucketNum * (ExtractorUtils.findInsertPosition(buckets, score) + 1.0);
                    newFeatureMap.put(name, String.valueOf(scoreNew));
                } else {
                    newFeatureMap.put(name, String.valueOf(score));
                }
            }
        }

        return newFeatureMap;
    }

}