package com.tzld.piaoquan.ad.engine.service.feature;

import com.google.common.reflect.TypeToken;
import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
import com.tzld.piaoquan.ad.engine.commons.util.JSONUtils;
import com.tzld.piaoquan.ad.engine.service.predict.v2.PredictContext;
import com.tzld.piaoquan.ad.engine.service.remote.FeatureV2RemoteService;
import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRequestContext;
import com.tzld.piaoquan.recommend.feature.model.feature.FeatureKeyProto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

@Slf4j
@Component
public class FeatureService {

    @Autowired
    private FeatureV2RemoteService remoteService;

    private static final String cidUkFormat = "c:%s:%s";
    private static final String cidUkDoubleFieldFormat = "c:%s:%s:%s";
    private static final String avUkFormat = "av:%s:%s";
    private static final String vidUkFormat = "v:%s:%s";
    private static final String midUkFormat = "m:%s:%s";

    public Feature getFeature(Collection<String> cidList, Collection<String> adVerIdList, ScoreParam param) {
        AdRequestContext context = param.getRequestContext();

        List<FeatureKeyProto> protos = new ArrayList<>();
        for (String cidStr : cidList) {

            // cid
            protos.add(genWithCid("alg_cid_feature_basic_info", cidStr));
            protos.add(genWithCid("alg_cid_feature_cid_action", cidStr));

            // cid + region
            protos.add(genWithCidAndRegion("alg_cid_feature_region_action", cidStr, context.getRegion()));

            // cid + apptype
            protos.add(genWithCidAndAppType("alg_cid_feature_app_action", cidStr, context.getApptype()));

            // cid + week
            protos.add(genWithCidAndWeek("alg_cid_feature_week_action", cidStr, context.getWeek()));

            // cid + hour
            protos.add(genWithCidAndHour("alg_cid_feature_hour_action", cidStr, context.getHour()));

            // cid + brand
            if (StringUtils.isNotEmpty(context.getMachineinfoBrand())) {
                protos.add(genWithCidAndBrand("alg_cid_feature_brand_action", cidStr, context.getMachineinfoBrand()));
            }

            // cid + wechatversion
            protos.add(genWithCidAndWechatVersion("alg_cid_feature_weChatVersion_action", cidStr, context.getMachineinfoWechatversion()));

            // cid + vid
            if (Objects.nonNull(param.getVideoId())) {
                protos.add(genWithCidAndVid("alg_cid_feature_vid_cf", cidStr, param.getVideoId().toString()));
            }
        }

        for (String adVerId : adVerIdList) {
            // adverid
            protos.add(genWithAdVerId("alg_cid_feature_adver_action", adVerId));
        }

        // vid
        if (Objects.nonNull(param.getVideoId())) {
            protos.add(genWithVid("alg_cid_feature_vid_cf_rank", param.getVideoId().toString()));
            protos.add(genWithVid("alg_vid_feature_basic_info", param.getVideoId().toString()));
        }

        // mid
        if (StringUtils.isNotEmpty(param.getMid())) {
            protos.add(genWithMid("alg_mid_feature_ad_action", param.getMid()));
            protos.add(genWithMid("alg_mid_feature_return_tags", param.getMid()));
            protos.add(genWithMid("alg_mid_feature_share_tags", param.getMid()));
        }
        return this.invokeFeatureService(protos);
    }

    public Feature getPredictFeature(PredictContext context) {
        List<FeatureKeyProto> protos = new ArrayList<>();
        protos.add(genWithMidAndAppType("alg_ad_crowd_choose_feature_v2", context.getMid(), context.getAppType()));
        return this.invokeFeatureService(protos);
    }

