package com.imprimelibros.erp.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

import com.imprimelibros.erp.users.UserServiceImpl;

import jakarta.servlet.http.HttpServletRequest;

@Configuration
public class SecurityConfig {

    private final DataSource dataSource;

    public SecurityConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    // ========== Beans base ==========

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

    // Remember-me (tabla persistent_logins)
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl repo = new JdbcTokenRepositoryImpl();
        repo.setDataSource(dataSource);
        // repo.setCreateTableOnStartup(true); // solo 1ª vez si necesitas crear la
        // tabla
        return repo;
    }

    // Provider que soporta UsernamePasswordAuthenticationToken
    private static RequestMatcher pathStartsWith(String... prefixes) {
        return new RequestMatcher() {
            @Override
            public boolean matches(HttpServletRequest request) {
                String uri = request.getRequestURI();
                if (uri == null)
                    return false;
                for (String p : prefixes) {
                    if (uri.startsWith(p))
                        return true;
                }
                return false;
            }
        };
    }

    @Bean
    public SecurityFilterChain securityFilterChain(
            HttpSecurity http,
            @Value("${security.rememberme.key}") String keyRememberMe,
            UserDetailsService userDetailsService,
            PersistentTokenRepository tokenRepo,
            PasswordEncoder passwordEncoder, UserServiceImpl userServiceImpl) throws Exception {

        DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userServiceImpl);
        provider.setPasswordEncoder(passwordEncoder);
        http.authenticationProvider(provider);
        http
                .authenticationProvider(provider)

                .sessionManagement(session -> session
                        .invalidSessionUrl("/login?expired")
                        .maximumSessions(1))

                // Ignora CSRF para tu recurso público (sin Ant/Mvc matchers)
                .csrf(csrf -> csrf
                        .ignoringRequestMatchers(pathStartsWith("/presupuesto/public/")))

                // ====== RequestCache: sólo navegaciones HTML reales ======
                .requestCache(rc -> {
                    HttpSessionRequestCache cache = new HttpSessionRequestCache();

                    // Navegación HTML (por tipo o por cabecera Accept)
                    RequestMatcher htmlPage = new OrRequestMatcher(
                            new MediaTypeRequestMatcher(MediaType.TEXT_HTML),
                            new MediaTypeRequestMatcher(MediaType.APPLICATION_XHTML_XML),
                            new RequestHeaderRequestMatcher("Accept", "text/html"));

                    // No AJAX
                    RequestMatcher nonAjax = new NegatedRequestMatcher(
                            new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));

                    // Excluir sondas .well-known
                    RequestMatcher notWellKnown = new NegatedRequestMatcher(pathStartsWith("/.well-known/"));

                    // Excluir estáticos: comunes + tu /assets/**
                    RequestMatcher notStatic = new AndRequestMatcher(
                            new NegatedRequestMatcher(PathRequest.toStaticResources().atCommonLocations()),
                            new NegatedRequestMatcher(pathStartsWith("/assets/")));

                    cache.setRequestMatcher(new AndRequestMatcher(htmlPage, nonAjax, notStatic, notWellKnown));
                    rc.requestCache(cache);
                })
                // ========================================================

                .authorizeHttpRequests(auth -> auth
                        // Aquí usa patrones String (no deprecados)
                        .requestMatchers(
                                "/",
                                "/login",
                                "/signup",
                                "/verify",
                                "/reset-password",
                                "/assets/**",
                                "/css/**",
                                "/js/**",
                                "/images/**",
                                "/public/**",
                                "/presupuesto/public/**",
                                "/error",
                                "/favicon.ico",
                                "/.well-known/**" // opcional
                        ).permitAll()
                        .requestMatchers("/users/**").hasAnyRole("SUPERADMIN", "ADMIN")
                        .anyRequest().authenticated())

                .formLogin(login -> login
                        .loginPage("/login").permitAll()
                        .loginProcessingUrl("/login")
                        .usernameParameter("username")
                        .passwordParameter("password")
                        .defaultSuccessUrl("/", false) // respeta SavedRequest (ya filtrada)
                        .failureUrl("/login?error"))

                .rememberMe(rm -> rm
                        .key(keyRememberMe)
                        .rememberMeParameter("remember-me")
                        .rememberMeCookieName("IMPRIMELIBROS_REMEMBER")
                        .tokenValiditySeconds(60 * 60 * 24 * 2)
                        .userDetailsService(userDetailsService)
                        .tokenRepository(tokenRepo))

                .logout(logout -> logout
                        .logoutUrl("/logout")
                        .logoutSuccessUrl("/")
                        .invalidateHttpSession(true)
                        .deleteCookies("JSESSIONID", "IMPRIMELIBROS_REMEMBER")
                        .permitAll());

        return http.build();
    }
}
