/**
 * 
 */
package de.bstly.board.security;

import java.util.Collections;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import com.google.common.collect.Lists;

import de.bstly.board.businesslogic.UserManager;

/**
 * The Class SecurityConfig.
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private UserManager localUserManager;
	@Autowired
	private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
	@Autowired
	private DataSource dataSource;
	@Value("${loginUrl:/login}")
	private String loginUrl;
	@Value("${loginTargetUrl:/}")
	private String loginTargetUrl;

	/*
	 * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
	 */
	/*
	 * @see org.springframework.security.config.annotation.web.configuration.
	 * WebSecurityConfigurerAdapter#configure(org.springframework.security.config.
	 * annotation.web.builders.HttpSecurity)
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {

		oAuth2AuthenticationSuccessHandler.setDefaultTargetUrl(loginTargetUrl);
		oAuth2AuthenticationSuccessHandler.setRememberMeServices(rememberMeServices());

		http
				// crsf
				.csrf().disable()
				// cors
				// .cors().configurationSource(corsConfigurationSource()).and()
				// anonymous
				.anonymous().disable()
				// login
				.formLogin().loginPage("/login").defaultSuccessUrl(loginTargetUrl)
				.failureHandler(new SimpleUrlAuthenticationFailureHandler(loginUrl
						+ "?error"))
				.and()
				// remember me
				.rememberMe().rememberMeServices(rememberMeServices()).and()
				// logout
				.logout().logoutUrl("/logout")
				.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK))
				.and()
				// exception
				.exceptionHandling()
				.defaultAuthenticationEntryPointFor(
						new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
						new AntPathRequestMatcher("/api/**"))
				.and()
				// oidc
				.oauth2Login().successHandler(oAuth2AuthenticationSuccessHandler)
				.failureHandler(new SimpleUrlAuthenticationFailureHandler(loginUrl
						+ "?externalError"))
				.loginPage("/login");
	}

	/**
	 * Password encoder.
	 *
	 * @return the argon 2 password encoder
	 */
	@Bean(name = "passwordEncoder")
	public Argon2PasswordEncoder passwordEncoder() {
		return new Argon2PasswordEncoder();
	}

	/**
	 * Persistent token repository.
	 *
	 * @return the persistent token repository
	 */
	@Bean
	public PersistentTokenRepository persistentTokenRepository() {
		JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
		tokenRepository.setDataSource(dataSource);
		return tokenRepository;
	}

	/**
	 * Remember me services.
	 *
	 * @return the remember me services
	 */
	@Bean
	public RememberMeServices rememberMeServices() {
		PersistentTokenBasedRememberMeServices rememberMeServices = new LocalRememberMeServices(
				"remember-me", localUserManager, persistentTokenRepository());
		return rememberMeServices;
	}

	/**
	 * Cors configuration source.
	 *
	 * @return the cors configuration source
	 */
	@Bean
	public CorsConfigurationSource corsConfigurationSource() {
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOriginPatterns(Lists.newArrayList("localhost", "http://localhost",
				"http://localhost:4200", "https://board.bstly.lh8.de"));
		configuration.setAllowedMethods(Collections.singletonList("*"));
		configuration.setAllowCredentials(true);
		configuration.setAllowedHeaders(Collections.singletonList("*"));
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}
}
