package com.tzld.piaoquan.ad.engine.service.predict.model.threshold;

import com.alibaba.fastjson.JSONObject;
import com.tzld.piaoquan.ad.engine.commons.redis.AlgorithmRedisHelper;
import com.tzld.piaoquan.ad.engine.service.predict.config.AdOutV1OnlineWeightConfig;
import com.tzld.piaoquan.ad.engine.service.predict.constant.RuleRedisKeyConst;
import com.tzld.piaoquan.ad.engine.service.predict.container.ThresholdModelContainer;
import com.tzld.piaoquan.ad.engine.service.predict.helper.RuleParamHelper;
import com.tzld.piaoquan.ad.engine.service.predict.param.ThresholdPredictModelParam;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.*;

@Component
public class ScoreThresholdPredictModel extends ThresholdPredictModel{

    @Autowired
    RuleParamHelper paramHelper;

    @Autowired
    AlgorithmRedisHelper redisHelper;

    @Override
    String initName() {
        return "model";
    }
    @Override
    public Map<String, Object> predict(ThresholdPredictModelParam modelParam) {
        Map<String, Object> result;

        String modelKey = (String) modelParam.getAbtestParam().getOrDefault("model_key", "ad_out_v1");
        String userKeyName = RuleRedisKeyConst.KEY_NAME_PREFIX_AD_OUT_MODEL_SCORE_USER + modelKey + ":" + modelParam.getMid();
        String itemKeyName = RuleRedisKeyConst.KEY_NAME_PREFIX_AD_OUT_MODEL_SCORE_ITEM + modelKey + ":" + modelParam.getVideoId();

        String userScore = redisHelper.get(userKeyName);

        String itemScore = redisHelper.get(itemKeyName);


        //加载配置数据
        String configKeyNamePrefix = RuleRedisKeyConst.KEY_NAME_PREFIX_AD_OUT_MODEL_CONFIG
                + modelKey + ":" + modelParam.getAbtestId() + ":"
//                + modelParam.getAbTestConfigTag();
                + modelParam.getAbtestParam().get("abtest_config_tag");
        String configKey = configKeyNamePrefix + ":config";
        String configStr = redisHelper.get(configKey);

        Map<String, Object> configMap = new HashMap<>();
        String hitStrategy="model";
        if (configStr != null) {
            try {
                configMap= JSONObject.parseObject(configStr);
            } catch (Exception e) {
                // 处理 JSON 解析异常
                e.printStackTrace();
                result = ThresholdModelContainer.
                        getBasicPredictModel()
                        .predict(modelParam);
                hitStrategy="error";
                result.put("hit_strategy",hitStrategy);
                return result;
            }
        }


        boolean useBackup = Boolean.valueOf((String)configMap.getOrDefault("use_backup_key","false"));


        //有数据为空且开启兜底策略
        if (
                (StringUtils.isBlank(userScore) || StringUtils.isBlank(itemScore))
                &&useBackup)
        {
            // If offline scores are empty, fall back to baseline logic
            result = ThresholdModelContainer.
                    getBasicPredictModel()
                    .predict(modelParam);
            hitStrategy="backup";
            result.put("hit_strategy",hitStrategy);
            return result;
        }

        double offlineScore ;
        //处理空值逻辑
        double threshold=(double)configMap.getOrDefault("threshold",0);
        if(StringUtils.isBlank(userScore)&&StringUtils.isBlank(itemScore)){
            itemScore="0";
            userScore="0";
            threshold=(double)configMap.getOrDefault("miss_threshold", 0);

        }else if(StringUtils.isBlank(userScore)||StringUtils.isBlank(itemScore)){
            boolean isUserScoreBlank=StringUtils.isBlank(userScore);
            userScore=isUserScoreBlank?"0":userScore;
            itemScore=isUserScoreBlank?itemScore:"0";
            threshold=isUserScoreBlank?
                    (double)configMap.getOrDefault("item_threshold", 0):
                    (double)configMap.getOrDefault("user_threshold", 0);
            hitStrategy=isUserScoreBlank?"item":"user";
        }

        //获取计算参数
        SimpleDateFormat hourFormat = new SimpleDateFormat("HH");
        SimpleDateFormat weekDayFormat = new SimpleDateFormat("u");
        Map<String, String> onlineFeatures = new HashMap<>();
        onlineFeatures.put("ctx_apptype", modelParam.getAppType().toString());
        onlineFeatures.put("ctx_week", weekDayFormat.format(modelParam.getDate()));
        onlineFeatures.put("ctx_hour", hourFormat.format(modelParam.getDate()));

        //新模型更新权重计算
        double rankScore = 0.0;
        if ((Boolean) configMap.getOrDefault("use_rank_score", false)) {
            String rankScoreKey = "rank:score1:" + modelParam.getVideoId();
            String rankScoreStr = redisHelper.get(rankScoreKey);
            if (rankScoreStr != null) {
                rankScore = Double.parseDouble(rankScoreStr);
            }
        }

        offlineScore=Double.parseDouble(userScore) + Double.parseDouble(itemScore);
        double onlineScore=getOlineScore(onlineFeatures);
        double rankScoreBias = (double)configMap.getOrDefault("rank_score_bias", 0.0);
        double finalScore =getFinalScore(onlineScore,offlineScore);

        double rankScoreW = (double)configMap.getOrDefault("rank_score_w", 1.0);
        double finalScoreW = (double)configMap.getOrDefault("final_score_w", 1.0);
        double mergeScore = finalScoreW * finalScore + rankScoreW * (rankScore + rankScoreBias);

        int adPredict;
        if (mergeScore < threshold) {
            // If final score is below threshold, show the ad
            adPredict = 2;
        } else {
            // Otherwise, do not show the ad
            adPredict = 1;
        }

        result=new HashMap<>();
        result.put("user_score", userScore);
        result.put("item_score", itemScore);
        result.put("final_score", finalScore);
        result.put("merge_score", mergeScore);
        result.put("rank_score", rankScore);
        result.put("rank_score_bias", rankScoreBias);
        result.put("online_score", onlineScore);
        result.put("threshold", threshold);
        result.put("ad_predict", adPredict);
        result.put("online_features", onlineFeatures);
        result.put("hit_strategy",hitStrategy);
        return result;
    }

    private double getFinalScore(Double onlineScore,Double offlineScore) {
        double finalScore = onlineScore + offlineScore;
        return 1.0 / (1.0 + Math.exp(0d-finalScore));
    }

    private double getOlineScore(Map<String, String> onlineFeatures ) {
        double score = AdOutV1OnlineWeightConfig.getWeight("bias");
        for (Map.Entry<String, String> entry : onlineFeatures.entrySet()) {
            double featureScore = AdOutV1OnlineWeightConfig.getWeight(entry.getKey() + "#" +  entry.getValue());
            score += featureScore;
        }
        return score;
    }
}
