package cn.axzo.workflow.starter.mq.retry.consumer;

import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.framework.rocketmq.EventHandler;
import cn.axzo.workflow.common.enums.WorkflowEngineEventEnum;
import cn.axzo.workflow.common.model.response.mq.WorkflowEngineStarterRpcInvokeDTO;
import cn.axzo.workflow.common.util.ThreadUtil;
import cn.axzo.workflow.starter.api.WorkflowCoreService;
import com.alibaba.fastjson.JSON;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.ConnectException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;

/**
 * RPC 动作事件的 MQ 消费者（集成业务系统中的自产自销）
 *
 * @author wangli
 * @since 2024/5/21 16:28
 */
public class WorkflowEngineStarterRetryEventListener implements EventHandler, InitializingBean {
    private final Logger log = LoggerFactory.getLogger(WorkflowEngineStarterRetryEventListener.class);
    private final EventConsumer workflowEngineStarterEventConsumer;
    private final Environment environment;
    private final String currentApplicationName;
    private final WorkflowCoreService workflowCoreService;
    private final Map<String, Method> methodCache = new HashMap<>();

    public WorkflowEngineStarterRetryEventListener(EventConsumer workflowEngineStarterEventConsumer,
                                                   Environment environment,
                                                   WorkflowCoreService workflowCoreService) {
        this.workflowEngineStarterEventConsumer = workflowEngineStarterEventConsumer;
        this.environment = environment;
        this.currentApplicationName = environment.getProperty("spring.application.name");
        this.workflowCoreService = workflowCoreService;
        parseWorkflowCoreService();
    }

    private void parseWorkflowCoreService() {
        Class coreService = (Class) workflowCoreService.getClass().getGenericInterfaces()[0];
        FeignClient feignClient = AnnotationUtils.findAnnotation(coreService, FeignClient.class);
        if (Objects.isNull(feignClient)) {
            throw new IllegalStateException("WorkflowCoreService 配置错误，没有找到 FeignClient 注解");
        }
        String parsedFeignContextUrl = resolveExpression(feignClient.url());

        Method[] methods = coreService.getDeclaredMethods();
        for (Method method : methods) {
            methodCache.put(method.getName(), method);
        }
    }

    private String resolveExpression(String expression) {
        // 假设表达式格式是 ${property:defaultValue}
        if (expression != null && expression.startsWith("${") && expression.endsWith("}")) {
            String content = expression.substring(2, expression.length() - 1); // 去除 ${ 和 }
            String[] parts = content.split(":", 2); // 分割属性和默认值
            String property = parts[0];
            String defaultValue = parts.length > 1 ? parts[1] : null;
            // 尝试从环境中获取属性值，如果未找到则使用默认值
            return environment.getProperty(property, defaultValue != null ? defaultValue : "");
        }
        // 如果表达式格式不匹配，直接返回原表达式或处理错误情况
        return expression;
    }

    @SneakyThrows
    @Override
    public void onEvent(Event event, EventConsumer.Context context) {
        log.info("WorkflowEngineClientRetryEventListener onEvent: {}", event.toPrettyJsonString());
        WorkflowEngineStarterRpcInvokeDTO dto = event.normalizedData(WorkflowEngineStarterRpcInvokeDTO.class);

        Method method = methodCache.getOrDefault(dto.getMethodName(), null);
        if (Objects.isNull(method)) {
            throw new IllegalStateException("Not found method：" + dto.getMethodName());
        }
        try {
            // 事件处理 RPC 请求, 强制使用同步模式
            ThreadUtil.set(SYNC);
            Object[] args = convertToActualArgs(method, dto.getParameters());
            log.info("event rpc request args: {}", JSON.toJSONString(args));
            Object invoke = method.invoke(workflowCoreService, args);
            log.info("Event Invoke Result: {}", JSON.toJSONString(invoke));
        } catch (Throwable e) {
            // 能抛出异常目前只有两种情况, 一个是网络异常, 另一个是对端服务内部异常
            Throwable cause = getRealCause(e);
            log.error("Event Invoke Exception: {}", cause.getMessage(), cause);
            if (cause instanceof ConnectException) {
                // 而只有当网络异常时, 利用 RocketMQ 的重试机制
                throw cause;
            }
        }
    }

    private Object[] convertToActualArgs(Method method, List<String> parameters) {
        Object[] args = new Object[parameters.size()];
        Class<?>[] parameterTypes = method.getParameterTypes();
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0; i < parameters.size(); i++) {
            Class<?> parameterType = parameterTypes[i];
            Annotation[] annotations = parameterAnnotations[i];
            for (Annotation annotation : annotations) {
                if (annotation instanceof RequestParam) {
                    List<String> arg = JSON.parseObject(parameters.get(i), List.class);
                    args[i] = Objects.equals(parameterType, List.class) ? arg : CollectionUtils.isEmpty(arg) ? null : arg.get(0);
                } else if (annotation instanceof PathVariable) {
                    args[i] = parameters.get(i);
                } else if (annotation instanceof RequestBody) {
                    args[i] = JSON.parseObject(parameters.get(i), parameterType);
                }
            }
        }
        return args;
    }

    private Throwable getRealCause(Throwable error) {
        while (Objects.nonNull(error.getCause())) {
            // TODO 如果有必要, 可以在这里对异常进行判断/过滤之类的动作, 目前暂未发现需要处理的场景,
            //  后续如果发现, 则需要在此处进行扩展, 注意不是 HardCode, 而是用接口扩展
            error = error.getCause();
        }
        return error;
    }

    @Override
    public void afterPropertiesSet() {
        workflowEngineStarterEventConsumer.registerHandler(WorkflowEngineEventEnum.WORKFLOW_ENGINE_STARTER.getEventCode(currentApplicationName), this);
    }
}
