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

import com.alibaba.fastjson.JSON;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
import com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto;
import com.tzld.piaoquan.recommend.server.model.MachineInfo;
import com.tzld.piaoquan.recommend.server.remote.FeatureV2RemoteService;
import com.tzld.piaoquan.recommend.server.service.rank.RankParam;
import com.tzld.piaoquan.recommend.server.service.rank.bo.UserSRBO;
import com.tzld.piaoquan.recommend.server.service.rank.bo.UserShareReturnProfile;
import com.tzld.piaoquan.recommend.server.util.FeatureUtils;
import com.tzld.piaoquan.recommend.server.util.JSONUtils;
import lombok.Data;
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.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author dyp
 */
@Service
@Slf4j
public class FeatureService {

    @Autowired
    private FeatureV2RemoteService remoteService;

    private static final Set<String> hotSceneSet = new HashSet<>(Arrays.asList("1008", "1007", "1058", "1074", "1010"));
    private static final Set<String> hotSceneTypeSet = new HashSet<>(Arrays.asList("1008", "1007"));

    public Map<String, Map<String, Map<String, String>>> getVideoStatistics(List<String> vids) {
        if (CollectionUtils.isEmpty(vids)) {
            return new HashMap<>();
        }
        List<FeatureKeyProto> protos = new ArrayList<>(vids.size());
        for (String vid : vids) {
            protos.add(genWithKeyMap("alg_vid_feature_b_c_data", vid, ImmutableMap.of("vid", vid)));
            protos.add(genWithKeyMap("alg_vid_feature_cn_dn_data", vid, ImmutableMap.of("vid", vid)));
        }
        Feature feature = getFeatureByProto(protos);
        return feature.getVideoFeature();
    }

    public Map<String, Map<String, Map<String, String>>> getVideoBaseInfo(String headVid, List<String> vidList) {
        List<FeatureKeyProto> protos = new ArrayList<>();
        if (null != headVid && !headVid.isEmpty()) {
            protos.add(genWithKeyMap("alg_vid_feature_basic_info", headVid, ImmutableMap.of("vid", headVid)));
        }
        for (String vid : vidList) {
            protos.add(genWithKeyMap("alg_vid_feature_basic_info", vid, ImmutableMap.of("vid", vid)));
        }
        Feature feature = getFeatureByProto(protos);
        return feature.getVideoFeature();
    }

    public Map<String, Map<String, String>> getUnionIdFeature(String unionId) {
        if (StringUtils.isBlank(unionId)) {
            return new HashMap<>();
        }
        List<FeatureKeyProto> protos = new ArrayList<>();
        protos.add(genWithUnionId("alg_user_network_seq_feature", unionId));
        return this.getFeatureByProto(protos).getUserFeature();
    }

