package cn.reghao.tnb.auth.app.security;

import cn.reghao.tnb.auth.app.security.filter.LoginRedirectFilter;
import cn.reghao.tnb.auth.app.security.form.ThirdPartyAuthFilter;
import cn.reghao.tnb.auth.app.security.form.AccountAuthFilter;
import cn.reghao.tnb.auth.app.security.form.AccountAuthProvider;
import cn.reghao.tnb.auth.app.security.session.MySecurityContextRepository;
import cn.reghao.tnb.auth.app.security.session.MySessionAuthenticationStrategy;
import cn.reghao.tnb.auth.app.service.AccountLoginService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.context.SecurityContextHolderFilter;

/**
 * Web 应用安全配置
 *
 * @author reghao
 * @date 2022-07-04 09:17:54
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    private final String loginPage = "/login";
    private final String loginApi = "/api/auth/signin";
    private final String logoutApi = "/api/auth/signout";

    private final MySecurityContextRepository mySecurityContextRepository;
    private final AccountAuthProvider userAuthProvider;
    private final AccountLoginService accountLoginService;
    private final AuthenticationSuccessHandler successHandler;
    private final AuthenticationFailureHandler failureHandler;
    private final LogoutHandler logoutHandler;
    private final LogoutSuccessHandler logoutSuccessHandler;

    public WebSecurityConfig(MySecurityContextRepository mySecurityContextRepository,
                             AccountAuthProvider userAuthProvider,
                             AccountLoginService accountLoginService, AuthenticationSuccessHandler successHandler,
                             AuthenticationFailureHandler failureHandler, LogoutHandler logoutHandler,
                             LogoutSuccessHandler logoutSuccessHandler) {
        this.mySecurityContextRepository = mySecurityContextRepository;
        this.userAuthProvider = userAuthProvider;
        this.accountLoginService = accountLoginService;
        this.successHandler = successHandler;
        this.failureHandler = failureHandler;
        this.logoutHandler = logoutHandler;
        this.logoutSuccessHandler = logoutSuccessHandler;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
        http
                .authorizeHttpRequests(request -> request
                        .requestMatchers("/api/account/**").authenticated()
                        .anyRequest().permitAll())
                .addFilterAfter(new LoginRedirectFilter(), SecurityContextHolderFilter.class)
                .addFilterBefore(accountAuthFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(thirdPartyAuthFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class)
                .securityContext().securityContextRepository(mySecurityContextRepository)
                .and()
                .formLogin().disable()
                .logout().logoutUrl(logoutApi).addLogoutHandler(logoutHandler).logoutSuccessHandler(logoutSuccessHandler)
                .and()
                .exceptionHandling().authenticationEntryPoint(new MyAuthenticationEntryPoint())
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS).sessionAuthenticationStrategy(new MySessionAuthenticationStrategy())
                .and()
                .rememberMe().disable()
                .cors().disable()
                .csrf().disable()
                .headers().disable()
                .authenticationProvider(userAuthProvider);

        return http.build();
    }

    /**
     * 放行静态资源
     *
     * @param
     * @return
     * @date 2023-08-02 11:09:45
     */
    /*@Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .antMatchers("/dist/**")
                .antMatchers("/classic/**")
                .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/v2/**", "/webjars/**");
    }*/

    /*@Override
    public void configure(HttpSecurity http) throws Exception {
        // 在 FilterSecurityInterceptor 中用于权限判定
        http.authorizeRequests()
                // 需要认证的接口
                .antMatchers("/api/account/**").authenticated()
                .and()
                .authorizeRequests().anyRequest().permitAll();

        // 配置 FilterChainProxy 过滤器链
        http.addFilterAfter(new LoginRedirectFilter(), SecurityContextPersistenceFilter.class);
        http.addFilterBefore(accountAuthFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(thirdPartyAuthFilter(), UsernamePasswordAuthenticationFilter.class);

        // 自定义 SecurityContextPersistenceFilter 中使用的 SecurityContextRepository
        http.securityContext().securityContextRepository(mySecurityContextRepository);

        // 配置 UsernamePasswordAuthenticationFilter, 禁用 UsernamePasswordAuthenticationFilter
        http.formLogin().disable();
                *//*.loginPage(loginPage)
                .loginProcessingUrl(loginApi);*//*

        // 配置 LogoutFilter
        http.logout()
                .logoutUrl(logoutApi)
                .addLogoutHandler(logoutHandler)
                //.logoutSuccessUrl(loginPage)
                .logoutSuccessHandler(logoutSuccessHandler);

        // 配置 ExceptionTranslationFilter, 登入认证接口失败时的处理, 不会重定向到 loginPage
        http.exceptionHandling()
                .authenticationEntryPoint(new MyAuthenticationEntryPoint());

        // 配置 SessionManagementFilter 和 ConcurrentSessionFilter
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
                .sessionAuthenticationStrategy(new MySessionAuthenticationStrategy());
                // spring security 中禁用 session
                //.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
                *//*.maximumSessions(1)
                .expiredUrl(loginPage);*//*

        // 配置 RememberMeAuthenticationFilter, 禁用 RememberMeAuthenticationFilter
        http.rememberMe().disable();
                *//*.key("DExNzAyNTQ2Nzo3NDI3MTNhYmM5MGE5")
                .rememberMeParameter("rememberMe");*//*

        // 配置 CorsFilter, 禁用 CorsFilter
        http.cors().disable();

        // 配置 CsrfFilter, 禁用 CsrfFilter
        http.csrf().disable();

        // 配置 HeaderWriterFilter, 禁用 HeaderWriterFilter
        http.headers().disable();
    }*/

    /**
     * 配置认证管理器
     *
     * @param
     * @return
     * @date 2021-07-25 下午2:28
     */
    /*@Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }*/
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    /**
     * 配置认证使用的 provider
     *
     * @param
     * @return
     * @date 2021-07-25 下午2:31
     */
    /*@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(userAuthProvider);
    }*/

    /**
     * 配置账号密码登入 filter
     *
     * @param
     * @return
     * @date 2022-07-06 上午9:54
     */
    private AccountAuthFilter accountAuthFilter(AuthenticationManager authenticationManager) throws Exception {
        AccountAuthFilter filter = new AccountAuthFilter(loginApi, "POST", accountLoginService);
        filter.setAuthenticationManager(authenticationManager);
        filter.setAuthenticationSuccessHandler(successHandler);
        filter.setAuthenticationFailureHandler(failureHandler);
        return filter;
    }

    /**
     * 配置第三方 OAuth 登入 filter
     *
     * @param
     * @return
     * @date 2023-07-31 16:14:14
     */
    private ThirdPartyAuthFilter thirdPartyAuthFilter(AuthenticationManager authenticationManager) throws Exception {
        ThirdPartyAuthFilter filter = new ThirdPartyAuthFilter("/oauth/redirect/**", "GET");
        filter.setAuthenticationManager(authenticationManager);
        filter.setAuthenticationSuccessHandler(successHandler);
        filter.setAuthenticationFailureHandler(failureHandler);
        return filter;
    }

    /**
     * 角色继承
     * ADMIN 可以访问 USER 的权限，反之不可
     *
     * @date 2019-07-05 上午11:18
     */
    @Bean
    public RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
        return roleHierarchy;
    }
}
