package com.lingniu.ingest.protocol.gb32960.inbound;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.lingniu.ingest.protocol.gb32960.model.Gb32960Message;
import com.lingniu.ingest.protocol.gb32960.model.InfoBlock;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Channel-scoped diagnostics for high-frequency GB/T 32960 reports.
 *
 * <p>每个 TCP channel 只记录有限个首次出现的 (vin, rawTypeSignature)，用于发现厂商 profile
 * 没命中、字段解析退化为 Raw 的问题。它不参与业务判断，也不会影响 raw archive 和历史查询。
 */
public final class Gb32960FrameDiagnostics {

    private static final AttributeKey<Set<String>> LOGGED_FRAME_KEYS_ATTR =
            AttributeKey.valueOf("gb32960.loggedFrameKeys");

    private static final ObjectMapper DEBUG_JSON = new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    private final int maxKeysPerChannel;

    public Gb32960FrameDiagnostics() {
        this(128);
    }

    public Gb32960FrameDiagnostics(int maxKeysPerChannel) {
        this.maxKeysPerChannel = Math.max(1, maxKeysPerChannel);
    }

    public boolean markFirstSeen(Channel channel, String vin, List<String> rawTypes) {
        String signature = rawTypes == null || rawTypes.isEmpty() ? "NORMAL" : "RAW:" + rawTypes;
        return markFirstSeenKey(channel, vin + "|" + signature);
    }

    public int seenKeyCount(Channel channel) {
        Set<String> seen = channel.attr(LOGGED_FRAME_KEYS_ATTR).get();
        return seen == null ? 0 : seen.size();
    }

    public static List<String> collectRawTypeHex(List<InfoBlock> blocks) {
        List<String> raws = new ArrayList<>(0);
        for (InfoBlock b : blocks) {
            if (b instanceof InfoBlock.Raw r) {
                raws.add(String.format("0x%02x", r.typeCode()));
            }
        }
        return raws;
    }

    public static String toJson(Gb32960Message msg) {
        try {
            return DEBUG_JSON.writeValueAsString(msg);
        } catch (Exception e) {
            return "<json-error:" + e.getMessage() + ">";
        }
    }

    private boolean markFirstSeenKey(Channel channel, String key) {
        Set<String> seen = channel.attr(LOGGED_FRAME_KEYS_ATTR).get();
        if (seen == null) {
            seen = new LinkedHashSet<>();
            channel.attr(LOGGED_FRAME_KEYS_ATTR).set(seen);
        }
        boolean added = seen.add(key);
        if (added) trimToLimit(seen);
        return added;
    }

    private void trimToLimit(Set<String> seen) {
        while (seen.size() > maxKeysPerChannel) {
            // LinkedHashSet 保持插入顺序，超限时淘汰最早的签名，避免异常设备导致 attribute 无界增长。
            String first = seen.iterator().next();
            seen.remove(first);
        }
    }
}
