package com.bsmlab.dfx.dfxconsole.framework.config;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

@Slf4j
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    //@Bean
    public SecurityFilterChain securityFilterChainFormLogin(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .csrf(AbstractHttpConfigurer::disable) // REST API 를 제공하는 서비스이므로 Cross Site Request Forgery disable
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/app-api/**").hasRole("USER")
                        .requestMatchers("/index.html").hasRole("USER")
                        .anyRequest().permitAll()
                )
                .formLogin(form -> form
                        .loginPage("/login.html")
                        .loginProcessingUrl("/public-api/loginProcess")
                        .defaultSuccessUrl("/index.html")
                        .permitAll()
                )
                .logout(logout -> logout
                        .logoutUrl("/public-api/logoutProcess")
                        .logoutSuccessHandler(this.logoutSuccessHandler())
                        .invalidateHttpSession(true)
                        .deleteCookies("JSESSIONID")
                )
                .sessionManagement(session -> session.maximumSessions(1).maxSessionsPreventsLogin(false));
        ;
        return httpSecurity.build();
    }

    /*
1. 사용자가 POST /public-api/loginProcess 요청 (username, password)
2. JsonUsernamePasswordAuthenticationFilter → attemptAuthentication()
3. UsernamePasswordAuthenticationToken 생성
4. AuthenticationManager → DaoAuthenticationProvider로 위임
5. DaoAuthenticationProvider
    ├─ AppUserDetailsService.loadUserByUsername() 호출
    ├─ UserDetails.getPassword() ← 암호화된 비밀번호
    ├─ 입력값 password 와 비교: passwordEncoder.matches(raw, encoded)
    └─ 성공 or 실패
     */
    @Bean
    public JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
        JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter = new JsonUsernamePasswordAuthenticationFilter();
        jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager);
        jsonUsernamePasswordAuthenticationFilter.setFilterProcessesUrl("/public-api/loginProcess");
        return jsonUsernamePasswordAuthenticationFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChainAjax(HttpSecurity httpSecurity, AuthenticationConfiguration authenticationConfiguration) throws Exception {
        httpSecurity
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(authorize -> authorize.requestMatchers("/app-api/**").authenticated().anyRequest().permitAll())
                .addFilterAt(this.jsonUsernamePasswordAuthenticationFilter(this.authenticationManager(authenticationConfiguration)), UsernamePasswordAuthenticationFilter.class)
                .sessionManagement(session -> session.maximumSessions(1).maxSessionsPreventsLogin(false))
        ;
        return httpSecurity.build();
    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler() {
        return new ConsoleLogoutSuccessHandler();
    }

    @Bean
    public PasswordEncoder getPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public GrantedAuthorityDefaults getGrantedAuthorityDefaults() {
        return new GrantedAuthorityDefaults(""); // "ROLE_" prefix 를 제거한다.
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
        return daoAuthenticationProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

}
