/*
 * Copyright 2002-2022 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.context.generator;

import java.util.function.BiConsumer;

import org.junit.jupiter.api.Test;

import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.aot.test.agent.EnabledIfRuntimeHintsAgent;
import org.springframework.aot.test.agent.RuntimeHintsInvocations;
import org.springframework.aot.test.agent.RuntimeHintsRecorder;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.aot.test.generate.compile.TestCompiler;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.aot.ApplicationContextAotGenerator;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.testfixture.context.generator.SimpleComponent;
import org.springframework.context.testfixture.context.generator.annotation.AutowiredComponent;
import org.springframework.context.testfixture.context.generator.annotation.InitDestroyComponent;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Tests the {@link RuntimeHints} generation in {@link ApplicationContextAotGenerator}.
 *
 * @author Brian Clozel
 * @author Stephane Nicoll
 */
@EnabledIfRuntimeHintsAgent
class ApplicationContextAotGeneratorRuntimeHintsTests {

	@Test
	void generateApplicationContextWithSimpleBean() {
		GenericApplicationContext context = new AnnotationConfigApplicationContext();
		context.registerBeanDefinition("test", new RootBeanDefinition(SimpleComponent.class));
		compile(context, (hints, invocations) -> assertThat(invocations).match(hints));
	}

	@Test
	void generateApplicationContextWithAutowiring() {
		GenericApplicationContext context = new AnnotationConfigApplicationContext();
		context.registerBeanDefinition("autowiredComponent", new RootBeanDefinition(AutowiredComponent.class));
		context.registerBeanDefinition("number", BeanDefinitionBuilder.rootBeanDefinition(Integer.class, "valueOf")
				.addConstructorArgValue("42").getBeanDefinition());
		compile(context, (hints, invocations) -> assertThat(invocations).match(hints));
	}

	@Test
	void generateApplicationContextWithInitDestroyMethods() {
		GenericApplicationContext context = new AnnotationConfigApplicationContext();
		context.registerBeanDefinition("initDestroyComponent", new RootBeanDefinition(InitDestroyComponent.class));
		compile(context, (hints, invocations) -> assertThat(invocations).match(hints));
	}

	@Test
	void generateApplicationContextWithMultipleInitDestroyMethods() {
		GenericApplicationContext context = new AnnotationConfigApplicationContext();
		RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyComponent.class);
		beanDefinition.setInitMethodName("customInit");
		beanDefinition.setDestroyMethodName("customDestroy");
		context.registerBeanDefinition("initDestroyComponent", beanDefinition);
		compile(context, (hints, invocations) -> assertThat(invocations)
				.withRegistrar(new InitDestroyIssueRegistrar()).match(hints));
	}

	// TODO: Remove once https://github.com/spring-projects/spring-framework/issues/29077 is fixed
	static class InitDestroyIssueRegistrar implements RuntimeHintsRegistrar {
		@Override
		public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
			hints.reflection()
					.registerType(TypeReference.of(InitDestroyComponent.class.getName()), typeHint ->
							typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void compile(GenericApplicationContext applicationContext, BiConsumer<RuntimeHints, RuntimeHintsInvocations> initializationResult) {
		ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
		TestGenerationContext generationContext = new TestGenerationContext();
		generator.processAheadOfTime(applicationContext, generationContext);
		generationContext.writeGeneratedContent();
		TestCompiler.forSystem().withFiles(generationContext.getGeneratedFiles()).compile(compiled -> {
			ApplicationContextInitializer instance = compiled.getInstance(ApplicationContextInitializer.class);
			GenericApplicationContext freshContext = new GenericApplicationContext();
			RuntimeHintsInvocations recordedInvocations = RuntimeHintsRecorder.record(() -> {
				instance.initialize(freshContext);
				freshContext.refresh();
				freshContext.close();
			});
			initializationResult.accept(generationContext.getRuntimeHints(), recordedInvocations);
		});
	}

}

@@ -779,182 +779,20 @@
 int.
-MemberCategory;%0Aimport org.springframework.aot.hint.RuntimeHints;%0Aimport org.springframework.aot.hint.RuntimeHintsRegistrar;%0Aimport org.springframework.aot.hint.TypeReference
+RuntimeHints
 ;%0Aim
@@ -3771,522 +3771,23 @@
 ons)
-%0A%09%09%09%09.withRegistrar(new InitDestroyIssueRegistrar()).match(hints));%0A%09%7D%0A%0A%09// TODO: Remove once https://github.com/spring-projects/spring-framework/issues/29077 is fixed%0A%09static class InitDestroyIssueRegistrar implements RuntimeHintsRegistrar %7B%0A%09%09@Override%0A%09%09public void registerHints(RuntimeHints hints, ClassLoader classLoader) %7B%0A%09%09%09hints.reflection()%0A%09%09%09%09%09.registerType(TypeReference.of(InitDestroyComponent.class.getName()), typeHint -%3E%0A%09%09%09%09%09%09%09typeHint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));%0A%09%09%7D
+.match(hints));
 %0A%09%7D%0A

Remove unnecessary workaround