package com.iqser.red.service.persistence.management.v1.processor.configuration;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry;
import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;

import com.knecon.fforesight.keycloakcommons.security.TokenUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    @Value("${cors.enabled:false}")
    private boolean corsEnabled;


    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {

        messages.simpTypeMatchers(SimpMessageType.HEARTBEAT, SimpMessageType.UNSUBSCRIBE, SimpMessageType.DISCONNECT)
                .permitAll()
                .simpTypeMatchers(SimpMessageType.CONNECT)
                .anonymous() // this is intended, see WebSocketConfiguration.configureClientInboundChannel
                .simpTypeMatchers(SimpMessageType.SUBSCRIBE)
                .access("@tenantWebSocketSecurityMatcher.checkCanSubscribeTo(authentication,message)")
                .anyMessage()
                .denyAll();
    }


    @Override
    protected boolean sameOriginDisabled() {

        return corsEnabled;
    }


    @Bean
    public TenantWebSocketSecurityMatcher tenantWebSocketSecurityMatcher() {

        return new TenantWebSocketSecurityMatcher();
    }


    public class TenantWebSocketSecurityMatcher {

        public boolean checkCanSubscribeTo(JwtAuthenticationToken authentication, Message<?> message) {

            var targetedTenant = extractTenantId(message);
            var currentTenant = getCurrentTenant(authentication);
            return targetedTenant.isPresent() && currentTenant.isPresent() && currentTenant.get().equals(targetedTenant.get());
        }


        private Optional<String> getCurrentTenant(JwtAuthenticationToken authentication) {

            if (authentication != null && authentication.getToken() != null && authentication.getToken().getTokenValue() != null) {
                return Optional.of(TokenUtils.toTenant(authentication.getToken().getTokenValue()));
            } else {
                return Optional.empty();
            }
        }

    }

    private Optional<String> extractTenantId(Message<?> message) {

        StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);
        String topic = sha.getDestination();
        if (topic == null) {
            return Optional.empty();
        }

        String tenant = topic.split("/")[2];
        return Optional.of(tenant);
    }

}
