package es.tatvil.taiageweb.config;

import es.tatvil.taiageweb.servicio.UsuarioService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

/**
 * Configuración de seguridad de la aplicación.
 *
 * <p>Define las reglas de acceso HTTP, el proveedor de autenticación basado en
 * base de datos y la configuración del formulario de login/logout.</p>
 *
 * <ul>
 *   <li>Rutas públicas: {@code /}, {@code /login}, {@code /registro}, {@code /leyes},
 *       {@code /noticias}, {@code /webhook/stripe} y recursos estáticos.</li>
 *   <li>Panel admin ({@code /admin/**}): requiere {@code ROLE_ADMIN}.</li>
 *   <li>Contenido de pago ({@code /curso/**}, {@code /api/**}): requiere
 *       {@code ROLE_PAGADO} o {@code ROLE_ADMIN}.</li>
 * </ul>
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    /**
     * Crea el {@link PasswordEncoder} BCrypt usado en toda la aplicación.
     *
     * @return encoder BCrypt con factor de coste por defecto (10)
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * Crea el proveedor de autenticación DAO que consulta {@link UsuarioService}.
     *
     * @param service servicio que implementa {@link org.springframework.security.core.userdetails.UserDetailsService}
     * @param encoder encoder de contraseñas
     * @return proveedor configurado
     */
    @Bean
    public DaoAuthenticationProvider authProvider(UsuarioService service, PasswordEncoder encoder) {
        var provider = new DaoAuthenticationProvider(service);
        provider.setPasswordEncoder(encoder);
        return provider;
    }

    /**
     * Define la cadena de filtros de seguridad HTTP.
     *
     * @param http objeto de configuración de Spring Security
     * @return cadena de filtros construida
     * @throws Exception si la configuración es inválida
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.ignoringRequestMatchers("/webhook/stripe"))
            .headers(headers -> headers
                .frameOptions(frame -> frame.sameOrigin())
            )
            .authorizeHttpRequests(auth -> auth
                // Recursos públicos
                .requestMatchers(
                    "/", "/inicio", "/login", "/registro",
                    "/leyes", "/noticias", "/acceso-denegado", "/error",
                    "/webhook/stripe",
                    "/css/**", "/js/**", "/images/**", "/leyes/**", "/audios/**", "/podcast.xml", "/favicon.ico"
                ).permitAll()
                // Panel de administración
                .requestMatchers("/admin/**").hasRole("ADMIN")
                // Contenido de pago
                .requestMatchers("/curso", "/curso/**", "/planning", "/esquema.html", "/flashcards.html", "/flashcards/**", "/api/**").hasAnyRole("PAGADO", "ADMIN")
                // Cualquier otra ruta requiere autenticación
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .usernameParameter("email")       // el campo del form se llama "email"
                .defaultSuccessUrl("/", false)
                .failureUrl("/login?error")
                .permitAll()
            )
            .logout(logout -> logout
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login?logout")
                .permitAll()
            )
            .exceptionHandling(ex -> ex
                .accessDeniedPage("/acceso-denegado")
            );
        return http.build();
    }
}
