package com.lingniu.ingest.protocol.jt808.session;

import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * JT808 终端 Channel 注册表。按 {@code phone} 索引，一个 phone 对应一个活跃 Channel。
 *
 * <p>{@link Jt808CommandDispatcher} 通过本类查 Channel；{@code Jt808ChannelHandler} 在
 * 连接建立 / 断开 / 鉴权完成时维护映射。
 *
 * <p>线程安全：{@link ConcurrentHashMap}。
 */
public final class Jt808ChannelRegistry {

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

    private final ConcurrentMap<String, Channel> byPhone = new ConcurrentHashMap<>();
    private final ConcurrentMap<Channel, String> reverse = new ConcurrentHashMap<>();
    private final ConcurrentMap<String, AtomicInteger> serials = new ConcurrentHashMap<>();

    public void bind(String phone, Channel channel) {
        if (phone == null || phone.isBlank() || channel == null) return;
        Channel old = byPhone.put(phone, channel);
        reverse.put(channel, phone);
        if (old != null && old != channel) {
            reverse.remove(old);
            // 同一 phone 新连接上线时关闭旧 channel，避免下行命令写到已被终端替换的连接。
            old.close();
            log.info("channel rebound phone={} oldChannel={} newChannel={}", phone, old.id(), channel.id());
        }
    }

    public Optional<Channel> find(String phone) {
        if (phone == null) return Optional.empty();
        Channel c = byPhone.get(phone);
        if (c == null || !c.isActive()) {
            if (c != null) unbind(c);
            return Optional.empty();
        }
        return Optional.of(c);
    }

    public Optional<String> unbind(Channel channel) {
        String phone = reverse.remove(channel);
        if (phone != null) {
            byPhone.remove(phone, channel);
        }
        return Optional.ofNullable(phone);
    }

    public int nextSerial(String phone) {
        // JT808 流水号 16 bit，无符号回绕到 0；pending 匹配也使用这个序号。
        return serials.computeIfAbsent(phone, k -> new AtomicInteger())
                .updateAndGet(i -> (i + 1) & 0xFFFF);
    }

    public int size() {
        return byPhone.size();
    }
}