    public Feature invokeFeatureService(List<FeatureKeyProto> protos) {

        Map<String, String> featureMap = remoteService.getFeature(protos);
        featureMap = this.featureStrCover(featureMap);

        Feature feature = new Feature();

        for (Map.Entry<String, String> entry : featureMap.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            String[] split = key.split(":");
            String prefix = split[0];
            String tableName = split[1];
            String id = split[2];
            switch (prefix) {
                case "c":
                    Map<String, Map<String, String>> tableFeatureMap = feature.getCidFeature().getOrDefault(id, new HashMap<>());
                    tableFeatureMap.put(tableName, JSONUtils.fromJson(value, new TypeToken<Map<String, String>>() {
                    }, Collections.emptyMap()));
                    feature.getCidFeature().put(id, tableFeatureMap);
                    break;
                case "av":
                    Map<String, Map<String, String>> avFeatureMap = feature.getAdVerFeature().getOrDefault(id, new HashMap<>());
                    avFeatureMap.put(tableName, JSONUtils.fromJson(value, new TypeToken<Map<String, String>>() {
                    }, Collections.emptyMap()));
                    feature.getAdVerFeature().put(id, avFeatureMap);
                    break;
                case "m":
                    feature.getUserFeature().put(tableName, JSONUtils.fromJson(value, new TypeToken<Map<String, String>>() {
                    }, Collections.emptyMap()));
                    break;
                case "v":
                    feature.getVideoFeature().put(tableName, JSONUtils.fromJson(value, new TypeToken<Map<String, String>>() {
                    }, Collections.emptyMap()));
                    break;
            }
        }

        return feature;
    }

    private FeatureKeyProto genWithCid(String table, String cid) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(cidUkFormat, table, cid))
                .setTableName(table)
                .putFieldValue("cid", cid)
                .build();
    }

    private FeatureKeyProto genWithAdVerId(String table, String adVerId) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(avUkFormat, table, adVerId))
                .setTableName(table)
                .putFieldValue("adverid", adVerId)
                .build();
    }

    private FeatureKeyProto genWithCidAndRegion(String table, String cid, String region) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(cidUkDoubleFieldFormat, table, cid, region))
                .setTableName(table)
                .putFieldValue("cid", cid)
                .putFieldValue("region", region)
                .build();
    }

    private FeatureKeyProto genWithCidAndAppType(String table, String cid, String appType) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(cidUkDoubleFieldFormat, table, cid, appType))
                .setTableName(table)
                .putFieldValue("cid", cid)
                .putFieldValue("apptype", appType)
                .build();
    }

    private FeatureKeyProto genWithCidAndWeek(String table, String cid, String week) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(cidUkDoubleFieldFormat, table, cid, week))
                .setTableName(table)
                .putFieldValue("cid", cid)
                .putFieldValue("week", week)
                .build();
    }

    private FeatureKeyProto genWithCidAndHour(String table, String cid, String hour) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(cidUkDoubleFieldFormat, table, cid, hour))
                .setTableName(table)
                .putFieldValue("cid", cid)
                .putFieldValue("hour", hour)
                .build();
    }

    private FeatureKeyProto genWithCidAndBrand(String table, String cid, String brand) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(cidUkDoubleFieldFormat, table, cid, brand))
                .setTableName(table)
                .putFieldValue("cid", cid)
                .putFieldValue("brand", brand)
                .build();
    }

    private FeatureKeyProto genWithCidAndWechatVersion(String table, String cid, String wechatVersion) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(cidUkDoubleFieldFormat, table, cid, wechatVersion))
                .setTableName(table)
                .putFieldValue("cid", cid)
                .putFieldValue("wechatversion", wechatVersion)
                .build();
    }

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

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

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

    private FeatureKeyProto genWithMidAndAppType(String table, String mid, String appType) {
        return FeatureKeyProto.newBuilder()
                .setUniqueKey(String.format(midUkFormat, table, mid))
                .setTableName(table)
                .putFieldValue("mid", mid)
                .putFieldValue("apptype", appType)
                .build();
    }

    private Map<String, String> featureStrCover(Map<String, String> metaFeatureMap) {
        Map<String, String> newFeatureMap = new HashMap<>();
        for (Map.Entry<String, String> entry : metaFeatureMap.entrySet()) {
            String key = entry.getKey();
            try {
                Map<String, String> valueMap = JSONUtils.fromJson(entry.getValue(), new TypeToken<Map<String, String>>() {
                }, Collections.emptyMap());
                String value = valueMap.getOrDefault("feature", "{}");
                newFeatureMap.put(key, value);
            } catch (Exception e) {
                log.error("featureStrCover error: ", e);
            }
        }
        return newFeatureMap;
    }
}
