// NOTE: [StateManager] cannot use injectors like @InjectDependency or @InjectState
package cz.jzitnik.utils;

import cz.jzitnik.annotations.Dependency;
import cz.jzitnik.annotations.State;
import cz.jzitnik.annotations.injectors.InjectDependency;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Optional;

@Slf4j
@Dependency
public class StateManager {
    private final HashMap<Class<?>, Object> data = new HashMap<>();

    public StateManager(Reflections reflections, DependencyManager dependencyManager) {
        var classes = reflections.getTypesAnnotatedWith(State.class);

        for (Class<?> clazz : classes) {
            try {
                var instance = clazz.getDeclaredConstructor().newInstance();
                data.put(clazz, instance);
            } catch (NoSuchMethodException e) {
                try {
                    var instance = clazz.getDeclaredConstructor(DependencyManager.class).newInstance(dependencyManager);
                    data.put(clazz, instance);
                } catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
                         IllegalAccessException ex) {
                    throw new RuntimeException(ex);
                }
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
                log.error("Failed to instantiate state class: {}", clazz.getName(), e);
            }
        }
    }

    public void registerManually(Object instance) {
        data.put(instance.getClass(), instance);
    }

    @SuppressWarnings("unchecked")
    public <T> Optional<T> get(Class<T> clazz) {
        return Optional.ofNullable((T) data.get(clazz));
    }

    @SuppressWarnings("unchecked")
    public <T> T getOrThrow(Class<T> clazz) {
        return (T) data.get(clazz);
    }
}