    /**
     * @return k1:视频、k2:表、k3:特征、v:特征值
     */
    public Feature getFeatureV3(RankParam param, Map<String, Map<String, Map<String, String>>> videoBaseInfoMap, List<String> vidList) {
        String mid = param.getMid();
        String mergeMid = getMergeMid(param.getUid(), mid);
        String headVid = String.valueOf(param.getHeadVid());
        String province = param.getProvince().replaceAll("省$", "");
        String apptype = param.getAppType() + "";
        String orgHotScene = String.valueOf(param.getHotSceneType());
        String brand = "";
        if (null != param.getMachineInfo()) {
            MachineInfo machineInfo = param.getMachineInfo();
            if (null != machineInfo.getBrand()) {
                brand = machineInfo.getBrand();
            }
        }
        String hotSceneType = orgHotScene;
        if (!hotSceneTypeSet.contains(hotSceneType)) {
            hotSceneType = "other";
        }
        String senceType = orgHotScene;
        if (!hotSceneSet.contains(senceType)) {
            senceType = "other";
        }

        List<FeatureKeyProto> protos = new ArrayList<>();
        for (String vid : vidList) {
            // ********************* old vid ******************
            // protos.add(genWithVid("alg_vid_feature_all_exp_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_all_share", vid));
            // protos.add(genWithVid("alg_vid_feature_all_return", vid));
            // protos.add(genWithVid("alg_vid_feature_exp2share_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_share2return", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_noflow_exp_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_noflow_root_share_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_noflow_root_return_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_flow_exp_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_flow_root_share_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_flow_root_return_v2", vid));

            // vid + province
            // protos.add(genWithVidAndProvince("alg_vid_feature_feed_province_exp_v2", vid, province));
            // protos.add(genWithVidAndProvince("alg_vid_feature_feed_province_root_share_v2", vid, province));
            // protos.add(genWithVidAndProvince("alg_vid_feature_feed_province_root_return_v2", vid, province));

            // ********************* new vid ******************
            protos.add(genWithKeyMap("alg_recsys_feature_video_clean_stat", vid, ImmutableMap.of("vid", vid)));
            protos.add(genWithKeyMap("alg_vid_global_feature_20250212", vid, ImmutableMap.of("vid", vid)));
            protos.add(genWithKeyMap("alg_vid_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid)));
            protos.add(genWithKeyMap("alg_vid_recommend_flowpool_exp_feature_20250212", vid, ImmutableMap.of("vid", vid)));
            // protos.add(genWithKeyMap("alg_vid_long_period_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid)));
            protos.add(genWithKeyMap("alg_vid_apptype_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "apptype", apptype)));
            protos.add(genWithKeyMap("alg_vid_province_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "province", province)));
            protos.add(genWithKeyMap("alg_vid_brand_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "brand", brand)));
            protos.add(genWithKeyMap("alg_vid_hotsencetype_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "hotsencetype", hotSceneType)));

            protos.add(genWithKeyMap("scene_type_vid_cf_feature_20250212", vid, ImmutableMap.of("sence_type", senceType, "vid_a", headVid, "vid_b", vid)));
            protos.add(genWithKeyMap("vid_click_cf_feature_20250212", vid, ImmutableMap.of("vid_a", headVid, "vid_b", vid)));
            protos.add(genWithKeyMap("alg_recsys_feature_cf_i2i_v2", vid, ImmutableMap.of("vid_a", headVid, "vid_b", vid)));
            if (null != videoBaseInfoMap) {
                Map<String, Map<String, String>> videoInfo = videoBaseInfoMap.getOrDefault(vid, new HashMap<>());
                Map<String, String> baseInfo = videoInfo.getOrDefault("alg_vid_feature_basic_info", new HashMap<>());
                String merge_cate1 = baseInfo.getOrDefault("merge_first_level_cate", "unknown").trim();
                protos.add(genWithKeyMap("alg_merge_cate1_recommend_exp_feature_20250212", vid, ImmutableMap.of("merge_cate1", merge_cate1)));
                // 特殊情况
                // protos.add(genWithKeyMap("mid_merge_cate1_feature_20250212", vid, ImmutableMap.of("mid", mid, "merge_cate1", merge_cate1)));

                String merge_cate2 = baseInfo.getOrDefault("merge_second_level_cate", "unknown").trim();
                protos.add(genWithKeyMap("alg_merge_cate2_recommend_exp_feature_20250212", vid, ImmutableMap.of("merge_cate2", merge_cate2)));
                // 特殊情况
                // protos.add(genWithKeyMap("mid_merge_cate2_feature_20250212", vid, ImmutableMap.of("mid", mid, "merge_cate2", merge_cate2)));

                String channel = baseInfo.getOrDefault("channel", "unknown").trim();
                protos.add(genWithKeyMap("alg_channel_recommend_exp_feature_20250212", vid, ImmutableMap.of("channel", channel)));

                String festive = baseInfo.getOrDefault("festive_label2", "unknown").trim();
                protos.add(genWithKeyMap("alg_festive_recommend_exp_feature_20250212", vid, ImmutableMap.of("festive", festive)));

                String videoUnionid = baseInfo.getOrDefault("title_time_w_h_unionid", "unknown");
                protos.add(genWithKeyMap("alg_video_unionid_recommend_exp_feature_20250212", vid, ImmutableMap.of("video_unionid", videoUnionid)));
            }
        }

        // user
        // protos.add(genWithMid("alg_mid_feature_play", mid));
        // protos.add(genWithMid("alg_mid_feature_share_and_return", mid));
        // protos.add(genWithMid("alg_mid_feature_play_tags", mid));
        protos.add(genWithMid("alg_mid_feature_return_tags", mid));
        protos.add(genWithMid("alg_mid_feature_share_tags", mid));
        // protos.add(genWithMid("alg_mid_feature_feed_exp_share_tags_v2", mid));
        // protos.add(genWithMid("alg_mid_feature_feed_exp_return_tags_v2", mid));
        // protos.add(genWithMid("alg_mid_feature_sharecf", mid));
        // protos.add(genWithMid("alg_mid_feature_returncf", mid));

        // ********************* new mid ******************
        protos.add(genWithMid("mid_global_feature_20250212", mid));
        // protos.add(genWithMid("mid_u2u_friend_index_feature_20250212", mid));
        protos.add(genWithMid("alg_recsys_feature_user_share_return_stat", mergeMid));

        return getFeatureByProto(protos);
    }

    /**
     * @return k1:视频、k2:表、k3:特征、v:特征值
     */
    public Feature getFeatureV4(RankParam param, Map<String, String> headInfo, Map<String, Map<String, Map<String, String>>> videoBaseInfoMap, List<String> vidList) {
        String mid = param.getMid();
        String mergeMid = getMergeMid(param.getUid(), mid);
        String headVid = String.valueOf(param.getHeadVid());
        String province = param.getProvince().replaceAll("省$", "");
        String apptype = param.getAppType() + "";
        String orgHotScene = String.valueOf(param.getHotSceneType());
        String brand = "";
        if (null != param.getMachineInfo()) {
            MachineInfo machineInfo = param.getMachineInfo();
            if (null != machineInfo.getBrand()) {
                brand = machineInfo.getBrand();
            }
        }
        String hotSceneType = orgHotScene;
        if (!hotSceneTypeSet.contains(hotSceneType)) {
            hotSceneType = "other";
        }
        String senceType = orgHotScene;
        if (!hotSceneSet.contains(senceType)) {
            senceType = "other";
        }
        String userChannel = "-1";
        if (null != param.getChannelName() && !param.getChannelName().isEmpty()) {
            userChannel = param.getChannelName();
        }
        String userLayer = "非0层";
        if (FeatureUtils.firstLevel(param.getUserShareDepth())) {
            userLayer = "0层";
        }
        String unionid = "-1";
        if (null != headInfo) {
            unionid = headInfo.getOrDefault("title_time_w_h_unionid", unionid);
        }

        List<FeatureKeyProto> protos = new ArrayList<>();
        // vid
        for (String vid : vidList) {
            protos.add(genWithKeyMap("alg_recsys_feature_video_clean_stat", vid, ImmutableMap.of("vid", vid)));
            protos.add(genWithKeyMap("alg_vid_global_feature_20250212", vid, ImmutableMap.of("vid", vid)));
            protos.add(genWithKeyMap("alg_vid_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid)));
            protos.add(genWithKeyMap("alg_vid_recommend_flowpool_exp_feature_20250212", vid, ImmutableMap.of("vid", vid)));
            protos.add(genWithKeyMap("alg_vid_apptype_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "apptype", apptype)));
            protos.add(genWithKeyMap("alg_vid_province_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "province", province)));
            protos.add(genWithKeyMap("alg_vid_brand_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "brand", brand)));
            protos.add(genWithKeyMap("alg_vid_hotsencetype_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "hotsencetype", hotSceneType)));

            protos.add(genWithKeyMap("scene_type_vid_cf_feature_20250212", vid, ImmutableMap.of("sence_type", senceType, "vid_a", headVid, "vid_b", vid)));
            protos.add(genWithKeyMap("vid_click_cf_feature_20250212", vid, ImmutableMap.of("vid_a", headVid, "vid_b", vid)));
            protos.add(genWithKeyMap("alg_recsys_feature_cf_i2i_v2", vid, ImmutableMap.of("vid_a", headVid, "vid_b", vid)));
            protos.add(genWithKeyMap("alg_recsys_feature_video_recommend_channel_layer", vid, ImmutableMap.of("channel", userChannel, "layer", userLayer, "vid", vid)));
            protos.add(genWithKeyMap("alg_recsys_feature_video_recommend_channel_layer_head", vid, ImmutableMap.of("channel", userChannel, "layer", userLayer, "unionid", unionid, "vid", vid)));
            if (null != videoBaseInfoMap) {
                Map<String, Map<String, String>> videoInfo = videoBaseInfoMap.getOrDefault(vid, new HashMap<>());
                Map<String, String> baseInfo = videoInfo.getOrDefault("alg_vid_feature_basic_info", new HashMap<>());
                String merge_cate1 = baseInfo.getOrDefault("merge_first_level_cate", "unknown").trim();
                protos.add(genWithKeyMap("alg_merge_cate1_recommend_exp_feature_20250212", vid, ImmutableMap.of("merge_cate1", merge_cate1)));

                String merge_cate2 = baseInfo.getOrDefault("merge_second_level_cate", "unknown").trim();
                protos.add(genWithKeyMap("alg_merge_cate2_recommend_exp_feature_20250212", vid, ImmutableMap.of("merge_cate2", merge_cate2)));

                String channel = baseInfo.getOrDefault("channel", "unknown").trim();
                protos.add(genWithKeyMap("alg_channel_recommend_exp_feature_20250212", vid, ImmutableMap.of("channel", channel)));

                String festive = baseInfo.getOrDefault("festive_label2", "unknown").trim();
                protos.add(genWithKeyMap("alg_festive_recommend_exp_feature_20250212", vid, ImmutableMap.of("festive", festive)));

                String videoUnionid = baseInfo.getOrDefault("title_time_w_h_unionid", "unknown");
                protos.add(genWithKeyMap("alg_video_unionid_recommend_exp_feature_20250212", vid, ImmutableMap.of("video_unionid", videoUnionid)));
            }
        }
        // user
        protos.add(genWithMid("alg_mid_feature_return_tags", mid));
        protos.add(genWithMid("alg_mid_feature_share_tags", mid));
        protos.add(genWithMid("mid_global_feature_20250212", mid));
        protos.add(genWithMid("alg_recsys_feature_user_share_return_stat", mergeMid));

        if (StringUtils.isNotBlank(param.getUnionId())) {
            protos.add(genWithUnionId("alg_user_network_seq_feature", param.getUnionId()));
        }

        return getFeatureByProto(protos);
    }

    public Feature getFeatureByNewLabel(String appType, String hotSceneType, String province, String brand, String mid, String headVideoId, List<String> vidList, Map<String, Map<String, Map<String, String>>> videoBaseInfoMap) {

        List<FeatureKeyProto> protos = new ArrayList<>();
        // 视频维度的特征
        for (String vid : vidList) {
            protos.add(this.genWithKeyMap("alg_vid_global_feature_20250212", vid, ImmutableMap.of("vid", vid)));
            protos.add(this.genWithKeyMap("alg_vid_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid)));
            protos.add(this.genWithKeyMap("alg_vid_recommend_flowpool_exp_feature_20250212", vid, ImmutableMap.of("vid", vid)));
            protos.add(this.genWithKeyMap("alg_vid_apptype_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "apptype", appType)));
            protos.add(this.genWithKeyMap("alg_vid_province_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "province", province)));
            protos.add(this.genWithKeyMap("alg_vid_brand_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "brand", brand)));
            protos.add(this.genWithKeyMap("alg_vid_hotsencetype_recommend_exp_feature_20250212", vid, ImmutableMap.of("vid", vid, "hotsencetype", hotSceneType)));
            protos.add(this.genWithKeyMap("scene_type_vid_cf_feature_20250212", vid, ImmutableMap.of("vid_a", headVideoId, "vid_b", vid, "sence_type", hotSceneType)));
            protos.add(this.genWithKeyMap("vid_click_cf_feature_20250212", vid, ImmutableMap.of("vid_a", headVideoId, "vid_b", vid)));
            protos.add(this.genWithKeyMap("alg_recsys_feature_cf_i2i_v2", vid, ImmutableMap.of("vid_a", headVideoId, "vid_b", vid)));

            if (MapUtils.isNotEmpty(videoBaseInfoMap)) {
                Map<String, Map<String, String>> baseInfo = videoBaseInfoMap.getOrDefault(vid, new HashMap<>());
                Map<String, String> featureMap = baseInfo.getOrDefault("alg_vid_feature_basic_info", new HashMap<>());
                protos.add(this.genWithKeyMap("alg_channel_recommend_exp_feature_20250212", vid, ImmutableMap.of("channel", featureMap.getOrDefault("channel", "unknown").trim())));
                protos.add(this.genWithKeyMap("alg_merge_cate1_recommend_exp_feature_20250212", vid, ImmutableMap.of("merge_cate1", featureMap.getOrDefault("merge_first_level_cate", "unknown").trim())));
                protos.add(this.genWithKeyMap("alg_merge_cate2_recommend_exp_feature_20250212", vid, ImmutableMap.of("merge_cate2", featureMap.getOrDefault("merge_second_level_cate", "unknown").trim())));
                protos.add(this.genWithKeyMap("alg_video_unionid_recommend_exp_feature_20250212", vid, ImmutableMap.of("video_unionid", featureMap.getOrDefault("title_time_w_h_unionid", "unknown"))));

                // protos.add(this.genWithKeyMap("mid_merge_cate1_feature_20250212", vid, ImmutableMap.of("mid", mid, "merge_cate1", featureMap.getOrDefault("merge_first_level_cate", "unknown").trim())));
                // protos.add(this.genWithKeyMap("mid_merge_cate2_feature_20250212", vid, ImmutableMap.of("mid", mid, "merge_cate2", featureMap.getOrDefault("merge_second_level_cate", "unknown").trim())));

            }
        }


        // 用户维度特征
        protos.add(this.genWithMid("mid_global_feature_20250212", mid));
        // protos.add(this.genWithMid("mid_u2u_friend_index_feature_20250212", mid));
        protos.add(this.genWithMid("alg_mid_feature_return_tags", mid));
        protos.add(this.genWithMid("alg_mid_feature_share_tags", mid));
        // protos.add(this.genWithMid("alg_mid_feature_sharecf", mid));
        // protos.add(this.genWithMid("alg_mid_feature_returncf", mid));

        return this.getFeatureByProto(protos);
    }

    public UserShareReturnProfile getUserProfile(String uid, String mid) {
        try {
            String mergeMid = getMergeMid(uid, mid);
            String table = "alg_recsys_feature_user_share_return_stat";
            List<FeatureKeyProto> protos = new ArrayList<>();
            protos.add(genWithMid(table, mergeMid));
            Feature feature = getFeatureByProto(protos);
            Map<String, Map<String, String>> userFeature = feature.getUserFeature();
            if (null != userFeature) {
                Map<String, String> c9 = userFeature.get(table);
                if (null != c9 && !c9.isEmpty()) {
                    String c9Str = JSONUtils.toJson(c9);
                    if (!c9Str.isEmpty()) {
                        return JSON.parseObject(c9Str, UserShareReturnProfile.class);
                    }
                }
            }
        } catch (Exception e) {
            log.error("get user profile error! value=[{}]", mid, e);
        }
        return null;
    }

    public Map<String, Map<String, String>> getBehaviorVideos(String headVid, UserShareReturnProfile userProfile) {
        Map<String, Map<String, String>> behaviorVideos = new HashMap<>();
        try {
            Set<String> vidSet = new HashSet<>();
            if (null != headVid && !headVid.isEmpty()) {
                vidSet.add(headVid);
            }
            if (null != userProfile) {
                for (List<UserSRBO> list : Arrays.asList(userProfile.getM_s_s(), userProfile.getM_r_s(), userProfile.getL_s_s(), userProfile.getL_r_s(), userProfile.getL_r1_s())) {
                    if (null != list) {
                        for (UserSRBO u : list) {
                            if (null != u) {
                                vidSet.add(u.getId() + "");
                            }
                        }
                    }
                }
            }
            if (!vidSet.isEmpty()) {
                Map<String, Map<String, Map<String, String>>> videoMap = getVideoBaseInfo("", new ArrayList<>(vidSet));
                if (null != videoMap && !videoMap.isEmpty()) {
                    for (Map.Entry<String, Map<String, Map<String, String>>> entry : videoMap.entrySet()) {
                        String vid = entry.getKey();
                        Map<String, Map<String, String>> map = entry.getValue();
                        if (null != map && map.containsKey("alg_vid_feature_basic_info")) {
                            Map<String, String> info = map.get("alg_vid_feature_basic_info");
                            if (null != info && !info.isEmpty()) {
                                behaviorVideos.put(vid, info);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("get user behavior video info error! value=[{}]", headVid, e);
        }
        return behaviorVideos;
    }

    public Map<String, String> getHeadVideoInfo(String headVid) {
        try {
            if (null != headVid && !headVid.isEmpty()) {
                Map<String, Map<String, Map<String, String>>> videoBaseInfoMap = getVideoBaseInfo(headVid, new ArrayList<>());
                return videoBaseInfoMap.getOrDefault(headVid, new HashMap<>()).get("alg_vid_feature_basic_info");
            }
        } catch (Exception e) {
            log.error("get head info error! value=[{}]", headVid, e);
        }
        return null;
    }

    public Map<String, String> getUserSocialRecallInfo(String mid) {
        if (StringUtils.isEmpty(mid)) {
            return new HashMap<>();
        }


        List<FeatureKeyProto> protos = new ArrayList<>();
        protos.add(genWithMid("alg_recsys_user_social_recall_feature_day", mid));
        Feature feature = getFeatureByProto(protos);
        return feature.getUserFeature().getOrDefault("alg_recsys_user_social_recall_feature_day", new HashMap<>());
    }

    private Feature getFeatureByProto(List<FeatureKeyProto> protos) {
        Map<String, String> result = remoteService.getFeature(protos);
        Feature feature = new Feature();
        result.forEach((key, value) -> {
            try {
                String[] uk = StringUtils.split(key, ":");
                String prefix = uk[0];
                String table = uk[1];
                Map<String, String> colMap = JSONUtils.fromJson(value, new TypeToken<Map<String, String>>() {
                }, Collections.emptyMap());

                String featureStr = colMap.get("feature");
                switch (prefix) {
                    case "v":
                        String vid = uk[2];
                        Map<String, Map<String, String>> tableFeatureMap = feature.getVideoFeature().getOrDefault(vid, new HashMap<>());
                        tableFeatureMap.put(table, JSONUtils.fromJson(featureStr, new TypeToken<Map<String, String>>() {
                        }, Collections.emptyMap()));
                        feature.getVideoFeature().put(vid, tableFeatureMap);
                        break;
                    case "u":
                        feature.getUserFeature().put(table, JSONUtils.fromJson(featureStr, new TypeToken<Map<String, String>>() {
                        }, Collections.emptyMap()));
                        break;
                    default:
                        break;
                }
            } catch (Exception e) {
                log.error("getFeatureByProto error key {}", key, e);
            }
        });
        return feature;
    }

    private String getMergeMid(String uid, String mid) {
        if (null != uid && !uid.isEmpty() && !uid.equals("null")) {
            return uid;
        } else {
            return mid;
        }
    }

    private final String videoUkFormat = "v:%s:%s";
    private final String userUkFormat = "u:%s";
    private final String kvUkFormat = "v:%s:%s";

    private FeatureKeyProto genWithVid(String table, String vid) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(videoUkFormat, table, vid))
                .setTableName(table)
                .putFieldValue("vid", vid)
                .build();
    }

    private FeatureKeyProto genWithVidAndAppType(String table, String vid, String appType) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(videoUkFormat, table, vid))
                .setTableName(table)
                .putFieldValue("vid", vid)
                .putFieldValue("apptype", appType)
                .build();
    }

    private FeatureKeyProto genWithVidAndProvince(String table, String vid, String province) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(videoUkFormat, table, vid))
                .setTableName(table)
                .putFieldValue("vid", vid)
                .putFieldValue("province", province)
                .build();
    }

    private FeatureKeyProto genWithVidAndHeadVid(String table, String vid, String headVid) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(videoUkFormat, table, vid))
                .setTableName(table)
                .putFieldValue("vid", vid)
                .putFieldValue("headVid", headVid)
                .build();
    }

    private FeatureKeyProto genWithMid(String table, String mid) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(userUkFormat, table))
                .setTableName(table)
                .putFieldValue("mid", mid)
                .build();
    }

    private FeatureKeyProto genWithUnionId(String table, String unionId) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(userUkFormat, table))
                .setTableName(table)
                .putFieldValue("union_id", unionId)
                .build();
    }

    private FeatureKeyProto genWithKeyMap(String table, String uniqKey, Map<String, String> map) {
        FeatureKeyProto.Builder builder = FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(kvUkFormat, table, uniqKey))
                .setTableName(table);
        for (Map.Entry<String, String> entry : map.entrySet()) {
            builder.putFieldValue(entry.getKey(), entry.getValue());
        }
        return builder.build();
    }

    @Data
    public static class Feature {
        // k1:视频、k2:表、k3:特征、v:特征值
        private Map<String, Map<String, Map<String, String>>> videoFeature = new HashMap<>();

        // k1:mid、k2:特征、v:特征值
        private Map<String, Map<String, String>> userFeature = new HashMap<>();

    }

}
