package com.ease.gsms.server;

import com.ease.gsms.server.controllers.filters.ApiKeyAuthenticationFilter;
import com.ease.gsms.server.controllers.filters.ApiKeyAuthenticationProvider;
import com.ease.gsms.server.repositories.ApiKeyRepository;
import com.ease.gsms.server.util.RaceAvoidingPersistentTokenBasedRememberMeServices;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.sql.DataSource;

@EnableWebSecurity
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private ApiKeyRepository apiKeyRepository;

    @Autowired
    private ApiKeyAuthenticationProvider apiKeyAuthenticationProvider;

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
        auth
                .authenticationProvider(apiKeyAuthenticationProvider)
                .jdbcAuthentication()
                .dataSource(dataSource);
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl() {{
            setDataSource(dataSource);
        }};
        http
                .csrf()
                    .disable()
                .addFilterBefore(new ApiKeyAuthenticationFilter(authenticationManager(), apiKeyRepository, userDetailsService()), AnonymousAuthenticationFilter.class)
                .authorizeRequests()
                    .antMatchers("/").permitAll()
                    .antMatchers("/descarcare-android").permitAll()
                    .antMatchers("/css/**").permitAll()
                    .antMatchers("/js/**").permitAll()
                    .antMatchers("/img/**").permitAll()
                    .antMatchers("/apk/**").permitAll()
                    .antMatchers("/login*").permitAll()
                    .antMatchers("/admin/**").hasRole("ADMIN")
                    .anyRequest()
                    .authenticated()
                .and()
                    .formLogin()
                    .defaultSuccessUrl("/", false)
                .and()
                    .logout()
                    .invalidateHttpSession(true)
                    .logoutSuccessUrl("/")
                    .deleteCookies("JSESSIONID")
                .and()
                    .rememberMe()
                        .tokenRepository(tokenRepository)
                        .tokenValiditySeconds(365 * 24 * 3600) // a year, basically
                        .rememberMeServices(new RaceAvoidingPersistentTokenBasedRememberMeServices(
                                "gsmsk",
                                userDetailsService(),
                                tokenRepository
                        ))
                        .key("gsmsk")
                .and()
                    .exceptionHandling()
                    .defaultAuthenticationEntryPointFor(new Http403ForbiddenEntryPoint(), new AntPathRequestMatcher("/wsms/**"));
    }

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

}
