package com.r11.tools.controller.internal;

import com.r11.tools.controller.internal.path.PathBuilder;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import spark.Spark;

public class RestControllerRegistry {

    private final Set<RestController> registeredControllers;

    public RestControllerRegistry() {
        this.registeredControllers = new HashSet<>();
    }

    public void registerController(RestController restController) {
        this.registeredControllers.add(restController);
    }

    public void register() {
        this.registeredControllers.forEach(controller -> {
            if (controller.getClass().isAnnotationPresent(GlobalControllerManifest.class)) {
                for (Method method : controller.getClass().getMethods()) {
                    this.registerAssignableHandlers(controller.getClass(), controller, method);
                }
            }
        });
    }

    private void registerAssignableHandlers(Class<? extends RestController> parent, RestController parentValue, Method method) {
        if (
                (parent.isAnnotationPresent(GlobalControllerManifest.class)) &&
                        (method.isAnnotationPresent(ScopedControllerManifest.class))
        ) {
            HandlerType handlerType = method.getAnnotation(ScopedControllerManifest.class).method();

            String path = PathBuilder.resolvePathOf(
                    parent.getAnnotation(GlobalControllerManifest.class).path(),
                    method.getAnnotation(ScopedControllerManifest.class).path()
            );

            switch (handlerType) {
                case GET:
                    Spark.get(path, (request, response) -> method.invoke(parentValue, request, response));
                    break;
                case PUT:
                    Spark.put(path, (request, response) -> method.invoke(parentValue, request, response));
                    break;
                case POST:
                    Spark.post(path, (request, response) -> method.invoke(parentValue, request, response));
                    break;
                case DELETE:
                    Spark.delete(path, (request, response) -> method.invoke(parentValue, request, response));
                    break;
            }
        }
    }
}