package com.ffii.core.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
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.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.servlet.LocaleResolver;

import com.ffii.core.common.mobile.service.AccessTokenService;
import com.ffii.core.config.auth.FailureHandler;
import com.ffii.core.config.auth.SuccessHandler;
import com.ffii.core.config.filter.TokenAuthFilter;
import com.ffii.core.user.service.UserService;

/** @author Fung */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

  private SuccessHandler successHandler;
  private FailureHandler failureHandler;

  private final String AUTH_HEADER_DEVICE_ID;
  private final String AUTH_HEADER_ACCESS_TOKEN;
  private AccessTokenService accessTokenService;
  private UserService userService;
  private LocaleResolver localeResolver;

  public SecurityConfig(
      SuccessHandler successHandler,
      FailureHandler failureHandler,

      @Value("${app.mobile.auth-header.deviceId:X-ATT-DeviceId}") String authHeaderDeviceId,
      @Value("${app.mobile.auth-header.accessToken:X-ATT-Access-Token}") String authHeaderAccessToken,
      AccessTokenService accessTokenService,
      UserService userService,
      LocaleResolver localeResolver) {
    this.successHandler = successHandler;
    this.failureHandler = failureHandler;

    this.AUTH_HEADER_DEVICE_ID = authHeaderDeviceId;
    this.AUTH_HEADER_ACCESS_TOKEN = authHeaderAccessToken;
    this.accessTokenService = accessTokenService;
    this.userService = userService;
    this.localeResolver = localeResolver;
  }

  @Bean
  public WebSecurityCustomizer webSecurityCustomizer() {
    return (web) -> web.ignoring().antMatchers(
        "/static/public/**",
        "/swagger**/**", "/v3/**" // swagger
    );
  }

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
        .csrf().disable()
        .requestCache().disable()
        .httpBasic(httpBasic -> httpBasic.authenticationEntryPoint(
            (request, response, authException) -> response.sendError(HttpStatus.UNAUTHORIZED.value())))
        .authorizeRequests(authorizeRequests -> authorizeRequests
            .antMatchers("/", "/public/**").permitAll()
            .anyRequest().authenticated())
        .formLogin(formLogin -> formLogin
            .loginPage("/")
            .loginProcessingUrl("/login")
            .successHandler(successHandler)
            .failureHandler(failureHandler))
        .sessionManagement(sessionManagement -> sessionManagement.maximumSessions(1))
        .addFilterBefore(new TokenAuthFilter(
            this.AUTH_HEADER_DEVICE_ID,
            this.AUTH_HEADER_ACCESS_TOKEN,
            this.accessTokenService,
            this.userService,
            this.localeResolver), BasicAuthenticationFilter.class)
        .build();
  }

}