package com.iformall.config;

import com.iformall.interceptor.AuthHandshakeInterceptor;
import com.iformall.model.User;
import com.iformall.service.UserService;
import com.iformall.util.Constance;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
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 org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;

import java.security.Principal;
import java.util.LinkedList;
import java.util.Map;

/**
 * websocket 配置
 * https://docs.spring.io/spring-security/site/docs/4.0.x/reference/html/websocket.html
 * https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html
 * https://stackoverflow.com/questions/48903044/how-to-secure-websocket-application-spring-boot-stomp
 *
 * @author Stormeye
 * @version 2018/03/20
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private RabbitMqProperty property;

    @Autowired
    private AuthHandshakeInterceptor authHandshakeInterceptor;

    @Autowired
    private UserService userService;

    @Autowired
    private RabbitAdmin rabbitAdmin;

    /**
     * 配置了一个简单的消息代理，
     *    如果不重载，默认情况下回自动配置一个简单的内存消息代理，用来处理以"/topic"为前缀的消息。这里重载configureMessageBroker()方法，
     * 消息代理将会处理前缀为"/topic"和"/queue"的消息。
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // 这句话表示在topic和user这两个域上可以向客户端发消息。
        /*registry.enableSimpleBroker("/topic", "/user")
                .setHeartbeatValue(new long[]{10000L, 10000L})
                .setTaskScheduler(new DefaultManagedTaskScheduler());*/
        registry.enableStompBrokerRelay("/topic", "/queue")
                .setRelayHost(property.getHost())      // rabbitmq-host服务器地址
                .setRelayPort(61613)     // rabbitmq-stomp 服务器服务端口
                .setClientLogin(property.getUsername())   // 登陆账户
                .setClientPasscode(property.getPassword()); // 登陆密码
        // 这句话表示客户单向服务器端发送时的主题上面需要加"/app"作为前缀。/app/xxx
        registry.setApplicationDestinationPrefixes("/app");
        // 这句话表示给指定用户发送一对一的主题前缀是"/user"。 /user/xxx
        registry.setUserDestinationPrefix("/user");
    }

    /**
     * 连接站点配置
     * 将"/gs-guide-websocket"路径注册为STOMP端点，这个路径与发送和接收消息的目的路径有所不同，这是一个端点，客户端在订阅或发布消息到目的地址前，要连接该端点，
     * 即用户发送请求url="/gs-guide-websocket"与STOMP server进行连接。之后再转发到订阅url；
     * PS：端点的作用——客户端在订阅或发布消息到目的地址前，要连接该端点。
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        /*
         * 在网页上可以通过"/gs-guide-websocket"来和服务器的WebSocket连接
         * 这个和客户端创建连接时的url有关，其中setAllowedOrigins()方法表示允许连接的域名，withSockJS()方法表示支持以SockJS方式连接服务器。
         */
        registry.addEndpoint("/ws")
                .setAllowedOrigins("*")
                ;//.addInterceptors(authHandshakeInterceptor);
    }

    /**
     * 输入通道配置
     *
     * @param registration
     */
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new ChannelInterceptor() {
            @Override
            public Message<?> preSend(Message<?> message, MessageChannel channel) {

                StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

                logger.info("interceptors-> "+accessor.getCommand()+"-"+accessor);

                if (StompCommand.CONNECT.equals(accessor.getCommand())) {// 创建连接
                    Object raw = message.getHeaders().get(SimpMessageHeaderAccessor.NATIVE_HEADERS);
                    if (raw instanceof Map) {
                        Object name = ((Map) raw).get("login");
                        if (name instanceof LinkedList) {
                            // 设置当前访问器的认证用户
                            String userName = ((LinkedList) name).get(0).toString();
                            System.out.println("regist userName:"+userName);
                            accessor.setUser(new User(userName));
                            userService.addUser(userName, accessor.getSessionId());
                        }
                    }
                }

                if (StompCommand.DISCONNECT.equals(accessor.getCommand())) {// 断开连接
                    String userName = accessor.getUser().getName();
                    rabbitAdmin.deleteQueue(Constance.queue_pre+userService.getSessionId(userName));
                    userService.deleteUser(userName);
                }
                return message;
            }
        });
    }

    /**
     * 消息传输参数配置
     *
     * @param registration
     */
    /*
    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        registration.setSendTimeLimit(15 * 1000)    // 超时时间
                .setSendBufferSizeLimit(512 * 1024) // 缓存空间
                .setMessageSizeLimit(128 * 1024);   // 消息大小
    }
    */

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        return rabbitAdmin;
    }

}