package com.dbnt.faisp.config;

import com.dbnt.faisp.main.userInfo.model.UserInfo;
import com.dbnt.faisp.main.userInfo.model.UserInoutLog;
import com.dbnt.faisp.main.userInfo.service.UserLogService;
import com.dbnt.faisp.util.Utils;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.http.HttpServletResponse;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig{
  private final Logger log = LoggerFactory.getLogger(getClass());
  private final BaseController baseController;
  private final UserLogService userLogService;

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

  @Bean
  public AccessDeniedHandler accessDeniedHandler() {
    log.warn("accessDeniedHandler");
    return (request, response, e) -> {
      response.setStatus(HttpServletResponse.SC_FORBIDDEN);
      response.setContentType("text/html;charset=UTF-8");
      response.getWriter().println("<script>alert('접근이 거부되었습니다.'); location.href='/';</script>");
      response.getWriter().flush();
      response.getWriter().close();
    };
  }

  @Bean
  public AuthenticationEntryPoint authenticationEntryPoint() {
    return (request, response, e) -> {
      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      response.setContentType("text/html;charset=UTF-8");
      response.getWriter().println("<script>alert('로그인이 해제되었습니다.'); location.href='/';</script>");
      response.getWriter().flush();
      response.getWriter().close();
    };
  }

  @Bean
  public AuthenticationSuccessHandler loginSuccessHandler() {
    return (request, response, authentication) -> {
      SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(request,response);
      baseController.setSession((UserInfo)authentication.getPrincipal(), request.getSession());

      UserInoutLog inoutLog = new UserInoutLog();
      inoutLog.setInoutType("IOT001");
      inoutLog.setContactIp(Utils.getClientIP(request));
      inoutLog.setSessionId(request.getSession().getId());
      inoutLog.setUserOrgan((String) request.getSession().getAttribute("userOrgan"));
      inoutLog.setUserBelong((String) request.getSession().getAttribute("belongValue"));
      userLogService.saveInoutLog(inoutLog);

      if(savedRequest != null){
        String targetUrl = savedRequest.getRedirectUrl();
        new DefaultRedirectStrategy().sendRedirect(request,response,targetUrl);
      }else{
        new DefaultRedirectStrategy().sendRedirect(request,response,"/");
      }
    };
  }
  @Bean
  public AuthenticationFailureHandler loginFailureHandler(){
    return (request, response, exception) -> {
      UserInoutLog inoutLog = new UserInoutLog();
      inoutLog.setInoutType("IOT002");
      inoutLog.setContactIp(Utils.getClientIP(request));
      userLogService.saveInoutLog(inoutLog);

      new DefaultRedirectStrategy().sendRedirect(request, response, "/login-error");
    };
  }
  @Bean
  public LogoutHandler logoutHandler(){
    return (request, response, authentication) -> {
      UserInoutLog inoutLog = new UserInoutLog();
      inoutLog.setInoutType("IOT003");
      inoutLog.setContactIp(Utils.getClientIP(request));
      inoutLog.setSessionId(request.getSession().getId());
      inoutLog.setUserOrgan((String) request.getSession().getAttribute("userOrgan"));
      inoutLog.setUserBelong((String) request.getSession().getAttribute("belongValue"));
      userLogService.saveInoutLog(inoutLog);
    };
  }

  @Bean
  protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests() // 페이지 권한 설정
          .antMatchers(
              "/dashboard",
              "/myInfo/**",
              "/modal/**",
              "/selectBoxOptions",
              "/publicBoard/**",
              "/faRpt/**",
              "/ivsgt/**",
              "/target/**",
              "/equip/**",
              "/affairPlan/**",
              "/affair/**",
              "/affairResult/**",
              "/faStatistics/**",
              "/translator/**",
              "/police/**",
              "/sri/**"
          ).hasRole(Role.USER.name()) // USER 접근 허용
          .antMatchers(
              "/authMgt/**",
              "/userMgt/**"
          ).hasRole(Role.SUB_ADMIN.name()) // SUB_ADMIN 접근 허용
          .antMatchers(
              "/codeMgt/**",
              "/organMgt/**",
              "/menuMgt/**"
          ).hasRole(Role.ADMIN.name()) // ADMIN 접근 허용
          .antMatchers(
              "/login",
              "/css/**",
              "/img/**",
              "/js/**",
              "/vendor/**",
              "/Crosseditor/uploadImg"
          ).permitAll() // 로그인 페이지는 권한 없이 접근 허용
        .and() // 로그인 설정
          .formLogin().loginPage("/login") // Custom login form 사용
          .failureHandler(loginFailureHandler()) // 로그인 실패시 동작 수행
          .successHandler(loginSuccessHandler()) // 로그인 성공시 동작 수행.
        .and() // 로그아웃 설정
          .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")) // 로그아웃 시 URL 재정의
          .addLogoutHandler(logoutHandler()) // 로그아웃 전 실행.
          .logoutSuccessUrl("/login")
          .invalidateHttpSession(true) // HTTP Session 초기화
          .deleteCookies("JSESSIONID") // 특정 쿠키 제거
        .and() // 403 예외처리 핸들링
          //.exceptionHandling().accessDeniedPage("/denied");
          .exceptionHandling()
          .accessDeniedHandler(accessDeniedHandler())
          .authenticationEntryPoint(authenticationEntryPoint());

    // 나모 에디터 'X-Frame-Options' to 'DENY' 오류로 인하여 추가.
    // https://computer-science-student.tistory.com/497
    http.headers().frameOptions().sameOrigin();
    // 나모에디터 이미지 업로드시 csrf 토큰을 추가할 수 없어서 예외처리.
    http.csrf().ignoringAntMatchers("/Crosseditor/uploadImg");
    return http.build();
  }

}
