package com.dbnt.kcscbackend.config.security;

import com.dbnt.kcscbackend.config.jwt.EgovJwtTokenUtil;
import com.dbnt.kcscbackend.config.jwt.JwtAuthenticationEntryPoint;
import com.dbnt.kcscbackend.config.jwt.JwtAuthenticationFilter;
import com.dbnt.kcscbackend.config.jwt.redis.RefreshTokenRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import java.util.Arrays;

/**
 * fileName       : SecurityConfig
 * author         : crlee
 * date           : 2023/06/10
 * description    :
 * ===========================================================
 * DATE              AUTHOR             NOTE
 * -----------------------------------------------------------
 * 2023/06/10        crlee       최초 생성
 */
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    @Resource(name = "loginService")
    private UserDetailsService loginService;
    private final ObjectMapper objectMapper;
    private final EgovJwtTokenUtil egovJwtTokenUtil;

    //Http Methpd : Get 인증예외 List
    private String[] AUTH_GET_WHITELIST = {
            "/schedule/daily", //일별 일정 조회
            "/schedule/week", //주간 일정 조회
            "/schedule/{schdulId}", //일정 상세조회
    };

    // 인증 예외 List
    private String[] AUTH_WHITELIST = {
            "/",
            "/login",
            "/auth/login",
            "/auth/accessTokenRefresh", // jwt accessToken 갱신
            "/auth/join",//회원가입
            "/auth/findId", // id 찾기
            "/auth/findPw", // pw 찾기

            "/cmm/main/**.do", // 메인페이지
            "/cmm/fms/FileDown.do", //파일 다운로드
            "/cmm/fms/getImage.do", //갤러리 이미지보기

            "/cop/bbs/selectUserBBSMasterInfAPI.do", //게시판 마스터 상세 조회
            "/cop/bbs/selectBoardListAPI.do", //게시판 목록조회
            "/cop/bbs/selectBoardArticleAPI.do", //게시물 상세조회

            /* swagger v2 */
            "/v2/api-docs",
            "/swagger-resources",
            "/swagger-resources/**",
            "/swagger-ui.html",
            "/swagger-ui/**",

            /*기준코드 조회*/
            "/standardCode/**.do"
    };
    private static final String[] ORIGINS_WHITELIST = {
            "http://localhost:3000",
    };

    @Bean
    public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
        return new JwtAuthenticationFilter();
    }


    @Bean
    protected CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();

        configuration.setAllowedOriginPatterns(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("HEAD","POST","GET","DELETE","PUT"));
        configuration.setAllowedOrigins(Arrays.asList(ORIGINS_WHITELIST));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.httpBasic().disable()
            .csrf().disable()
            .formLogin().disable();

        http.authorizeHttpRequests(authorize -> authorize
                .antMatchers(AUTH_WHITELIST).permitAll()
                .antMatchers(HttpMethod.GET,AUTH_GET_WHITELIST).permitAll()
                .anyRequest().authenticated()
            );
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);


        http.cors().and().addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
            .exceptionHandling(exceptionHandlingConfigurer ->
                exceptionHandlingConfigurer.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
            );

        http.addFilterBefore(jsonUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/auth/logout")).logoutSuccessHandler(new CustomLogoutSuccessHandler());

        return http.build();
    }

    @Bean
    public AuthenticationFailureHandler loginFailureHandler(){
        return (request, response, exception) -> {
            new DefaultRedirectStrategy().sendRedirect(request, response, "/login-error");
        };
    }

    @Bean
    public JsonAuthenticationFilter jsonUsernamePasswordAuthenticationFilter() {
        JsonAuthenticationFilter jsonAuthenticationFilter = new JsonAuthenticationFilter(objectMapper, new CustomUrlAuthenticationSuccessHandler(egovJwtTokenUtil), loginFailureHandler());
        jsonAuthenticationFilter.setAuthenticationManager(authenticationManager());
        return jsonAuthenticationFilter;
    }

    @Bean
    public AuthenticationManager authenticationManager() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();

        provider.setPasswordEncoder(passwordEncoder());
        provider.setUserDetailsService(loginService);

        return new ProviderManager(provider);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new Pbkdf2PasswordEncoder();
    }

}