package com.shzg.project.worn.websocket.endpoint;

import com.shzg.framework.security.LoginUser;
import com.shzg.framework.security.service.TokenService;
import com.shzg.project.system.domain.SysDept;
import com.shzg.project.system.mapper.SysDeptMapper;
import com.shzg.project.worn.websocket.domain.WsUserInfo;
import com.shzg.project.worn.websocket.manager.WebSocketSessionManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.util.*;

@Slf4j
@Component
@ServerEndpoint("/ws")
public class WornWebSocketServer {

    private static WebSocketSessionManager sessionManager;
    private static SysDeptMapper deptMapper;
    private static TokenService tokenService;

    // 🔥 限流：记录IP最近日志时间
    private static final Map<String, Long> INVALID_TOKEN_LOG_CACHE = new HashMap<>();
    private static final long LOG_INTERVAL = 60_000; // 60秒

    @Autowired
    public void setSessionManager(WebSocketSessionManager manager) {
        WornWebSocketServer.sessionManager = manager;
    }

    @Autowired
    public void setDeptMapper(SysDeptMapper mapper) {
        WornWebSocketServer.deptMapper = mapper;
    }

    @Autowired
    public void setTokenService(TokenService service) {
        WornWebSocketServer.tokenService = service;
    }

    @OnOpen
    public void onOpen(Session session) {

        String ip = getClientIp(session);
        session.getUserProperties().put("ip", ip);

        log.info("[WebSocket] 连接建立 sessionId={}, ip={}", session.getId(), ip);

        String query = session.getQueryString();

        if (query == null || !query.contains("token=")) {
            logInvalid(ip, "missing token", session.getId(), query, null);
            closeSession(session, "missing token");
            return;
        }

        try {
            String token = parseToken(query);
            String maskedToken = maskToken(token);

            if (token == null || token.trim().isEmpty()) {
                logInvalid(ip, "invalid token(empty)", session.getId(), query, maskedToken);
                closeSession(session, "invalid token");
                return;
            }

            LoginUser loginUser = tokenService.getLoginUserByToken(token);

            if (loginUser == null) {
                logInvalid(ip, "invalid token(loginUser null)", session.getId(), query, maskedToken);
                closeSession(session, "invalid token");
                return;
            }

            Long userId = loginUser.getUserId();
            Long deptId = loginUser.getDeptId();
            String userName = loginUser.getUsername();

            boolean isAdmin = loginUser.getUser() != null && loginUser.getUser().isAdmin();

            if (deptId == null) {
                log.warn("[WebSocket] 关闭连接 reason=deptId is null, sessionId={}, userId={}, ip={}",
                        session.getId(), userId, ip);
                closeSession(session, "deptId is null");
                return;
            }

            Set<Long> deptIds = new HashSet<>();

            if (isAdmin) {
                List<SysDept> allDept = deptMapper.selectDeptList(new SysDept());
                if (allDept != null) {
                    for (SysDept dept : allDept) {
                        if (dept.getDeptId() != null) {
                            deptIds.add(dept.getDeptId());
                        }
                    }
                }
            } else {
                List<SysDept> deptList = deptMapper.selectDeptAndChildren(deptId, null);
                if (deptList != null && !deptList.isEmpty()) {
                    for (SysDept dept : deptList) {
                        if (dept.getDeptId() != null) {
                            deptIds.add(dept.getDeptId());
                        }
                    }
                } else {
                    deptIds.add(deptId);
                }
            }

            WsUserInfo userInfo = new WsUserInfo();
            userInfo.setUserId(userId);
            userInfo.setUserName(userName);
            userInfo.setAdmin(isAdmin);
            userInfo.setDeptIds(deptIds);

            boolean success = sessionManager.register(session, userInfo);

            if (!success) {
                log.warn("[WebSocket] 连接过多，拒绝 sessionId={}, userId={}, ip={}",
                        session.getId(), userId, ip);
                closeSession(session, "too many connections");
                return;
            }

            log.info("[WebSocket] 注册成功 sessionId={}, userId={}, userName={}, ip={}, deptIds={}",
                    session.getId(), userId, userName, ip, deptIds);

        } catch (Exception e) {
            log.error("[WebSocket] 连接异常 sessionId={}, ip={}", session.getId(), ip, e);
            closeSession(session, "error");
        }
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        sessionManager.remove(session);
        log.info("[WebSocket] 连接关闭 sessionId={}, code={}, reason={}",
                session.getId(),
                closeReason != null ? closeReason.getCloseCode() : "unknown",
                closeReason != null ? closeReason.getReasonPhrase() : "unknown");
    }

    @OnError
    public void onError(Session session, Throwable error) {
        if (session != null) {
            sessionManager.remove(session);
            log.error("[WebSocket] 异常 sessionId={}", session.getId(), error);
        } else {
            log.error("[WebSocket] 异常", error);
        }
    }

    // ================= IP获取（核心） =================
    private String getClientIp(Session session) {
        try {
            // 1️⃣ 优先 remoteAddress
            Object addr = session.getUserProperties().get("javax.websocket.endpoint.remoteAddress");
            if (addr instanceof InetSocketAddress) {
                InetSocketAddress inet = (InetSocketAddress) addr;
                if (inet.getAddress() != null) {
                    return inet.getAddress().getHostAddress();
                }
            }

            // 2️⃣ URI解析
            String uri = session.getRequestURI().toString();
            return uri;

        } catch (Exception e) {
            return "unknown";
        }
    }

    // ================= token解析 =================
    private String parseToken(String query) throws Exception {
        String[] params = query.split("&");
        for (String param : params) {
            if (param.startsWith("token=")) {
                String token = param.substring(6);
                token = URLDecoder.decode(token, "UTF-8");

                if (token.startsWith("Bearer ")) {
                    token = token.substring(7);
                }
                return token;
            }
        }
        throw new IllegalArgumentException("token not found");
    }

    // ================= token脱敏 =================
    private String maskToken(String token) {
        if (token == null) return "null";
        if (token.length() <= 10) return token;
        return token.substring(0, 6) + "..." + token.substring(token.length() - 4);
    }

    // ================= 限流日志 =================
    private void logInvalid(String ip, String reason, String sessionId, String query, String token) {
        long now = System.currentTimeMillis();

        Long lastTime = INVALID_TOKEN_LOG_CACHE.get(ip);

        if (lastTime == null || now - lastTime > LOG_INTERVAL) {
            log.warn("[WebSocket] 无效连接 ip={}, reason={}, sessionId={}, token={}",
                    ip, reason, sessionId, token);
            INVALID_TOKEN_LOG_CACHE.put(ip, now);
        }
    }

    private void closeSession(Session session, String reason) {
        try {
            if (session != null && session.isOpen()) {
                session.close(new CloseReason(
                        CloseReason.CloseCodes.CANNOT_ACCEPT,
                        reason
                ));
            }
        } catch (IOException ignored) {}
    }
}