package com.eactive.testmaster.config;


import com.navercorp.lucy.security.xss.servletfilter.XssEscapeServletFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class PortalConfigSecurity {

    private final PortalAuthenticationFailureHandler authenticationFailureHandler;

    private final PortalAuthenticationSuccessHandler authenticationSuccessHandler;
    private final AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler;
    private final AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler;

    private final PortalAuthenticationManager portalAuthenticationManager;
    private final CsrfExclusionService csrfExclusionService;

    public PortalConfigSecurity(PortalAuthenticationFailureHandler authenticationFailureHandler, PortalAuthenticationSuccessHandler authenticationSuccessHandler, AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler,
        AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler, PortalAuthenticationManager portalAuthenticationManager, CsrfExclusionService csrfExclusionService) {
        this.authenticationFailureHandler = authenticationFailureHandler;
        this.authenticationSuccessHandler = authenticationSuccessHandler;
        this.ajaxAuthenticationSuccessHandler = ajaxAuthenticationSuccessHandler;
        this.ajaxAuthenticationFailureHandler = ajaxAuthenticationFailureHandler;
        this.portalAuthenticationManager = portalAuthenticationManager;
        this.csrfExclusionService = csrfExclusionService;
    }

    @Bean
    public FilterRegistrationBean<XssEscapeServletFilter> xssFilterRegistrationBean() {
        FilterRegistrationBean<XssEscapeServletFilter> registrationBean = new FilterRegistrationBean<>();
        XssEscapeServletFilter xssEscapeServletFilter = new XssEscapeServletFilter();
        registrationBean.setFilter(xssEscapeServletFilter);
        registrationBean.setOrder(Integer.MIN_VALUE + 1);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    @Bean
    public CsrfTokenRepository csrfTokenRepository(CsrfExclusionService csrfExclusionService) {
        CookieCsrfTokenRepository repository = new CookieCsrfTokenRepository();
        repository.setCookieHttpOnly(false);
        return new CsrfTokenRepository() {
            @Override
            public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
                repository.saveToken(token, request, response);
            }

            @Override
            public CsrfToken loadToken(HttpServletRequest request) {
                return repository.loadToken(request);
            }

            @Override
            public CsrfToken generateToken(HttpServletRequest request) {
                return repository.generateToken(request);
            }
        };
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authenticationManager(portalAuthenticationManager)
            .formLogin(form -> form
                .loginPage("/login")
                .usernameParameter("id")
                .passwordParameter("password")
                .loginProcessingUrl("/actionLogin.do")
                .failureHandler((request, response, exception) -> {
                    if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
                        ajaxAuthenticationFailureHandler.onAuthenticationFailure(request, response, exception);
                    } else {
                        authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
                    }
                })
                .successHandler((request, response, authentication) -> {
                    if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
                        ajaxAuthenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication);
                    } else {
                        authenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication);
                    }
                }))
            .logout(logout -> logout
                .logoutRequestMatcher(new AntPathRequestMatcher("/actionLogout.do", "GET"))
                .logoutSuccessUrl("/"))
            .csrf(csrf -> csrf
                .csrfTokenRepository(csrfTokenRepository(csrfExclusionService))
                .ignoringRequestMatchers(request -> csrfExclusionService.shouldExclude(request.getServletPath()))
                .ignoringAntMatchers("/h2-console/**")
            )
            .headers(headers -> headers.frameOptions().sameOrigin())
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .sessionFixation()
                .changeSessionId()
            );

        return http.build();
    }
}
