package com.github.thomasdarimont.keycloak.embedded.support;

import org.keycloak.platform.Platform;
import org.keycloak.platform.PlatformProvider;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;

public class SpringBootPlatform implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return ApplicationReadyEvent.class.equals(eventType) || ContextStoppedEvent.class.equals(eventType);
    }

    public void onApplicationEvent(ApplicationEvent event) {

        if (event instanceof ApplicationReadyEvent) {
            Runnable startupHook = getPlatformField(Runnable.class, "startupHook");
            if (startupHook != null) {
                startupHook.run();
            }
        } else if (event instanceof ContextStoppedEvent) {
            Runnable shutdownHook = getPlatformField(Runnable.class, "shutdownHook");
            if (shutdownHook != null) {
                shutdownHook.run();
            }
        }
    }

    // TODO find better way to work around classloader issues
    private <T> T getPlatformField(Class<T> type, String name) {
        PlatformProvider p = Platform.getPlatform();
        Field field = ReflectionUtils.findField(p.getClass(), name);
        ReflectionUtils.makeAccessible(field);
        return type.cast(ReflectionUtils.getField(field, p));
    }

    public static class Delegate implements PlatformProvider {
        private Runnable startupHook;

        private Runnable shutdownHook;

        @Override
        public void onStartup(Runnable startupHook) {
            this.startupHook = startupHook;
        }

        @Override
        public void onShutdown(Runnable shutdownHook) {
            this.shutdownHook = shutdownHook;
        }

        @Override
        public void exit(Throwable cause) {
            throw new RuntimeException(cause);
        }
    }
}