package ru.ulstu.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import ru.ulstu.user.controller.UserController;
import ru.ulstu.user.model.UserRoleConstants;
import ru.ulstu.user.service.UserService;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);

    @Value("${server.http.port}")
    private int httpPort;
    @Value("${server.port}")
    private int httpsPort;

    private final UserService userService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;
    private final AuthenticationSuccessHandler authenticationSuccessHandler;
    private final LogoutSuccessHandler logoutSuccessHandler;
    private final ApplicationProperties applicationProperties;

    public SecurityConfiguration(UserService userService,
                                 BCryptPasswordEncoder bCryptPasswordEncoder,
                                 AuthenticationSuccessHandler authenticationSuccessHandler,
                                 LogoutSuccessHandler logoutSuccessHandler,
                                 ApplicationProperties applicationProperties) {
        this.userService = userService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
        this.authenticationSuccessHandler = authenticationSuccessHandler;
        this.logoutSuccessHandler = logoutSuccessHandler;
        this.applicationProperties = applicationProperties;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .disable();
        if (applicationProperties.isDevMode()) {
            log.debug("Security disabled");
            http.authorizeRequests()
                    .anyRequest()
                    .permitAll();
            http.anonymous()
                    .principal("developer")
                    .authorities(UserRoleConstants.ADMIN);
        } else {
            log.debug("Security enabled");
            http.authorizeRequests()
                    .antMatchers(UserController.ACTIVATE_URL).permitAll()
                    .antMatchers(Constants.PASSWORD_RESET_REQUEST_PAGE).permitAll()
                    .antMatchers(Constants.PASSWORD_RESET_PAGE).permitAll()
                    .antMatchers(UserController.URL + UserController.REGISTER_URL).permitAll()
                    .antMatchers(UserController.URL + UserController.ACTIVATE_URL).permitAll()
                    .antMatchers(UserController.URL + UserController.PASSWORD_RESET_REQUEST_URL).permitAll()
                    .antMatchers(UserController.URL + UserController.PASSWORD_RESET_URL).permitAll()
                    .antMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN)
                    .anyRequest().authenticated()
                    .and()
                    .formLogin()
                    .loginPage("/login")
                    .successHandler(authenticationSuccessHandler)
                    .permitAll()
                    .and()
                    .logout()
                    .logoutSuccessHandler(logoutSuccessHandler)
                    .logoutSuccessUrl(Constants.LOGOUT_URL)
                    .invalidateHttpSession(false)
                    .clearAuthentication(true)
                    .deleteCookies(Constants.COOKIES_NAME)
                    .permitAll();
        }
        http.portMapper()
                .http(httpPort)
                .mapsTo(httpsPort)
                .and()
                .requiresChannel()
                .anyRequest()
                .requiresSecure();
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .antMatchers("/css/**")
                .antMatchers("/js/**")
                .antMatchers("/templates/**")
                .antMatchers("/webjars/**");
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        if (applicationProperties.isDevMode()) {
            return;
        }
        try {
            auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
        } catch (Exception e) {
            throw new BeanInitializationException("Security configuration failed", e);
        }
    }
}
