package com.ttstd.signaling.manager;

import com.ttstd.signaling.model.AuthPrincipal;
import com.ttstd.signaling.model.DeviceInfo;
import com.ttstd.signaling.model.DeviceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 在线会话管理。
 *
 * <p>所有注册信息均来源于握手阶段已鉴权的 {@link AuthPrincipal}，
 * 客户端无法通过消息自行声明身份。
 */
@Component
public class SessionManager {

    private static final Logger logger = LoggerFactory.getLogger(SessionManager.class);

    // deviceId -> WebSocketSession
    private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
    // deviceId -> deviceType
    private final Map<String, DeviceType> deviceTypes = new ConcurrentHashMap<>();
    // session.getId() -> deviceId (用于断开连接时清理)
    private final Map<String, String> sessionToDevice = new ConcurrentHashMap<>();
    // deviceId -> 注册（上线）时间戳
    private final Map<String, Long> connectTime = new ConcurrentHashMap<>();
    // deviceId -> 已鉴权主体
    private final Map<String, AuthPrincipal> principals = new ConcurrentHashMap<>();
    // principalId -> 该主体当前占用的 deviceId 集合（一个账号可能多端登录）
    private final Map<String, Set<String>> principalDevices = new ConcurrentHashMap<>();

    /**
     * 注册已鉴权的连接。deviceId 与 deviceType 均取自 {@link AuthPrincipal}。
     */
    public void registerDevice(AuthPrincipal principal, WebSocketSession session) {
        String deviceId = principal.deviceId();
        sessions.put(deviceId, session);
        deviceTypes.put(deviceId, principal.deviceType());
        sessionToDevice.put(session.getId(), deviceId);
        connectTime.put(deviceId, System.currentTimeMillis());
        principals.put(deviceId, principal);
        principalDevices
                .computeIfAbsent(principal.principalId(), k -> ConcurrentHashMap.newKeySet())
                .add(deviceId);
        logger.info("连接已注册: deviceId={} type={} principal={}",
                deviceId, principal.deviceType(), principal.principalId());
    }

    public void unregisterSession(WebSocketSession session) {
        String deviceId = sessionToDevice.remove(session.getId());
        if (deviceId != null) {
            sessions.remove(deviceId);
            deviceTypes.remove(deviceId);
            connectTime.remove(deviceId);
            AuthPrincipal principal = principals.remove(deviceId);
            if (principal != null) {
                Set<String> owned = principalDevices.get(principal.principalId());
                if (owned != null) {
                    owned.remove(deviceId);
                    if (owned.isEmpty()) {
                        principalDevices.remove(principal.principalId());
                    }
                }
            }
            logger.info("连接已注销: {}", deviceId);
        }
    }

    public WebSocketSession getSession(String deviceId) {
        return sessions.get(deviceId);
    }

    public AuthPrincipal getPrincipal(String deviceId) {
        return principals.get(deviceId);
    }

    /** 返回指定主体当前所有在线连接的 deviceId。 */
    public Set<String> getDeviceIdsByPrincipal(String principalId) {
        Set<String> owned = principalDevices.get(principalId);
        return owned == null ? Set.of() : Set.copyOf(owned);
    }

    public boolean isDeviceOnline(String deviceId) {
        WebSocketSession session = sessions.get(deviceId);
        return session != null && session.isOpen();
    }

    public DeviceType getDeviceType(String deviceId) {
        return deviceTypes.get(deviceId);
    }

    public List<String> getDevicesByType(DeviceType type) {
        List<String> result = new ArrayList<>();
        for (Map.Entry<String, DeviceType> entry : deviceTypes.entrySet()) {
            if (entry.getValue() == type && isDeviceOnline(entry.getKey())) {
                result.add(entry.getKey());
            }
        }
        return result;
    }

    public List<String> getAllOnlineDevices() {
        List<String> result = new ArrayList<>();
        for (Map.Entry<String, WebSocketSession> entry : sessions.entrySet()) {
            if (entry.getValue().isOpen()) {
                result.add(entry.getKey());
            }
        }
        return result;
    }

    public int getOnlineCount() {
        int count = 0;
        for (WebSocketSession session : sessions.values()) {
            if (session.isOpen()) {
                count++;
            }
        }
        return count;
    }

    public Long getConnectedAt(String deviceId) {
        return connectTime.get(deviceId);
    }

    /** 返回全部已注册设备的明细（含当前是否在线）。 */
    public List<DeviceInfo> getAllDeviceDetails() {
        Set<String> allIds = new HashSet<>();
        allIds.addAll(sessions.keySet());
        allIds.addAll(deviceTypes.keySet());
        List<DeviceInfo> result = new ArrayList<>();
        for (String id : allIds) {
            DeviceType type = deviceTypes.get(id);
            boolean online = isDeviceOnline(id);
            result.add(new DeviceInfo(id, type == null ? null : type.name(), online, connectTime.get(id)));
        }
        return result;
    }
}
