/*
 * Copyright 2004-present 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.springframework.security.config.annotation.method.configuration;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AutoProxyRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
 * @author Rob Winch
 * @author Evgeniy Cheban
 * @since 5.0
 */
class ReactiveMethodSecuritySelector implements ImportSelector {

	private static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = "org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor";

	private static final boolean isDataPresent = ClassUtils
		.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);

	private static final boolean isObservabilityPresent = ClassUtils
		.isPresent("io.micrometer.observation.ObservationRegistry", null);

	private final ImportSelector autoProxy = new AutoProxyRegistrarSelector();

	@Override
	public String[] selectImports(AnnotationMetadata importMetadata) {
		if (!importMetadata.hasAnnotation(EnableReactiveMethodSecurity.class.getName())) {
			return new String[0];
		}
		EnableReactiveMethodSecurity annotation = importMetadata.getAnnotations()
			.get(EnableReactiveMethodSecurity.class)
			.synthesize();
		List<String> imports = new ArrayList<>(Arrays.asList(this.autoProxy.selectImports(importMetadata)));
		if (annotation.useAuthorizationManager()) {
			imports.add(ReactiveAuthorizationManagerMethodSecurityConfiguration.class.getName());
		}
		else {
			Assert.state(
					ClassUtils.isPresent(METHOD_SECURITY_METADATA_SOURCE_ADVISOR, ClassUtils.getDefaultClassLoader()),
					() -> "@EnableReactiveMethodSecurity(useAuthorizationManager = false) requires the "
							+ "spring-security-access dependency on the classpath. Please add spring-security-access, "
							+ "or use the default useAuthorizationManager = true which does not require it.");
			imports.add(ReactiveMethodSecurityConfiguration.class.getName());
		}
		if (isDataPresent) {
			imports.add(AuthorizationProxyDataConfiguration.class.getName());
		}
		if (isObservabilityPresent) {
			imports.add(ReactiveMethodObservationConfiguration.class.getName());
		}
		imports.add(AuthorizationProxyConfiguration.class.getName());
		return imports.toArray(new String[0]);
	}

	private static final class AutoProxyRegistrarSelector
			extends AdviceModeImportSelector<EnableReactiveMethodSecurity> {

		private static final String[] IMPORTS = new String[] { AutoProxyRegistrar.class.getName(),
				MethodSecurityAdvisorRegistrar.class.getName() };

		@Override
		protected String[] selectImports(@NonNull AdviceMode adviceMode) {
			if (adviceMode == AdviceMode.PROXY) {
				return IMPORTS;
			}
			throw new IllegalStateException("AdviceMode " + adviceMode + " is not supported");
		}

	}

}
