package cn.axzo.workflow.starter;

import cn.axzo.workflow.starter.api.WorkflowCoreService;
import cn.axzo.workflow.starter.common.enums.FailHandleTypeEnum;
import cn.axzo.workflow.starter.handler.MessageNotificationEventHandler;
import cn.axzo.workflow.starter.handler.ProcessActivityEventHandler;
import cn.axzo.workflow.starter.handler.ProcessInstanceEventHandler;
import cn.axzo.workflow.starter.handler.ProcessTaskEventHandler;
import cn.axzo.workflow.starter.handler.execute.ListenerExecutor;
import cn.axzo.workflow.starter.handler.execute.interceptor.ExecuteInterceptor;
import cn.axzo.workflow.starter.handler.execute.interceptor.ExecutorInvoker;
import cn.axzo.workflow.starter.handler.execute.interceptor.FailBackInterceptor;
import cn.axzo.workflow.starter.handler.execute.interceptor.FailFastInterceptor;
import cn.axzo.workflow.starter.handler.execute.interceptor.FailOverInterceptor;
import cn.axzo.workflow.starter.handler.execute.interceptor.LogInterceptor;
import cn.axzo.workflow.starter.handler.filter.MessageNotificationEventFilter;
import cn.axzo.workflow.starter.handler.filter.ProcessActivityEventFilter;
import cn.axzo.workflow.starter.handler.filter.ProcessInstanceEventFilter;
import cn.axzo.workflow.starter.handler.filter.ProcessTaskEventFilter;
import cn.axzo.workflow.starter.mq.broadcast.consumer.InnerActivityEventListener;
import cn.axzo.workflow.starter.mq.broadcast.consumer.InnerInstanceEventListener;
import cn.axzo.workflow.starter.mq.broadcast.consumer.InnerNotificationEventListener;
import cn.axzo.workflow.starter.mq.broadcast.consumer.InnerTaskEventListener;
import cn.axzo.workflow.starter.mq.broadcast.consumer.WorkflowListener;
import cn.axzo.workflow.starter.mq.monitor.WorkflowEngineStarterDefaultMQMonitor;
import cn.axzo.workflow.starter.mq.monitor.console.WorkflowEngineStarterMQMonitorController;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import java.util.ArrayList;
import java.util.List;

/**
 * Workflow Engine Auto Configuration
 *
 * @author wangli
 * @since 2024/5/21 11:47
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WorkflowEngineStarterProperties.class)
@EnableFeignClients(clients = {WorkflowCoreService.class})
@Import({StarterBroadcastMQConfiguration.class, StarterRPCInvokeMQConfiguration.class})
public class WorkflowEngineStarterAutoConfiguration {

    private final Logger log = LoggerFactory.getLogger(WorkflowEngineStarterAutoConfiguration.class);

    @Bean
    public ListenerExecutor listenerExecutor(WorkflowEngineStarterProperties starterProperties,
                                             List<ExecuteInterceptor> additionalInterceptors) {
        List<ExecuteInterceptor> interceptors = new ArrayList<>();
        interceptors.add(new LogInterceptor());
        interceptors.add(getFailInterceptor(starterProperties));
        // TODO
//        if (!CollectionUtils.isEmpty(additionalInterceptors)) {
//            additionalInterceptors.forEach(interceptor -> interceptor.setNext(interceptor));
//        }
        interceptors.add(new ExecutorInvoker());
        return new ListenerExecutor(interceptors);
    }

    @Bean
    public WorkflowListener innerProcessEventListener(ListenerExecutor executor,
                                                      ObjectProvider<List<ProcessInstanceEventHandler>> handlerProvider,
                                                      ObjectProvider<List<ProcessInstanceEventFilter>> filterProvider) {
        return new InnerInstanceEventListener(executor, handlerProvider, filterProvider);
    }

    @Bean
    public WorkflowListener innerActivityEventListener(ListenerExecutor executor,
                                                       ObjectProvider<List<ProcessActivityEventHandler>> handlerProvider,
                                                       ObjectProvider<List<ProcessActivityEventFilter>> filterProvider) {
        return new InnerActivityEventListener(executor, handlerProvider, filterProvider);
    }

    @Bean
    public WorkflowListener innerTaskEventListener(ListenerExecutor executor,
                                                   ObjectProvider<List<ProcessTaskEventHandler>> handlerProvider,
                                                   ObjectProvider<List<ProcessTaskEventFilter>> filterProvider) {
        return new InnerTaskEventListener(executor, handlerProvider, filterProvider);
    }

    @Bean
    public WorkflowListener innerNotificationEventListener(ListenerExecutor executor,
                                                           ObjectProvider<List<MessageNotificationEventHandler>> handlerProvider,
                                                           ObjectProvider<List<MessageNotificationEventFilter>> filterProvider) {
        return new InnerNotificationEventListener(executor, handlerProvider, filterProvider);
    }

    private ExecuteInterceptor getFailInterceptor(WorkflowEngineStarterProperties starterProperties) {
        BroadcastListenerConfigurationProperties listenerRetry = starterProperties.getBroadcast();
        FailHandleTypeEnum failHandleType = listenerRetry.getFailHandleType();
        log.info("workflow engine starter fail handle type : {}", failHandleType);
        switch (failHandleType) {
            case FAIL_BACK:
                return new FailBackInterceptor();
            case FAIL_FAST:
                return new FailFastInterceptor();
            case FAIL_OVER:
            default:
                return new FailOverInterceptor(listenerRetry.getNumOfRetries(),
                        listenerRetry.getWaitTimeInMs(),
                        listenerRetry.getWaitIncreaseFactor());
        }
    }

    @Bean(destroyMethod = "shutdown", name = "defaultMQAdminExt")
    public DefaultMQAdminExt defaultMQAdminExt() {
        String namesrvAddress = System.getProperty("rocketmq.name-server");
        if (StringUtils.isBlank(namesrvAddress)) {
            log.error("Build DefaultMQAdminExt error, namesrv is null");
            throw new RuntimeException("Build DefaultMQAdminExt error, namesrv is null", null);
        }
        DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt((RPCHook) null, 5000L);
        defaultMQAdminExt.setInstanceName("admin-" + System.currentTimeMillis());
        defaultMQAdminExt.setNamesrvAddr(namesrvAddress);
        try {
            defaultMQAdminExt.start();
        } catch (MQClientException ex) {
            log.error(String.format("init default admin error, namesrv=%s", System.getProperty(MixAll.NAMESRV_ADDR_PROPERTY)), ex);
        }
        return defaultMQAdminExt;
    }

    @Bean
    public WorkflowEngineStarterMQMonitorController workflowEngineStarterMQMonitorController() {
        return new WorkflowEngineStarterMQMonitorController();
    }

    @Bean
    public WorkflowEngineStarterDefaultMQMonitor workflowEngineStarterDefaultMQMonitor(DefaultMQAdminExt defaultMQAdminExt) {
        return new WorkflowEngineStarterDefaultMQMonitor(defaultMQAdminExt);
    }
}
