package com.seer.teach.netty.im.manager;

import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Channel 管理器
 * <p>
 * 维护设备与 Channel 的映射关系，支持增删改查、广播等操作。
 * </p>
 *
 * @Author: Captain
 * @Date: 2025-05-20 16:50
 */
@Slf4j
@Component
public class ImChannelManager {

    /** 设备与 Channel 的映射表 */
    private static final ConcurrentHashMap<String, Channel> TV_CHANNEL_MAP = new ConcurrentHashMap<>();

    /**
     * 添加设备和通道映射
     */
    public static void add(String device, Channel channel) {
        log.info("准备绑定设备 [{}] 到 channel={}，remote={}",
                device, channel.id().asShortText(), channel.remoteAddress());

        Channel old = TV_CHANNEL_MAP.put(device, channel);
        if (old != null && old != channel) {
            log.warn("设备 [{}] 已存在旧 channel={}，将替换为新 channel={}，remote={} —— 关闭旧连接",
                    device, old.id().asShortText(), channel.id().asShortText(), channel.remoteAddress());
            old.close();
        }

        log.info("设备 [{}] 绑定成功，当前在线设备总数={}", device, TV_CHANNEL_MAP.size());
    }

    /**
     * 根据设备移除映射
     */
    public static void remove(String device) {
        log.info("准备移除设备 [{}] 的 channel 映射", device);
        Channel removed = TV_CHANNEL_MAP.remove(device);
        if (removed != null) {
            log.info("设备 [{}] 已移除 channel={}，remote={}，当前在线设备总数={}",
                    device, removed.id().asShortText(), removed.remoteAddress(), TV_CHANNEL_MAP.size());
        } else {
            log.warn("尝试移除设备 [{}]，但未找到对应 channel", device);
        }
    }

    /**
     * 根据设备获取 Channel
     */
    public static Channel get(String device) {
        return TV_CHANNEL_MAP.get(device);
    }

    /**
     * 根据 Channel 移除映射
     */
    public static void removeByChannel(Channel channel) {
        log.info("准备根据 channel={} 移除映射，remote={}", channel.id().asShortText(), channel.remoteAddress());
        boolean removed = TV_CHANNEL_MAP.entrySet().removeIf(entry -> entry.getValue().equals(channel));
        if (removed) {
            log.info("已根据 channel={} 移除映射，当前在线设备总数={}", channel.id().asShortText(), TV_CHANNEL_MAP.size());
        } else {
            log.warn("尝试根据 channel={} 移除映射，但未找到对应 device", channel.id().asShortText());
        }
    }

    /**
     * 广播消息
     */
    public static void broadcast(Object msg) {
        log.info("开始广播消息，目标设备数={}", TV_CHANNEL_MAP.size());
        for (Map.Entry<String, Channel> entry : TV_CHANNEL_MAP.entrySet()) {
            String device = entry.getKey();
            Channel ch = entry.getValue();
            if (ch.isActive()) {
                ch.writeAndFlush(msg);
                log.debug("广播消息到设备 [{}]，channel={}", device, ch.id().asShortText());
            } else {
                log.warn("跳过广播，设备 [{}] 的 channel 不活跃，ch={}", device, ch.id().asShortText());
            }
        }
        log.info("广播完成");
    }

    /**
     * 根据 Channel 获取设备ID
     */
    public static String getDeviceByChannel(Channel channel) {
        log.debug("查询 channel={} 对应的设备", channel.id().asShortText());
        for (Map.Entry<String, Channel> entry : TV_CHANNEL_MAP.entrySet()) {
            if (entry.getValue().equals(channel)) {
                log.debug("找到 channel={} 对应的设备 [{}]", channel.id().asShortText(), entry.getKey());
                return entry.getKey();
            }
        }
        log.warn("未找到 channel={} 对应的设备", channel.id().asShortText());
        return null;
    }

    /**
     * 打印当前所有设备连接
     */
    public static void printAllChannels() {
        if (TV_CHANNEL_MAP.isEmpty()) {
            log.info("当前没有任何设备连接");
            return;
        }

        log.info("当前连接设备总数 = {}", TV_CHANNEL_MAP.size());
        for (Map.Entry<String, Channel> entry : TV_CHANNEL_MAP.entrySet()) {
            String device = entry.getKey();
            Channel ch = entry.getValue();
            log.info("设备 [{}] -> channel={}，remote={}，active={}",
                    device,
                    ch.id().asShortText(),
                    ch.remoteAddress(),
                    ch.isActive());
        }
    }
}
