package sklep.security;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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 jakarta.servlet.DispatcherType;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
	@Autowired
	// Spring wstrzyknie tu domyślne połączenie z bazą danych - to sonfigurowane w application.properties
	private DataSource dataSource;
	
	// To pomogło mi napisać poprawną konfigurację w "nowym stylu":
	// https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html
	// Kluczowe było dodanie linii z DispatcherType.FORWARD → bez tego Spring wymagał autentykacji na etapie przechodzenia do szablonu jsp (FORWARD) lub wyświetlenia błędu
	@Bean
	SecurityFilterChain setHttpSecurity(HttpSecurity httpSecurity) throws Exception {
		httpSecurity
			.authorizeHttpRequests((authz) -> authz
					// zezwalamy na działanie przekierowań wewnętrznych (szablony) i błędów
					.dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
					.requestMatchers("/", "/whoami", "/*.css").permitAll()
					.requestMatchers("/hello", "/time").permitAll()
					.requestMatchers("/alt?/**").authenticated() // zalogowany jako ktokolwiek
					.requestMatchers("/products/new", "/products/*/edit").hasAuthority("ROLE_manager")
					.requestMatchers("/products/**").permitAll() // kolejność reguł ma znaczenie
					.requestMatchers("/customers/new", "/customers/*/edit").hasRole("manager") // skrót na hasAuthority("ROLE_...")
					.requestMatchers("/customers/**").authenticated()
					.anyRequest().denyAll() // dobra praktyka - odrzucanie pozostałych zapytań; Spring domyślnie wymagałby "authenticated"
				)
			.formLogin();
		return httpSecurity.build();
	}
	
	@Bean
	AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration, ApplicationContext applicationContext) throws Exception {
        ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
			public <O> O postProcess(O object) {
				return object;
			}
		};
		
		return authenticationConfiguration.authenticationManagerBuilder(objectPostProcessor, applicationContext)
				.jdbcAuthentication()
				.dataSource(dataSource)
				// mamy podać zapytanie SQL, które pozwoli Springowi odczytać informacje o userze na podstawie nazwy usera
		        // w wyniku ma zwrócić rekord z trzeba kolumnami: nazwa, hasło, czy aktywny (0/1)
		        .usersByUsernameQuery("SELECT username, password, enabled FROM spring_accounts WHERE username = ?")
		        // dla użytkownika zwraca info o uprawnieniach (rolach) danego użytkownika; wynik może składać się z wielu rekordów
		        .authoritiesByUsernameQuery("SELECT username, role FROM spring_account_roles WHERE username = ?")	
			.and()
			.build();
	}
	
}
