package com.arms.api.schedule.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class ScheduleTaskDispatcher implements CommandLineRunner, ApplicationContextAware {

    private ApplicationContext applicationContext;
    private final Map<String, Runnable> feignClientTaskMap = new HashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public Runnable getTask(String name) {
        Runnable task = feignClientTaskMap.get(name);

        if (task == null) {
            log.warn("ScheduleTaskDispatcher :: No task found for this (schedule or method) name : {}", name);
            throw new IllegalArgumentException("ScheduleTaskDispatcher :: No task found for name: " + name);
        }
        return task;
    }

    public boolean contains(String name) {
        return feignClientTaskMap.containsKey(name);
    }

    @Override
    public void run(String... args) throws Exception {

        Map<String, Object> feignClientBeans = applicationContext.getBeansWithAnnotation(FeignClient.class);

        for (Map.Entry<String, Object> entry : feignClientBeans.entrySet()) {
            Object feignClientProxy = entry.getValue();

            Class<?>[] interfaces = feignClientProxy.getClass().getInterfaces();
            Class<?> feignClientInterface = Arrays.stream(interfaces)
                                                .filter(iface -> iface.isAnnotationPresent(FeignClient.class))
                                                .findFirst()
                                                .orElse(null);;

            if (feignClientInterface == null) {
                log.error("Could not find FeignClient interface for bean : {}", entry.getKey());
                continue;
            }

            for (Method method : feignClientInterface.getDeclaredMethods()) {

                if (java.lang.reflect.Modifier.isPublic(method.getModifiers()) && method.getParameters().length == 0) {

                    String key = method.getName();

                    Runnable task = () -> {
                        try {
                            log.info("  Executing Feign Client Method: {} (no parameters)", key);
                            Object result = method.invoke(feignClientProxy);

                            if (method.getReturnType() == void.class) {
                                log.info("  Method {} executed (void return type).", key);
                            } else {
                                log.info("  Method {} executed. Result: {}", key, result);
                            }
                        } catch (Exception e) {
                            log.error(" Error executing Feign Client Method {} : {}", key, e.getMessage());
                            if (e.getCause() != null) {
                                log.error("  Caused by: {} : {}", e.getCause().getClass().getSimpleName(), e.getCause().getMessage());
                            }
                        }
                    };

                    feignClientTaskMap.put(key, task);
                    log.info("  Added task:  {}", key);
                } else {
                    log.info("  Skipping method (has parameters or not public):  {}", method.getName());
                }
            }
        }
    }
}
