package de.champonthis.buntspecht.security;

import java.util.Collections;
import java.util.List;

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.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
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 de.champonthis.buntspecht.businesslogic.UserManager;
import de.champonthis.buntspecht.security.handler.FormAuthenticationFailureHandler;
import de.champonthis.buntspecht.security.handler.OAuth2AuthenticationSuccessHandler;

@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityConfig {

	@Autowired
	private UserManager userManager;
	@Autowired
	private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
	@Autowired
	private DataSource dataSource;
	@Value("${loginUrl:/login}")
	private String loginUrl;
	@Value("${loginTargetUrl:/}")
	private String loginTargetUrl;
	@Value("${spring.security.oauth2.client:false}")
	private boolean oauth2Enabled;

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

		if (oauth2Enabled) {
			oAuth2AuthenticationSuccessHandler.setDefaultTargetUrl(loginTargetUrl);
			oAuth2AuthenticationSuccessHandler.setRememberMeServices(rememberMeServices());
		}

		http
				// crsf
				.csrf((csrf) -> csrf.disable())
				// cors
				// .cors().configurationSource(corsConfigurationSource()).and()
				// anonymous
				.anonymous((anonymous) -> anonymous.disable())
				// login
				.formLogin((formLogin) -> formLogin.loginPage("/login").defaultSuccessUrl(loginTargetUrl)
						.failureHandler(new FormAuthenticationFailureHandler(loginUrl)))
				// remember me
				.rememberMe((rememberMe) -> rememberMe.rememberMeServices(rememberMeServices()))
				// logout
				.logout((logout) -> logout.logoutUrl("/logout")
						.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)))
				// exception
				.exceptionHandling((exceptionHandling) -> exceptionHandling
						.defaultAuthenticationEntryPointFor(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
								new AntPathRequestMatcher("/api/**")));

		if (oauth2Enabled) {
			http.oauth2Login((oauth2Login) -> oauth2Login.successHandler(oAuth2AuthenticationSuccessHandler)
					.failureHandler(new SimpleUrlAuthenticationFailureHandler(loginUrl + "?externalError"))
					.loginPage("/login"));
		}

		return http.build();
	}

	@Bean
	public PersistentTokenRepository persistentTokenRepository() {
		JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
		tokenRepository.setDataSource(dataSource);
		return tokenRepository;
	}

	@Bean
	public RememberMeServices rememberMeServices() {
		PersistentTokenBasedRememberMeServices rememberMeServices = new LocalRememberMeServices("remember-me",
				userManager, persistentTokenRepository());
		return rememberMeServices;
	}

	@Bean
	public CorsConfigurationSource corsConfigurationSource() {
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOriginPatterns(List.of("*"));
		configuration.setAllowedMethods(Collections.singletonList("*"));
		configuration.setAllowCredentials(true);
		configuration.setAllowedHeaders(Collections.singletonList("*"));
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}
}
