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

import com.tzld.piaoquan.recommend.server.service.rank.extractor.ExtractorUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class FeatureBucketUtils {
    private static final Map<String, Pair<Map<String, Double>, Map<String, double[]>>> featureBucketMap = new HashMap<>();

    public static void init(List<String> bucketFileList) {
        if (null != bucketFileList) {
            for (String bucketFile : bucketFileList) {
                Pair<Map<String, Double>, Map<String, double[]>> pair = loadBucket(bucketFile);
                if (MapUtils.isNotEmpty(pair.getLeft()) && MapUtils.isNotEmpty(pair.getRight())) {
                    featureBucketMap.put(bucketFile, pair);
                    log.info("load {} bucketFile, {} bucketsLen, {} bucketsMap", bucketFile, pair.getLeft().size(), pair.getRight().size());
                }
            }
        }
        log.info("load {} bucketFile", featureBucketMap.size());
    }

    public static Map<String, Float> noBucketFeature(Map<String, Double> srcFeature) {
        if (MapUtils.isEmpty(srcFeature)) {
            return new HashMap<>();
        }
        Map<String, Float> featureMap = new HashMap<>(srcFeature.size());
        for (Map.Entry<String, Double> entry : srcFeature.entrySet()) {
            String name = entry.getKey();
            double score = entry.getValue();
            if (score > 1E-8) {
                featureMap.put(name, (float) score);
            }
        }
        return featureMap;
    }

    public static Map<String, String> bucketFeature(String bucketFile, Map<String, Double> srcFeature) {
        if (MapUtils.isEmpty(srcFeature)) {
            return new HashMap<>();
        }
        Map<String, String> featureMap = new HashMap<>(srcFeature.size());
        Pair<Map<String, Double>, Map<String, double[]>> pair = featureBucketMap.get(bucketFile);
        if (null != pair && MapUtils.isNotEmpty(pair.getLeft()) && MapUtils.isNotEmpty(pair.getRight())) {
            Map<String, Double> bucketsLen = pair.getLeft();
            Map<String, double[]> bucketsMap = pair.getRight();
            for (Map.Entry<String, Double> entry : srcFeature.entrySet()) {
                String name = entry.getKey();
                Double score = entry.getValue();
                // 注意：0值、不在分桶文件中的特征，会被过滤掉。
                if (score > 1E-8 && bucketsLen.containsKey(name) && bucketsMap.containsKey(name)) {
                    Double bucketNum = bucketsLen.get(name);
                    double[] buckets = bucketsMap.get(name);
                    Double scoreNew = 1.0 / bucketNum * (ExtractorUtils.findInsertPosition(buckets, score) + 1.0);
                    featureMap.put(name, String.valueOf(scoreNew));
                }
            }
        } else {
            log.error("failed to get {} bucket feature", bucketFile);
        }
        return featureMap;
    }

    public static Map<String, Float> bucketFeatureV2(String bucketFile, Map<String, Double> srcFeature) {
        if (MapUtils.isEmpty(srcFeature)) {
            return new HashMap<>();
        }
        Map<String, Float> featureMap = new HashMap<>(srcFeature.size());
        Pair<Map<String, Double>, Map<String, double[]>> pair = featureBucketMap.get(bucketFile);
        if (null != pair && MapUtils.isNotEmpty(pair.getLeft()) && MapUtils.isNotEmpty(pair.getRight())) {
            Map<String, Double> bucketsLen = pair.getLeft();
            Map<String, double[]> bucketsMap = pair.getRight();
            for (Map.Entry<String, Double> entry : srcFeature.entrySet()) {
                String name = entry.getKey();
                double score = entry.getValue();
                // 注意：0值、不在分桶文件中的特征，会被过滤掉。
                if (score > 1E-8 && bucketsLen.containsKey(name) && bucketsMap.containsKey(name)) {
                    Double bucketNum = bucketsLen.get(name);
                    double[] buckets = bucketsMap.get(name);
                    double scoreNew = 1.0 / bucketNum * (ExtractorUtils.findInsertPosition(buckets, score) + 1.0);
                    featureMap.put(name, (float) scoreNew);
                } else if (score > 1E-8) {
                    featureMap.put(name, (float) score);
                }
            }
        } else {
            log.error("failed to get {} bucket feature", bucketFile);
        }
        return featureMap;
    }

    private static Pair<Map<String, Double>, Map<String, double[]>> loadBucket(String bucketFile) {
        Map<String, Double> bucketsLen = new HashMap<>();
        Map<String, double[]> bucketsMap = new HashMap<>();
        InputStream resourceStream = FeatureBucketUtils.class.getClassLoader().getResourceAsStream(bucketFile);
        if (null != resourceStream) {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    // 替换空格和换行符，过滤空行
                    line = line.replace(" ", "").replaceAll("\n", "");
                    if (!line.isEmpty()) {
                        String[] rList = line.split("\t");
                        if (rList.length == 3) {
                            String key = rList[0];
                            double value1 = Double.parseDouble(rList[1]);
                            bucketsLen.put(key, value1);
                            double[] value2 = Arrays.stream(rList[2].split(","))
                                    .mapToDouble(Double::valueOf)
                                    .toArray();
                            bucketsMap.put(key, value2);
                        }
                    }
                }
            } catch (IOException e) {
                log.error("something is wrong in parse feature bucket file", e);
            }
        } else {
            log.error("bucket file: {} is not exist", bucketFile);
        }
        return new MutablePair<>(bucketsLen, bucketsMap);
    }
}
