package com.m2pool.chat.interceptor;


import com.m2pool.chat.config.CustomWebSocketConfig;
import com.m2pool.chat.constant.ExceptionEnum;
import com.m2pool.chat.entity.StompPrincipal;
import com.m2pool.chat.listener.IpLimitEvent;
import io.lettuce.core.ScriptOutputType;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import static com.m2pool.chat.constant.CustomHeader.*;
import static com.m2pool.chat.constant.UserType.LOGIN_USER;
import static com.m2pool.chat.constant.UserType.TOURIST;
import static com.m2pool.chat.utils.StompUtils.getConnectCustomHeaders;


/**
 * @ClassName WebsocketChannelInterceptor
 * @Description websocket channel 通道拦截器   适用于前端@stomp/stompjs 实现.
 * @Author yyb
 * @Date 2025/4/10 15:44
 */
@Data
public class WebsocketChannelInterceptor implements ChannelInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketChannelInterceptor.class);

    /**
     * 当前加入链接的ip ,key 为 ip+邮箱 ,VALUE 邮箱
     */
    private static final ConcurrentHashMap<String, EmailLimit> ipConnectionCountMap = new ConcurrentHashMap<>();

    /**
     * 当前链接数
     */
    private static final AtomicInteger connectionCount = new AtomicInteger(0);

    /**
     * websocket 一些配置
     */
    private CustomWebSocketConfig customWebSocketConfig;

    private ApplicationEventPublisher applicationEventPublisher;


    public WebsocketChannelInterceptor(CustomWebSocketConfig customWebSocketConfig,
                                       ApplicationEventPublisher applicationEventPublisher) {
        this.customWebSocketConfig = customWebSocketConfig;
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * websocket channel 通道拦截器
     * @param message
     * @param channel
     * @return
     */
    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        //获取链接建立时的请求头信息
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
        if (accessor.getCommand() == StompCommand.CONNECT ) {
            System.out.println("yyb-开始链接"+new Date());
            StompHeaderAccessor mha = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
            if(mha == null){
                throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.SET_PRINCIPAL_FAIL));
            }
            Object raw = message.getHeaders().get(SimpMessageHeaderAccessor.NATIVE_HEADERS);
            int type = Integer.parseInt(getConnectCustomHeaders(raw, TYPE));
            String email = getConnectCustomHeaders(raw, EMAIL);
            //最大链接数限制
            maxConnectionsLimit();
            //根据客服端ip + 用户类型限制连接数
            ipLimit(accessor,type,email);
            //链接请求头中用户信息存入stomp中
            mha.setUser(new StompPrincipal(email,type,true));
            System.out.println("yyb-链接成功"+new Date());
        }
        if (accessor.getCommand() == StompCommand.SUBSCRIBE) {
            LOGGER.info("------------websocket subscribe message");
        }

        if (accessor.getCommand() == StompCommand.UNSUBSCRIBE) {
            LOGGER.info("------------websocket unsubscribe");
        }

        if (accessor.getCommand() == StompCommand.DISCONNECT){
            StompHeaderAccessor mha = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
            if(mha == null){
                try {
                    throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.SET_PRINCIPAL_FAIL));
                }catch (MessageDeliveryException e){
                    System.out.println("错误消息"+e.getMessage());
                    accessor.setHeader("error", e.getMessage());
                }

            }
            if (mha.getUser() != null){
                String email = mha.getUser().getName();
                LOGGER.info("断开连接的邮箱为:{}",email);
                disconnectHandler(accessor,email);
            }
            LOGGER.info("------------websocket disconnect");
        }
        return message;
    }

    /**
     * 最大连接数限制
     */
    private void maxConnectionsLimit(){
        if(connectionCount.incrementAndGet() >= customWebSocketConfig.getMaxConnections()){
            LOGGER.info("reach max connections,refuse connect");
            throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.MAX_LIMIT_CONNECT));
        }
    }

    /**
     * ip 链接个数限制
     * @param accessor
     */
    private void ipLimit(StompHeaderAccessor accessor,int type,String email){
        Map<String, Object> sessionAttributes = accessor.getSessionAttributes();
        if (sessionAttributes != null) {
            String ipAddr = (String) sessionAttributes.get(IPADDR);
            String key = ipAddr + email;
            EmailLimit emailAndCount = ipConnectionCountMap.get(key);

            //两种ip限制情况
            //本次链接为游客 且ip上已经有人链接直接拒绝本次链接
            if(type == TOURIST && emailAndCount != null){
                throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.IP_LIMIT_CONNECT));
            }

            // 登录用户现在ip限制不用做了，前端在同一ip情况下，不同用户会断开连接
            //本次链接为登录用户,并且已经链接.直接拒绝本次链接
            if ( type != TOURIST && emailAndCount != null) {
                emailAndCount.setCount(emailAndCount.getCount() + 1);
                if (emailAndCount.getCount() >= 10){
                    throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.IP_LIMIT_CONNECT));
                }
                //if(ipConnectionCountMap.containsValue(emailAndCount)){
                //    ipConnectionCountMap.put(ipAddr, emailAndCount);
                //    throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.ACCOUNT_HAS_CONNECTED));
                //}
            }else{
                EmailLimit emailLimit = new EmailLimit();
                emailLimit.setEmail(email);
                emailLimit.setCount(1);
                ipConnectionCountMap.put(key,emailLimit );
            }
        }else{
            throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.GET_PRINCIPAL_FAIL));
        }
    }



    /**
     * 断开链接处理
     */
    private void disconnectHandler(StompHeaderAccessor accessor,String email){
        //连接数减一
        connectionCount.decrementAndGet();
        //断开链接的ip
        Map<String, Object> sessionAttributes = accessor.getSessionAttributes();
        if (sessionAttributes != null ) {
            String ipAddr = (String) sessionAttributes.get(IPADDR);
            String key = ipAddr + email;
            EmailLimit emailAndCount = ipConnectionCountMap.get(key);
            if (emailAndCount != null ){
                if (emailAndCount.getCount() > 1){
                    emailAndCount.setCount(emailAndCount.getCount() - 1);
                }else{
                    ipConnectionCountMap.remove(key);
                }
            }


        }else{
            throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.GET_PRINCIPAL_FAIL));
        }
    }

    @Override
    public void postSend(Message<?> message, MessageChannel channel, boolean sent) {

    }

    @Override
    public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, @Nullable Exception ex) {
    }

    @Override
    public boolean preReceive(MessageChannel channel) {
        return true;
    }

    @Override
    public Message<?> postReceive(Message<?> message, MessageChannel channel) {
        return message;
    }

    @Override
    public void afterReceiveCompletion(@Nullable Message<?> message, MessageChannel channel, @Nullable Exception ex) {
    }

    /**
     * ip限制存储对象
     */
    @Data
    private class EmailLimit{
        private String email;
        private int count = 1;
    }
}
