package com.example.dateplanner.configurations;

import com.example.dateplanner.configurations.filters.ErrorHandlingFilter;
import com.example.dateplanner.configurations.handlers.JwtAuthenticationConverter;
import com.example.dateplanner.configurations.handlers.JwtAuthenticationManager;
import com.example.dateplanner.configurations.handlers.JwtAuthenticationSuccessHandler;
import com.example.dateplanner.configurations.handlers.JwtLogoutSuccessHandler;
import com.example.dateplanner.repositories.AppUserRepository;
import com.example.dateplanner.services.JwtService;
import com.example.dateplanner.services.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService;
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.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository;
import org.springframework.security.web.server.csrf.ServerCsrfTokenRequestAttributeHandler;
import org.springframework.security.web.server.savedrequest.ServerRequestCache;
import org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
import reactor.core.publisher.Mono;
import java.net.URI;

@Slf4j
@Configuration
@EnableWebFluxSecurity
@RequiredArgsConstructor
@EnableReactiveMethodSecurity
public class SecurityConfig {

    private final AppUserRepository userRepository;
    private final JwtService jwtService;

    // Цепочка для API (Basic Auth) - CSRF отключаем
    @Bean
    @Order(1)
    public SecurityWebFilterChain apiFilterChain(ServerHttpSecurity http) {
        return http
                .securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**"))
                .authorizeExchange(exchange -> exchange
                        .pathMatchers("/api/mobile/login").permitAll()
                        .anyExchange().authenticated()
                )
                .httpBasic(Customizer.withDefaults())
                .formLogin(ServerHttpSecurity.FormLoginSpec::disable)
                .csrf(ServerHttpSecurity.CsrfSpec::disable) // CSRF отключаем для API
                .authenticationManager(reactiveAuthenticationManager())
                .build();
    }

    // Цепочка для веб-интерфейса (Form Login) - CSRF включаем
    @Bean
    @Order(2)
    public SecurityWebFilterChain webFilterChain(ServerHttpSecurity http) {
        ServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();
        requestHandler.setTokenFromMultipartDataEnabled(true);

        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(authenticationManager());
        authenticationWebFilter.setServerAuthenticationConverter(authenticationConverter());

        ErrorHandlingFilter errorHandlingFilter = new ErrorHandlingFilter();

        return http
                .securityMatcher(new PathPatternParserServerWebExchangeMatcher("/account/**"))
                .csrf(csrf -> csrf.csrfTokenRequestHandler(requestHandler).csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))
                .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .addFilterAfter(errorHandlingFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .authorizeExchange(exchange -> exchange
                        .pathMatchers("/account/login").permitAll()
                        .anyExchange().authenticated()
                )
                .formLogin(loginSpec -> loginSpec.loginPage("/account/login").authenticationSuccessHandler(authenticationSuccessHandler()))
                .logout(logoutSpec -> logoutSpec.logoutSuccessHandler(logoutSuccessHandler()))
                .requestCache(requestCacheSpec -> requestCacheSpec.requestCache(serverRequestCache()))
                .build();
    }

    // Цепочка для всех остальных путей (публичные)
    @Bean
    @Order(3)
    public SecurityWebFilterChain defaultFilterChain(ServerHttpSecurity http) {
        return http
                .securityMatcher(new PathPatternParserServerWebExchangeMatcher("/**"))
                .authorizeExchange(exchange -> exchange
                        .anyExchange().permitAll()
                )
//                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .build();
    }

    @Bean
    public ServerRequestCache serverRequestCache() {
        return new WebSessionServerRequestCache();
    }

    @Bean
    public JwtAuthenticationManager authenticationManager() {
        return new JwtAuthenticationManager(jwtService, userService(), passwordEncoder());
    }

    @Bean
    public JwtAuthenticationSuccessHandler authenticationSuccessHandler(){
        return new JwtAuthenticationSuccessHandler(jwtService,serverRequestCache());
    }

    @Bean
    public JwtLogoutSuccessHandler logoutSuccessHandler(){
        JwtLogoutSuccessHandler logoutSuccessHandler = new JwtLogoutSuccessHandler();
        logoutSuccessHandler.setLogoutSuccessUrl(URI.create("/"));
        return logoutSuccessHandler;
    }

    @Bean
    public JwtAuthenticationConverter authenticationConverter(){
        return new JwtAuthenticationConverter();
    }

    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager() {
        UserDetailsRepositoryReactiveAuthenticationManager manager = new UserDetailsRepositoryReactiveAuthenticationManager(userService());
        manager.setPasswordEncoder(passwordEncoder());
        manager.setUserDetailsPasswordService(userDetailsPasswordService());
        return manager;
    }

    @Bean
    public ReactiveUserDetailsPasswordService userDetailsPasswordService() {
        return new ReactiveUserDetailsPasswordService() {
            @Override
            public Mono<UserDetails> updatePassword(UserDetails user, String newPassword) {
                return userRepository.findByPhone(user.getUsername()).flatMap(appUser -> {
                    appUser.setPassword(passwordEncoder().encode(newPassword));
                    return userRepository.save(appUser);
                });
            }
        };
    }

    @Bean
    public UserService userService(){
        return new UserService(userRepository,passwordEncoder());
    }

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