package com.cmvr.edge.client.manage;

import io.grpc.ManagedChannel;
import lombok.Getter;

import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

/**
 * gRPC 通道缓存
 */
public class GrpcChannelCache {

    private final long ttlSeconds; // 通道存活时间（秒）
    private final Map<String, ChannelWrapper> channelMap = new ConcurrentHashMap<>(); // 设备 ID -> 通道包装器

    public GrpcChannelCache(long ttlSeconds) {
        this.ttlSeconds = ttlSeconds;
    }

    /**
     * 获取通道：存在则刷新时间，过期则重建，不存在则创建
     */
    public ManagedChannel get(String terminalId, Function<String, ManagedChannel> supplier) {
        return channelMap.compute(terminalId, (k, v) -> {
            if (v == null || v.isExpired(ttlSeconds)) { // 为空或过期时重建
                if (v != null) v.close(); // 关闭旧通道
                return new ChannelWrapper(supplier.apply(terminalId)); // 创建新通道包装器
            } else {
                v.refresh(); // 更新最后访问时间
                return v;
            }
        }).getChannel();
    }

    /**
     * 清理所有过期通道
     */
    public void cleanExpiredChannels() {
        channelMap.entrySet().removeIf(entry -> {
            ChannelWrapper wrapper = entry.getValue();
            if (wrapper.isExpired(ttlSeconds)) { // 检查过期
                wrapper.close(); // 关闭通道
                return true;
            }
            return false;
        });
    }

    /**
     * 通道包装器（管理通道生命周期和访问时间）
     */
    private static class ChannelWrapper {
        @Getter private final ManagedChannel channel; // gRPC 通道
        private volatile Instant lastAccess; // 最后访问时间

        public ChannelWrapper(ManagedChannel channel) {
            this.channel = channel;
            this.lastAccess = Instant.now(); // 初始化访问时间
        }

        public void refresh() {
            this.lastAccess = Instant.now(); // 更新访问时间
        }

        public boolean isExpired(long ttlSeconds) {
            // 当前时间 - TTL 秒 > 最后访问时间则过期
            return Instant.now().minusSeconds(ttlSeconds).isAfter(lastAccess);
        }

        public void close() {
            try {
                channel.shutdownNow(); // 立即关闭通道
            } catch (Exception ignored) {
            }
        }
    }
}