package com.customer.design;

import jakarta.servlet.DispatcherType;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;

import com.flowable.autoconfigure.design.security.DesignHttpSecurityCustomizer;
import com.flowable.autoconfigure.design.security.servlet.DesignPathRequest;
import com.flowable.design.security.spring.web.authentication.AjaxAuthenticationFailureHandler;
import com.flowable.design.security.spring.web.authentication.AjaxAuthenticationSuccessHandler;

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "application.design.security", name = "type", havingValue = "basic", matchIfMissing = true)
@EnableWebSecurity
public class SecurityHttpBasicConfiguration {

    @Bean
    @Order(10)
    public SecurityFilterChain basicDefaultSecurity(HttpSecurity http, ObjectProvider<DesignHttpSecurityCustomizer> httpSecurityCustomizers) throws Exception {
        for (DesignHttpSecurityCustomizer customizer : httpSecurityCustomizers.orderedStream().toList()) {
            customizer.customize(http);
        }

        http.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        http.logout(logout -> logout.logoutUrl("/auth/logout").logoutSuccessUrl("/"));

        // Non authenticated exception handling. The formLogin and httpBasic configure the exceptionHandling
        // We have to initialize the exception handling with a default authentication entry point in order to return 401 each time and not have a
        // forward due to the formLogin or the http basic popup due to the httpBasic
        http
                .exceptionHandling(exceptionHandling -> exceptionHandling
                        .defaultAuthenticationEntryPointFor((request, response, authException) -> {}, new DispatcherTypeRequestMatcher(DispatcherType.ERROR))
                        .defaultAuthenticationEntryPointFor(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), AnyRequestMatcher.INSTANCE))
                .formLogin(formLogin -> formLogin
                        .loginProcessingUrl("/auth/login")
                        .successHandler(new AjaxAuthenticationSuccessHandler())
                        .failureHandler(new AjaxAuthenticationFailureHandler())
                )
                .authorizeHttpRequests(configurer -> configurer
                        .requestMatchers(DesignPathRequest.toStaticResources().atCommonLocations()).permitAll()
                        .anyRequest().authenticated()
                )
                .httpBasic(Customizer.withDefaults());

        return http.build();
    }
}
