package ru.serega6531.packmate.configuration;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import ru.serega6531.packmate.properties.PackmateProperties;
import ru.serega6531.packmate.security.FakeAdminAuthFilter;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Configuration
@EnableWebSecurity
@Slf4j
public class SecurityConfiguration {

    @Bean
    public InMemoryUserDetailsManager userDetailsService(PackmateProperties properties, PasswordEncoder passwordEncoder) {
        List<UserDetails> users = new ArrayList<>();

        users.add(User.builder()
                .username(properties.web().accountLogin())
                .password(passwordEncoder.encode(properties.web().accountPassword()))
                .roles("USER")
                .build());

        Optional.ofNullable(properties.web().fakeAdmin())
                .filter(PackmateProperties.FakeAdminProperties::enabled)
                .ifPresent(fakeAdmin -> users.add(User.builder()
                        .username("admin")
                        .password(passwordEncoder.encode("admin"))
                        .roles("FAKE")
                        .build()));

        return new InMemoryUserDetailsManager(users);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, FakeAdminAuthFilter fakeAdminAuthFilter) throws Exception {
        return http.csrf()
                .disable()
                .authorizeHttpRequests((auth) ->
                        auth.requestMatchers("/site.webmanifest", "/fake-admin/**", "/fake/**", "/api/fake/**")
                                .permitAll()
                                .requestMatchers("/api/**", "/ws/**")
                                .hasRole("USER")
                                .anyRequest()
                                .authenticated()
                )
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .httpBasic()
                .and()
                .headers()
                .frameOptions()
                .sameOrigin()
                .and()
                .addFilterAfter(fakeAdminAuthFilter, BasicAuthenticationFilter.class)
                .build();
    }

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

    @EventListener
    public void authenticationFailed(AuthenticationFailureBadCredentialsEvent e) {
        log.info("Login failed for user {}, password {}",
                e.getAuthentication().getPrincipal(), e.getAuthentication().getCredentials());
    }

}
