package com.jeeplus.gateway.config;

import com.jeeplus.gateway.excepiton.JwtAccessDeniedHandler;
import com.jeeplus.gateway.excepiton.JwtAuthenticationEntryPoint;
import com.jeeplus.gateway.filter.JWTFilter;
import com.jeeplus.gateway.security.TokenProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.savedrequest.NoOpServerRequestCache;
import org.springframework.security.web.server.util.matcher.NegatedServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
import org.springframework.util.StringUtils;

import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers;

@EnableWebFluxSecurity
@EnableWebSecurity
@EnableReactiveMethodSecurity
public class SecurityConfiguration {

    /**
     * 放行白名单
     */
    @Value("${ignore.whiteList}")
    private String whiteList;

    private final TokenProvider tokenProvider;

    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;

    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;


    public SecurityConfiguration(
            TokenProvider tokenProvider,
            JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
            JwtAccessDeniedHandler jwtAccessDeniedHandler
    ) {
        this.tokenProvider = tokenProvider;
        this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
        this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
    }

    @Bean
    public MapReactiveUserDetailsService userDetailsService(SecurityProperties properties) {
        SecurityProperties.User user = properties.getUser ( );
        UserDetails userDetails = User
                .withUsername ( user.getName ( ) )
                .password ( "{noop}" + user.getPassword ( ) )
                .roles ( StringUtils.toStringArray ( user.getRoles ( ) ) )
                .build ( );
        return new MapReactiveUserDetailsService ( userDetails );
    }

    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager(ReactiveUserDetailsService userDetailsService) {
        return new UserDetailsRepositoryReactiveAuthenticationManager ( userDetailsService );
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        // @formatter:off
        http
                .securityMatcher ( new NegatedServerWebExchangeMatcher ( new OrServerWebExchangeMatcher (
                        pathMatchers ( whiteList.split ( "," ) ),
                        pathMatchers ( HttpMethod.OPTIONS, "/**" )
                ) ) )
                .csrf ( )
                .disable ( )
                .addFilterAt ( new JWTFilter ( tokenProvider ), SecurityWebFiltersOrder.HTTP_BASIC )
                .exceptionHandling ( )
                .accessDeniedHandler ( jwtAccessDeniedHandler )
                .authenticationEntryPoint ( jwtAuthenticationEntryPoint )
                .and ( )
                .headers ( )
                .referrerPolicy ( ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN )
                .and ( )
                .featurePolicy ( "geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; fullscreen 'self'; payment 'none'" )
                .and ( )
                .frameOptions ( ).disable ( )
                .and ( )
                .requestCache ( )
                .requestCache ( NoOpServerRequestCache.getInstance ( ) )
                .and ( )
                .authorizeExchange ( )
                .pathMatchers ( whiteList.split ( "," ) ).permitAll ( ) // 允许请求无需认证
                .pathMatchers ( "/api/**" ).authenticated ( )
                .pathMatchers ( "/management/health" ).permitAll ( )
                .pathMatchers ( "/management/health/**" ).permitAll ( )
                .pathMatchers ( "/management/info" ).permitAll ( )
                .pathMatchers ( "/management/prometheus" ).permitAll ( )
                .pathMatchers ( HttpMethod.OPTIONS, "/**" ).permitAll ( )
                .anyExchange ( ).authenticated ( );

        // @formatter:on
        return http.build ( );
    }


}
