package cn.axzo.workflow.starter.feign.ext;

import cn.axzo.workflow.common.enums.RpcInvokeModeEnum;
import cn.axzo.workflow.common.util.ThreadUtil;
import cn.axzo.workflow.starter.WorkflowEngineStarterProperties;
import cn.azxo.framework.common.constatns.Constants;
import feign.Client;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Retryer;
import feign.Target;
import feign.codec.Decoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignBuilderCustomizer;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;

import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_API_VERSION;
import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_HTTP_CLIENT;
import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_HTTP_CLIENT_VALUE;
import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_SERVER_NAME;
import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_W_E;
import static cn.axzo.workflow.common.constant.StarterConstants.ENABLE_MANAGEABLE;
import static cn.axzo.workflow.common.constant.StarterConstants.STARTER_INVOKE_MODE;
import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * Starter Feign Client 的自定义的配置
 *
 * @author wangli
 * @since 2024/5/28 23:17
 */
public class WorkflowEngineStarterFeignConfiguration {
    private final Logger log = LoggerFactory.getLogger(WorkflowEngineStarterFeignConfiguration.class);

    @Bean
    public Retryer defaultRetryer() {
        return new Retryer.Default(100, SECONDS.toMillis(1), 3);
    }

    @Bean
    public Client complexInvokeClient(WorkflowEngineStarterProperties starterProperties,
                                      BeanFactory beanFactory,
                                      Client feignClient,
                                      Environment environment) {
        return new ComplexInvokeClient(starterProperties, beanFactory, feignClient, environment);
    }

    @Bean
    public Decoder workflowEngineStarterDecoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        return new WorkflowEngineStarterDecoder(new ResponseEntityDecoder(new SpringDecoder(messageConverters)));
    }

    @Bean
    public RequestInterceptor workflowEngineStarterRequestInterceptor(WorkflowEngineStarterProperties starterProperties,
                                                                      Environment environment,
                                                                      @Qualifier("serviceVersion") String serviceVersion) {
        return template -> {
            // 接入应用上报
            template.header(HEADER_SERVER_NAME, environment.getProperty("spring.application.name"));
            resetInvokeMode(starterProperties, template);
            // 通过服务端的校验
            Target.HardCodedTarget target = (Target.HardCodedTarget) template.feignTarget();
            String apiClassPath = target.type().getName();
            if (apiClassPath.contains("cn.axzo.workflow.client")) {
                template.header(HEADER_W_E, "true");
            }
            if (apiClassPath.contains("cn.axzo.workflow.starter.api")) {
                template.header(HEADER_HTTP_CLIENT, HEADER_HTTP_CLIENT_VALUE);
                template.header(HEADER_API_VERSION, serviceVersion);
            }
            //链路追踪
            template.header(Constants.CTX_LOG_ID_MDC, MDC.get(Constants.CTX_LOG_ID_MDC));
            Boolean manageable = Boolean.parseBoolean(environment.getProperty("workflow.manageable", "false"));
            template.header(ENABLE_MANAGEABLE, manageable.toString());
            HttpServletRequest originalRequest = getOriginalRequest();
            if (Objects.nonNull(originalRequest)) {
                setRequestParams(template, originalRequest);
            }
        };
    }

    private void setRequestParams(RequestTemplate template, HttpServletRequest originalRequest) {
        String MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME = "pkg";
        String[] packageNames = originalRequest.getParameterValues(MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME);
        template.query(MICRO_SERVER_RECORD_ERROR_GET_PARAM_NAME, packageNames);
    }

    public HttpServletRequest getOriginalRequest() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            return attributes.getRequest();
        } catch (Exception e) {
            log.debug("not HttpServletRequest instance bean found");
            return null;
        }
    }

    private void resetInvokeMode(WorkflowEngineStarterProperties starterProperties, RequestTemplate template) {
        Collection<String> invokeModeInHeader = template.headers().getOrDefault(STARTER_INVOKE_MODE, Collections.emptyList());
        template.removeHeader(STARTER_INVOKE_MODE);
        // 接口的同步异步模式
        RpcInvokeModeEnum rpcInvokeModeEnum = ThreadUtil.get();
        ThreadUtil.clear();
        if (Objects.isNull(rpcInvokeModeEnum)) {
            if (CollectionUtils.isEmpty(invokeModeInHeader)) {
                log.debug("Without calling the async or sync methods of the WorkflowCoreService, the default configuration in WorkflowEngineStarterProperties will be loaded.");
                rpcInvokeModeEnum = starterProperties.getInvokeMode();
            } else {
                rpcInvokeModeEnum = RpcInvokeModeEnum.valueOf(invokeModeInHeader.iterator().next());
            }
        }
        template.header(STARTER_INVOKE_MODE, rpcInvokeModeEnum.name());
    }


    @Bean
    public FeignBuilderCustomizer workflowEngineFeignBuilderCustomizer() {
        return builder -> builder.invocationHandlerFactory(new WorkflowEngineStarterInvocationHandlerFactory());
    }
}
