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

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.remote.FeatureV2RemoteService;
import com.tzld.piaoquan.recommend.server.util.JSONUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
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"));

    /**
     * @return k1:视频、k2:表、k3:特征、v:特征值
     */
    public Feature getFeature(String mid, List<String> vidList, String appType,
                              String province, String headVid) {


        List<FeatureKeyProto> protos = new ArrayList<>();

        for (String vid : vidList) {
            // TODO 补充其他特征
            // vid
            // protos.add(genWithVid("alg_vid_feature_all_exp", 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", 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", vid));
            protos.add(genWithVid("alg_vid_feature_feed_noflow_exp_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_noflow_root_share", vid));
            protos.add(genWithVid("alg_vid_feature_feed_noflow_root_share_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_noflow_root_return", vid));
            protos.add(genWithVid("alg_vid_feature_feed_noflow_root_return_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_flow_exp", vid));
            protos.add(genWithVid("alg_vid_feature_feed_flow_exp_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_flow_root_share", vid));
            protos.add(genWithVid("alg_vid_feature_feed_flow_root_share_v2", vid));
            // protos.add(genWithVid("alg_vid_feature_feed_flow_root_return", vid));
            protos.add(genWithVid("alg_vid_feature_feed_flow_root_return_v2", vid));

            protos.add(genWithVid("alg_vid_feature_basic_info", vid));
            // vid + apptype

            // vid + province
            // protos.add(genWithVidAndProvince("alg_vid_feature_feed_province_exp", vid, province));
            protos.add(genWithVidAndProvince("alg_vid_feature_feed_province_exp_v2", vid, province));
            // protos.add(genWithVidAndProvince("alg_vid_feature_feed_province_root_share", 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", vid, province));
            protos.add(genWithVidAndProvince("alg_vid_feature_feed_province_root_return_v2", vid, province));

            // vid + headvid
            protos.add(genWithVidAndHeadVid("alg_recsys_feature_cf_i2i_new", vid, headVid));
            protos.add(genWithVidAndHeadVid("alg_recsys_feature_cf_i2i_new_v2", vid, headVid));
        }


        // 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", mid));
        protos.add(genWithMid("alg_mid_feature_feed_exp_share_tags_v2", mid));
        // protos.add(genWithMid("alg_mid_feature_feed_exp_return_tags", 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));


        Map<String, String> result = remoteService.getFeature(protos);

        Feature feature = new Feature();

        result.entrySet().forEach(e -> {

            String[] uk = StringUtils.split(e.getKey(), ":");
            String prefix = uk[0];
            String table = uk[1];
            Map<String, String> colMap = JSONUtils.fromJson(e.getValue(), 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;
            }

        });


        return feature;
    }


    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 Feature getNewFeature(String province, String mid, String sceneType, String headVid,
                                 Map<String, Map<String, Map<String, String>>> videoBaseInfoMap, List<String> vidList) {
        List<FeatureKeyProto> protos = new ArrayList<>();
        String i2iSceneType = "other";
        if (hotSceneSet.contains(sceneType)) {
            i2iSceneType = sceneType;
        }
        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));

            // headvid + vid
            //protos.add(genWithKeyMap("alg_recsys_feature_cf_i2i_new_v2", vid, ImmutableMap.of("vid_a", headVid, "vid_b", vid)));

            // ********************* new vid ******************
            protos.add(genWithKeyMap("alg_vid_feature_day", vid, ImmutableMap.of("vid", vid)));
            protos.add(genWithKeyMap("alg_sence_type_feature", vid, ImmutableMap.of("sence_type", sceneType, "videoid", vid)));
            //protos.add(genWithKeyMap("alg_recsys_feature_cf_i2i_scene_rov", vid, ImmutableMap.of("sence_type", i2iSceneType, "vid_a", headVid, "vid_b", vid)));
            //protos.add(genWithKeyMap("alg_recsys_feature_cf_i2i_scene_ros", vid, ImmutableMap.of("sence_type", i2iSceneType, "vid_a", headVid, "vid_b", vid)));
            //protos.add(genWithKeyMap("alg_recsys_feature_weak_cf_i2i_scene_rov", vid, ImmutableMap.of("sence_type", i2iSceneType, "vid_a", headVid, "vid_b", vid)));
            //protos.add(genWithKeyMap("alg_recsys_feature_weak_cf_i2i_scene_ros", vid, ImmutableMap.of("sence_type", i2iSceneType, "vid_a", headVid, "vid_b", vid)));
            if (null != videoBaseInfoMap && videoBaseInfoMap.containsKey(vid)) {
                Map<String, Map<String, String>> videoInfo = videoBaseInfoMap.get(vid);
                if (null != videoInfo && videoInfo.containsKey("alg_vid_feature_basic_info")) {
                    Map<String, String> baseInfo = videoInfo.get("alg_vid_feature_basic_info");
                    if (null != baseInfo) {
                        String cate1 = baseInfo.get("cate1_list");
                        if (null != cate1 && !cate1.isEmpty()) {
                            protos.add(genWithKeyMap("alg_cate1_feature", vid, ImmutableMap.of("cate1", cate1)));
                            protos.add(genWithKeyMap("alg_cate1_feature_day", vid, ImmutableMap.of("cate1", cate1)));
                        }
                        String cate2 = baseInfo.get("cate2");
                        if (null != cate2 && !cate2.isEmpty()) {
                            protos.add(genWithKeyMap("alg_cate2_feature", vid, ImmutableMap.of("cate2", cate2)));
                            protos.add(genWithKeyMap("alg_cate2_feature_day", vid, ImmutableMap.of("cate2", cate2)));
                        }
                        String vidSource = baseInfo.get("vid_source");
                        if (null != vidSource && !vidSource.isEmpty()) {
                            protos.add(genWithKeyMap("alg_vid_source_feature", vid, ImmutableMap.of("vid_source", vidSource)));
                            protos.add(genWithKeyMap("alg_video_source_feature_day", vid, ImmutableMap.of("video_source", vidSource)));
                        }
                        String videoUnionid = baseInfo.get("title_time_w_h_unionid");
                        if (null != videoUnionid && !videoUnionid.isEmpty()) {
                            protos.add(genWithKeyMap("alg_video_unionid_feature_day", 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));

        return getFeatureByProto(protos);
    }

    private Feature getFeatureByProto(List<FeatureKeyProto> protos) {
        Map<String, String> result = remoteService.getFeature(protos);
        Feature feature = new Feature();
        result.entrySet().forEach(e -> {
            String[] uk = StringUtils.split(e.getKey(), ":");
            String prefix = uk[0];
            String table = uk[1];
            Map<String, String> colMap = JSONUtils.fromJson(e.getValue(), 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;
            }
        });
        return feature;
    }

    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 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<>();

    }

}
