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

import com.aliyun.openservices.eas.predict.http.HttpConfig;
import com.aliyun.openservices.eas.predict.http.PredictClient;
import com.aliyun.openservices.eas.predict.request.TFDataType;
import com.aliyun.openservices.eas.predict.request.TFRequest;
import com.aliyun.openservices.eas.predict.response.TFResponse;
import com.tzld.piaoquan.recommend.server.common.base.RankItem;
import com.tzld.piaoquan.recommend.server.model.Tuple4;
import com.tzld.piaoquan.recommend.server.service.rank.tansform.FGEncoder;
import com.tzld.piaoquan.recommend.server.service.rank.tansform.FGEncoderManager;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class PAIModel {

    private static final Logger LOGGER = LoggerFactory.getLogger(PAIModel.class);

    private final String fgConfigFilePath;
    private final String easToken;
    private final String easEndpoint;
    private final String modelName;

    private PredictClient predictClient;
    private List<Tuple4<String, String, String, TFDataType>> featureKeys = new ArrayList<>();

    private static volatile PAIModel MODEL;

    private PAIModel() {
        this.fgConfigFilePath = "zhaohaipeng/pai/config/fg/feature_list_20260403.json";
        this.easToken = "MmEwYzVlZGFiOTM4YWM3ZTE5ZDMzNTgzY2Q5YjZlYjVjODE5ZjIzYQ==";
        this.easEndpoint = "1894469520484605.vpc.cn-hangzhou.pai-eas.aliyuncs.com";
        this.modelName = "recsys_dnn_v1_20260327";

        this.parseFeatureKey();
        this.initPredictClient();
    }

    public static PAIModel getModelInstance() {
        if (MODEL == null) {
            synchronized (PAIModel.class) {
                if (MODEL == null) {
                    MODEL = new PAIModel();
                }
            }
        }
        return MODEL;
    }

    private void parseFeatureKey() {
        // 初始化fg key与特征生产时的key的映射
        FGEncoder fgEncoder = FGEncoderManager.getInstance().getFGEncoder(fgConfigFilePath);
        for (FGEncoder.FeatureDef featureDef : fgEncoder.getFeatureDefs()) {
            Tuple4<String, String, String, TFDataType> featureKeyTuple = new Tuple4<>();

            featureKeyTuple.setV1(featureDef.feature_key);
            featureKeyTuple.setV2(featureDef.feature_name);
            featureKeyTuple.setV3(featureDef.default_value);
            featureKeyTuple.setV4(featureDef.typeCoverToTFDataType());
            featureKeys.add(featureKeyTuple);
        }

    }

    private void initPredictClient() {
        predictClient = new PredictClient(new HttpConfig());
        predictClient.setModelName(this.modelName);
        predictClient.setToken(this.easToken);
        predictClient.setEndpoint(this.easEndpoint);
    }

    public Map<String, List<Float>> score(List<RankItem> rankItems) {

        List<String> scoreKeys = Arrays.asList("y_return_n_uv", "probs_is_share");
        int size = rankItems.size();
        String currentName = "";
        // 生成特征名与特征值数组的映射
        Map<String, double[]> doubleFeatureArr = new HashMap<>();
        Map<String, String[]> stringFeatureArr = new HashMap<>();

        try {

            List<Map<String, Float>> featureMapList = new ArrayList<>(rankItems.size());
            List<Map<String, String>> featureMapStringList = new ArrayList<>(rankItems.size());

            for (RankItem rankItem : rankItems) {
                featureMapList.add(Optional.ofNullable(rankItem.featureMap).orElse(new HashMap<>()));
                featureMapStringList.add(Optional.ofNullable(rankItem.featureMapString).orElse(new HashMap<>()));
            }

            for (Tuple4<String, String, String, TFDataType> featureKeyTuple : featureKeys) {
                TFDataType dataType = featureKeyTuple.getV4();
                String featureName = featureKeyTuple.getV2();
                String featureKey = featureKeyTuple.getV1();
                String defaultValue = featureKeyTuple.getV3();
                currentName = String.format("1@%s@%s", featureKey, featureName);
                if (TFDataType.DT_STRING.equals(dataType)) {
                    for (int i = 0; i < size; i++) {
                        Map<String, String> featureMapString = featureMapStringList.get(i);
                        String[] strArr = stringFeatureArr.computeIfAbsent(featureName, s -> new String[size]);
                        String value = featureMapString.get(featureKey);
                        strArr[i] = Objects.nonNull(value) ? value : defaultValue;
                    }
                } else {
                    for (int i = 0; i < size; i++) {
                        Map<String, Float> featureMap = featureMapList.get(i);
                        double[] doubleArr = doubleFeatureArr.computeIfAbsent(featureName, s -> new double[size]);
                        double value = featureMap.getOrDefault(featureKey, NumberUtils.toFloat(defaultValue, 0f));
                        doubleArr[i] = value;
                    }
                }
            }

            TFRequest request = new TFRequest();
            long[] shape = new long[]{size};
            for (Tuple4<String, String, String, TFDataType> featureKey : featureKeys) {
                String featureName = featureKey.getV2();
                TFDataType dataType = featureKey.getV4();
                currentName = String.format("2@%s@%s", featureKey.getV1(), featureName);
                if (TFDataType.DT_STRING.equals(dataType)) {
                    request.addFeed(featureName, TFDataType.DT_STRING, shape, stringFeatureArr.get(featureName));
                } else {
                    request.addFeed(featureName, TFDataType.DT_DOUBLE, shape, doubleFeatureArr.get(featureName));
                }
            }
            TFResponse response = predictClient.predict(request);
            // Map<String, PredictProtos.ArrayProto> outputsMap = response.getOutputsMap();
            // LOGGER.info("response keys: {}", outputsMap.keySet());
            Map<String, List<Float>> resultMap = new HashMap<>();
            for (String scoreKey : scoreKeys) {
                resultMap.put(scoreKey, response.getFloatVals(scoreKey));
            }
            return resultMap;
        } catch (Exception e) {
            LOGGER.error("pai eas error currentName: {}", currentName, e);
            Map<String, List<Float>> resultMap = new HashMap<>();
            for (String scoreKey : scoreKeys) {
                resultMap.put(scoreKey, Collections.nCopies(size, 0.0f));
            }
            return resultMap;
        }
    }

}
