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

import com.alibaba.fastjson.JSON;
import com.aliyun.openservices.eas.predict.request.TFDataType;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * FG 编码器：按 fg_config.json 定义的特征顺序，将 FeatureV6 的输出编码为 EasyRec FG 格式字符串。
 * <p>
 * 使用方式:
 * 1. 初始化时加载 fg_config.json（从 classpath 或文件路径）
 * 2. 调用 encode() 传入 FeatureV6 计算好的特征 Map + 上下文信息
 * 3. 返回 chr(2) 分隔的 features 字符串，可直接发给 EAS bypass 模式
 * <p>
 * 特征映射规则（fg_config name → FeatureV6 key）:
 * - 统计特征: b0_12h_exp → b0_12h@exp（下划线→@）
 * - log压缩:  b0_12h_ros_l → b0_12h@ros_#（_l → _#）
 * - ID特征:   ctx_app → 直接从 stringFeatures 取值
 * - need_prefix=true 的 ID 特征: 输出 "name_value" 格式
 */
public class FGEncoder {

    private static final Logger log = LoggerFactory.getLogger(FGEncoder.class);

    private final List<FeatureDef> featureDefs;

    // ── 特征定义 ─────────────────────────────────

    public static class FeatureDef {
        public String feature_name;
        public String feature_key;
        public String value_type;
        public String default_value;

        public TFDataType typeCoverToTFDataType() {
            if ("Double".equalsIgnoreCase(value_type)) {
                return TFDataType.DT_DOUBLE;
            } else if ("Long".equalsIgnoreCase(value_type)) {
                return TFDataType.DT_INT64;
            } else {
                return TFDataType.DT_STRING;
            }
        }
    }

    // ── 初始化 ─────────────────────────────────

    /**
     * 从 classpath 加载 fg_config.json
     */
    public FGEncoder(String fgConfigJsonPath) {
        try (InputStream is = getClass().getClassLoader().getResourceAsStream(fgConfigJsonPath)) {
            if (is == null) {
                throw new RuntimeException("fg_config not found: " + fgConfigJsonPath);
            }

            this.featureDefs = this.loadConfig(is);
        } catch (Exception e) {
            throw new RuntimeException("加载 fg_config 失败: " + fgConfigJsonPath, e);
        }
    }

    public FGEncoder(InputStream inputStream) {
        this.featureDefs = loadConfig(inputStream);
    }

    private List<FeatureDef> loadConfig(InputStream inputStream) {
        Gson gson = new Gson();
        InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);

        // 格式: {"features": [...], "reserves": [...]}
        Map<String, Object> config = gson.fromJson(reader, new TypeToken<Map<String, Object>>() {
        }.getType());
        String featuresJson = gson.toJson(config.get("features"));
        List<FeatureDef> defs = gson.fromJson(featuresJson, new TypeToken<List<FeatureDef>>() {
        }.getType());

        log.info("FeatureFGEncoder 初始化: {} 个特征", defs.size());
        return defs;
    }


    // ── fg_name → FeatureV6 key 映射 ─────────────

    /**
     * 将 fg_config 的 feature_name 转换为 FeatureV6 Map 里的 key。
     * <p>
     * 规则:
     * b0_12h_exp       → b0_12h@exp        (prefix_period_metric → prefix_period@metric)
     * b0_12h_ros_l     → b0_12h@ros_#      (_l 后缀 → _#)
     * b0_12h_ros_n_l   → b0_12h@ros_n_#
     * c9_s_pv          → c9@s_pv
     * ctx_app           → null (不走 Map，从 stringFeatures 取)
     * h_total_time      → null (不走 Map，从 stringFeatures 取)
     */
    public String fgNameToV6Key(String fgName) {
        // 统计特征前缀列表
        Set<String> statPrefixes = new HashSet<>(Arrays.asList(
                "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9",
                "b10", "b11", "b13", "b14", "b15", "c1"
        ));

        // c9 前缀
        if (fgName.startsWith("c9_")) {
            return "c9@" + fgName.substring(3);
        }

        // 统计特征: 找 prefix_period_metric 模式
        for (String prefix : statPrefixes) {
            if (!fgName.startsWith(prefix + "_")) continue;
            String rest = fgName.substring(prefix.length() + 1);  // "12h_exp" or "12h_ros_l"

            // 找 period（第一个 _ 之前的部分，如 "12h", "1h", "168h"）
            int periodEnd = rest.indexOf('_');
            if (periodEnd < 0) continue;
            String period = rest.substring(0, periodEnd);
            String metric = rest.substring(periodEnd + 1);  // "exp", "ros_l", "ros_minus_l"

            // _l 后缀 → _#
            if (metric.endsWith("_l")) {
                metric = metric.substring(0, metric.length() - 2) + "_#";
            }

            return prefix + "_" + period + "@" + metric;
        }

        // d1/d2/d3 CF 特征
        if (fgName.startsWith("d1_") || fgName.startsWith("d2_") || fgName.startsWith("d3_")) {
            String prefix = fgName.substring(0, 2);
            String metric = fgName.substring(3);
            return prefix + "@" + metric;
        }

        // 视频属性特征: h_total_time → h@total_time, r_bit_rate → r@bit_rate
        if (fgName.startsWith("h_") || fgName.startsWith("r_")) {
            return fgName.substring(0, 1) + "@" + fgName.substring(2);
        }

        // 上下文数值特征: ctx_hour_val → hour
        if ("ctx_hour_val".equals(fgName)) {
            return "hour";
        }

        // e1 创意特征: e1_xxx → e1@xxx
        if (fgName.startsWith("e1_")) {
            return "e1@" + fgName.substring(3);
        }

        // 其他 → 不走 numeric Map，从 stringFeatures 取
        return null;
    }

    // ── 辅助方法 ─────────────────────────────────

    /**
     * 获取特征数量
     */
    public int getFeatureCount() {
        return featureDefs.size();
    }

    /**
     * 获取特征名列表（用于调试/对齐）
     */
    public List<String> getFeatureNames() {
        List<String> names = new ArrayList<>(featureDefs.size());
        for (FeatureDef def : featureDefs) {
            names.add(def.feature_name);
        }
        return names;
    }

    public List<FeatureDef> getFeatureDefs() {
        return featureDefs;
    }
}
