/*
 * Copyright 2020-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.lingniu.sdk.config;

import org.lingniu.sdk.filter.IdpAuthenticationFilter;
import org.lingniu.sdk.handler.LoginSuccessHandler;
import org.lingniu.sdk.handler.LogoutIdpSuccessHandler;
import org.lingniu.sdk.handler.RedirectHandler;
import org.lingniu.sdk.service.RedisOAuth2AuthorizedClientService;
import org.lingniu.sdk.service.TokenService;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * @author Joe Grandja
 * @author Dmitriy Dubson
 * @author Steve Riesenberg
 * @since 0.0.1
 */
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
@EnableConfigurationProperties({OAuth2ClientProperties.class})
@Configuration
public class SecurityConfig {
	private final LoginSuccessHandler loginSuccessHandler;
	private final OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository;
	private final RedisOAuth2AuthorizedClientService redisOAuth2AuthorizedClientService;
	private final TokenService tokenService;
	private final LogoutIdpSuccessHandler logoutIdpSuccessHandler;
	private final RedirectHandler redirectHandler;

    public SecurityConfig(LoginSuccessHandler loginSuccessHandler, OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository, RedisOAuth2AuthorizedClientService redisOAuth2AuthorizedClientService, TokenService tokenService, LogoutIdpSuccessHandler logoutIdpSuccessHandler, RedirectHandler redirectHandler) {
        this.loginSuccessHandler = loginSuccessHandler;
        this.oAuth2AuthorizedClientRepository = oAuth2AuthorizedClientRepository;
        this.redisOAuth2AuthorizedClientService = redisOAuth2AuthorizedClientService;
        this.tokenService = tokenService;
        this.logoutIdpSuccessHandler = logoutIdpSuccessHandler;
        this.redirectHandler = redirectHandler;
    }

    // @formatter:off
	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtDecoder jwtDecoder) throws Exception {
		http
				.csrf(AbstractHttpConfigurer::disable)
			.authorizeHttpRequests(authorize ->
				authorize
					.anyRequest().authenticated()
			)
			.oauth2Login(oauth2->
				oauth2.authorizationEndpoint(authorization ->authorization.authorizationRedirectStrategy(redirectHandler))
						.successHandler(loginSuccessHandler)
						.authorizedClientRepository(oAuth2AuthorizedClientRepository)
						.authorizedClientService(redisOAuth2AuthorizedClientService)
			)
				.exceptionHandling(withDefaults())
				.addFilterBefore(new IdpAuthenticationFilter(tokenService),UsernamePasswordAuthenticationFilter.class)
				.oauth2ResourceServer(resource ->resource.jwt(withDefaults()))
			.logout(httpSecurityLogoutConfigurer ->
					httpSecurityLogoutConfigurer
							.logoutSuccessHandler(logoutIdpSuccessHandler)
			);
		return http.build();
	}
	// @formatter:on

	@Bean
	public JwtDecoder jwtDecoder(OAuth2ClientProperties oAuth2ClientProperties) {
		RestOperations rest = new RestTemplate();

		return NimbusJwtDecoder
				.withJwkSetUri(oAuth2ClientProperties.getProvider().get("idp").getJwkSetUri())
				.restOperations(rest)
				.build();
	}

}
