package demo;

import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
public class OAuth2LoginSecurityConfig {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http, OAuth2LoginAccountService accounts,
                                    @Value("${app.spa-url}") String spaUrl) throws Exception {

        OidcUserService delegateOidc = new OidcUserService();
        OAuth2UserService<OidcUserRequest, OidcUser> oidcMapper = request ->
                accounts.mapOidc(request.getClientRegistration().getRegistrationId(), delegateOidc.loadUser(request));

        DefaultOAuth2UserService delegateGithub = new DefaultOAuth2UserService();
        OAuth2UserService<OAuth2UserRequest, OAuth2User> githubMapper = request -> {
            OAuth2User user = delegateGithub.loadUser(request);
            return "github".equals(request.getClientRegistration().getRegistrationId())
                    ? accounts.mapGithub(user) : user;
        };

        http
            .authorizeHttpRequests(a -> a
                .requestMatchers("/", "/error").permitAll()
                .anyRequest().authenticated())
            .oauth2Login(o -> o
                .defaultSuccessUrl(spaUrl, true)            // always land back on the SPA
                .userInfoEndpoint(u -> u
                    .oidcUserService(oidcMapper)             // Google (OIDC)
                    .userService(githubMapper)))             // GitHub (plain OAuth2)
            .exceptionHandling(e -> e.defaultAuthenticationEntryPointFor(
                new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),   // XHR gets 401, not a 302 to /login
                PathPatternRequestMatcher.withDefaults().matcher("/api/**")))
            .logout(l -> l.logoutSuccessUrl(spaUrl))
            .cors(Customizer.withDefaults())
            .csrf(c -> c.spa());                              // cookie token + header, SPA-friendly
        return http.build();
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource(@Value("${app.spa-url}") String spaUrl) {
        CorsConfiguration cfg = new CorsConfiguration();
        cfg.setAllowedOrigins(List.of(spaUrl.replaceAll("/$", "")));
        cfg.setAllowedMethods(List.of("GET", "POST"));
        cfg.setAllowedHeaders(List.of("X-XSRF-TOKEN", "Content-Type"));
        cfg.setAllowCredentials(true);                        // lets the browser send the session cookie
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", cfg);
        return source;
    }
}
