package org.lingniu.idp.config;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.lingniu.idp.enums.CustomScopes;
import org.lingniu.idp.security.filter.IdpAuthenticationFilter;
import org.lingniu.idp.security.granttype.SmsCodeGrantType;
import org.lingniu.idp.security.handler.Oauth2CodeSuccessHandler;
import org.lingniu.idp.service.core.UserDetailsServiceImpl;
import org.lingniu.idp.service.core.login.IdpTokenService;
import org.lingniu.idp.service.core.login.LoginService;
import org.lingniu.idp.service.core.login.RedisAccessTokenService;
import org.lingniu.idp.utils.jwt.Jwks;
import org.lingniu.idp.utils.jwt.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.Duration;
import java.util.UUID;
import java.util.function.Function;

import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer.authorizationServer;

@Configuration
public class AuthorizationServerConfig {
    
//    @Autowired
//    private SmsCodeAuthenticationProvider smsCodeAuthenticationProvider;
    private static final String CUSTOM_CONSENT_PAGE_URI = "http://localhost/oauth2/consent";
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private IdpTokenService idpTokenService;
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private OidcUserInfoMapper oidcUserInfoMapper;

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = authorizationServer();
        http
                .securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
                .with(authorizationServerConfigurer, (authorizationServer) ->
                        authorizationServer
                                .authorizationEndpoint(authorizationEndpoint ->
                                        authorizationEndpoint.consentPage(CUSTOM_CONSENT_PAGE_URI)
                                                .authorizationResponseHandler(new Oauth2CodeSuccessHandler())
                                )
                                .tokenEndpoint(oAuth2TokenEndpointConfigurer -> {})
                                .oidc(Customizer.withDefaults())
                                .oidc(oidcConfigurer ->
                                        oidcConfigurer.userInfoEndpoint(oidcUserInfoEndpointConfigurer ->
                                                oidcUserInfoEndpointConfigurer.userInfoMapper(oidcUserInfoMapper)
                                                )
                                )	// Enable OpenID Connect 1.0
                )
                .addFilterAfter(jwtAuthenticationFilter(), LogoutFilter.class)
                .oauth2ResourceServer(oauth2ResourceServer ->
                        oauth2ResourceServer.jwt(Customizer.withDefaults()))
                .authorizeHttpRequests((authorize) ->
                        authorize.anyRequest().authenticated()
                )
                // Redirect to the /login page when not authenticated from the authorization endpoint
                // NOTE: DefaultSecurityConfig is configured with formLogin.loginPage("/login")
                .exceptionHandling((exceptions) -> exceptions
                        .defaultAuthenticationEntryPointFor(
                                new LoginUrlAuthenticationEntryPoint("http://localhost/login"),
                                new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                        )
                );
        return http.build();
    }
    @Bean
    public JdbcRegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
        RegisteredClient client = RegisteredClient.withId("68dc155a-1034-4cd8-91dc-fca206e2f6d3")
                .clientId("2c6f1d9ff78641c78d72a848")
                //l3X95am7RaX8-Uu9-5nDYmD9-OFU-8_GHmkfnKfaS_0
                .clientSecret("{bcrypt}$2a$10$JDoQUK5aWvYiZZDpgSC8k.qPGfqbYtXAF5H1IW/e2quGWA8Zr6ffq")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .authorizationGrantType(SmsCodeGrantType.SMS_CODE)  // 添加自定义Grant Type
                .clientName("PORTAL")
                .redirectUri("http://localhost:81/callback")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .scope(CustomScopes.PERMS.name().toLowerCase())
                .tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofHours(12)).refreshTokenTimeToLive(Duration.ofDays(7)).build())
                .scope("message.read")
                .scope("message.write")
                .build();
        RegisteredClient demo = RegisteredClient.withId("c81008aa-b33f-4ebc-bba7-3c6d0eee23b9")
                .clientId("b55c88c20db94790a60a5075")
                //l3X95am7RaX8-Uu9-5nDYmD9-OFU-8_GHmkfnKfaS_0
                .clientSecret("{bcrypt}$2a$10$gLsAsObi5ffOddmkf5YchOtT3olfcAmSTt75X6H/mHBVYf8DXnIwi")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .authorizationGrantType(SmsCodeGrantType.SMS_CODE)  // 添加自定义Grant Type
                .clientName("DEMO")
                .redirectUri("http://localhost:9506/oauth2/callback")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .scope(CustomScopes.PERMS.name().toLowerCase())
                .tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofHours(12)).refreshTokenTimeToLive(Duration.ofDays(7)).build())
                .scope("read")
                .scope("write")
                .build();
        // Save registered client's in db as if in-memory
        JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
        registeredClientRepository.save(client);
        registeredClientRepository.save(demo);
        return registeredClientRepository;
    }

//    @Bean
//    public OAuth2TokenGenerator<?> tokenGenerator(CustomAccessTokenGenerator accessTokenGenerator,OAuth2RefreshTokenGenerator refreshTokenGenerator) {
//        CustomAccessTokenGenerator accessTokenGenerator = new CustomAccessTokenGenerator();
//        OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
//        return new DelegatingOAuth2TokenGenerator(
//                accessTokenGenerator, refreshTokenGenerator
//        );
//    }
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate) {
    return new JdbcOAuth2AuthorizationService(jdbcTemplate,registeredClientRepository(jdbcTemplate));
}

    /**
     * 自定义JWT认证过滤器
     */
    @Bean
    public IdpAuthenticationFilter jwtAuthenticationFilter() {
        return new IdpAuthenticationFilter(idpTokenService,userDetailsService);
    }
    /**
     * 主JWK源，使用应用配置的RSA密钥
     */
    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        try {
            // 从JwtUtil中获取密钥
            RSAPublicKey publicKey = jwtUtil.getPublicKey();
            RSAPrivateKey privateKey = jwtUtil.getPrivateKey();

            // 创建RSA JWK，包含私钥（用于签名）
            RSAKey rsaKey = Jwks.generateRsa(
                    publicKey,
                    privateKey,
                    "idp" // 密钥ID
            );

            JWKSet jwkSet = new JWKSet(rsaKey);

            // 使用ImmutableJWKSet包装
            return new ImmutableJWKSet<>(jwkSet);

        } catch (Exception e) {
            throw new RuntimeException("初始化JWK源失败", e);
        }
    }
    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }
}