package wf.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.csrf.CsrfToken;
import org.springframework.security.web.server.csrf.DefaultCsrfToken;
import org.springframework.security.web.server.csrf.ServerCsrfTokenRepository;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import wf.model.CaptchaUserDetailsReactiveAuthenticationManager;
import wf.security.CustomServerFormLoginAuthenticationConverter;
import wf.spring.RedirectFluxWebAuthenticationFailureHandler;
import wf.util.Loggers4j2;

/**
 *
 * @author kent
 */
@EnableWebFluxSecurity
@DependsOn("serverLogoutSuccessHandler")
@EnableReactiveMethodSecurity
public class TestSecConfig extends SecConfig {

    private static final reactor.util.Logger logger = Loggers4j2.getLogger(TestSecConfig.class);
    @Value("#{systemProperties['captcha'] ?: '1234'}")
    private String csrf;

    @Override
    @Bean
    public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
        SecurityWebFilterChain build = http
                .authorizeExchange()
                .pathMatchers("/", "/index", "/hello/**", "/static/**", "/login", "/logout", "/ocs").permitAll()
                .pathMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
                .anyExchange()
                .authenticated()
                .and().csrf(c -> c.csrfTokenRepository(new FixedCsrfTokenRepository(csrf)))
                .formLogin((ServerHttpSecurity.FormLoginSpec flt) -> {
                    flt.authenticationManager(new CaptchaUserDetailsReactiveAuthenticationManager(userDetailsService()));
                    flt.loginPage("/login");
                    flt.authenticationFailureHandler(new RedirectFluxWebAuthenticationFailureHandler("/login?error"));
                })
                .logout()
                .logoutUrl("/logout").requiresLogout(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/logout"))
                .logoutSuccessHandler(getLogoutHandler())
                .and().build();
        build.getWebFilters().subscribe(filter -> {
            if (filter instanceof AuthenticationWebFilter) {
                AuthenticationWebFilter awf = (AuthenticationWebFilter) filter;
                awf.setServerAuthenticationConverter(new CustomServerFormLoginAuthenticationConverter());
            }
        });
        return build;
    }

    private static class FixedCsrfTokenRepository implements ServerCsrfTokenRepository {

        private final String csrf;

        public FixedCsrfTokenRepository(String csrf) {
            this.csrf = csrf;
        }

        @Override
        public Mono<CsrfToken> generateToken(ServerWebExchange exchange) {
            return Mono.fromCallable(() -> new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", csrf));
        }

        @Override
        public Mono<Void> saveToken(ServerWebExchange exchange, CsrfToken token) {
            return Mono.empty();
        }

        @Override
        public Mono<CsrfToken> loadToken(ServerWebExchange exchange) {
            return generateToken(exchange);
        }
    }
}