package cn.axzo.workflow.server.common.interceptor;

import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.repository.entity.ExtAxProperty;
import cn.axzo.workflow.core.service.ExtAxPropertyService;
import cn.axzo.workflow.server.common.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.util.Enumeration;
import java.util.Objects;
import java.util.Optional;

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.common.constant.BpmnConstants.FLOW_SERVER_VERSION_130;
import static cn.axzo.workflow.common.constant.StarterConstants.ENABLE_MANAGEABLE;
import static cn.axzo.workflow.common.code.OtherRespCode.CLIENT_VERSION_SUPPORT;
import static cn.axzo.workflow.common.code.OtherRespCode.MICRO_SERVER_NEED_REBUILD;

/**
 * 客户端与服务端的版本比较
 *
 * @author wangli
 * @since 2024/2/4 15:48
 */
@Component
@Slf4j
public class RequestHeaderContextInterceptor implements HandlerInterceptor {
    @Autowired
    private String serviceVersion;
    @Autowired
    private ExtAxPropertyService extAxPropertyService;
    private static final ThreadLocal<String> KEY_CACHE = new ThreadLocal<>();
    private static final String REPEAT_KEY = "global:api_application:";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (Objects.equals(HEADER_HTTP_CLIENT_VALUE, request.getHeader(HEADER_HTTP_CLIENT))) {
            String headerClientVersion = request.getHeader(HEADER_API_VERSION)
                .replaceAll("-SNAPSHOT", "")
                .replaceAll("-RELEASE", "");
            serviceVersion = serviceVersion
                .replaceAll("-SNAPSHOT", "")
                .replaceAll("-RELEASE", "");
            DefaultArtifactVersion minimumSupportedVersion = new DefaultArtifactVersion(FLOW_SERVER_VERSION_130);
            DefaultArtifactVersion clientVersion = new DefaultArtifactVersion(headerClientVersion);
            DefaultArtifactVersion serverVersion = new DefaultArtifactVersion(serviceVersion);
            if (clientVersion.compareTo(minimumSupportedVersion) >= 0 || clientVersion.compareTo(serverVersion) >= 0) {

                recordClientInfo(request, headerClientVersion, clientVersion);

                return true;
            } else {
                printHeader(request);
                throw new WorkflowEngineException(CLIENT_VERSION_SUPPORT, serviceVersion, headerClientVersion);
            }
        }
        // 仅 feignApi 才需要检查版本
        if (!request.getRequestURI().contains("/web/") && !request.getRequestURI().contains("checkDeath")
            && !request.getRequestURI().contains("/error")
            && !StringUtils.hasText(request.getHeader(HEADER_HTTP_CLIENT))) {
            String serverName = request.getHeader(HEADER_SERVER_NAME);
            printHeader(request);
            log.error(MICRO_SERVER_NEED_REBUILD.getMessage(), serverName);
            throw new WorkflowEngineException(MICRO_SERVER_NEED_REBUILD, serverName);
        }
        return true;
    }

    private void recordClientInfo(HttpServletRequest request, String headerClientVersion,
                                  DefaultArtifactVersion clientVersion) {
        String applicationName = request.getHeader(HEADER_SERVER_NAME);
        log.info("HEADER_SERVER_NAME : {}", applicationName);
        if (!StringUtils.hasText(applicationName)) {
            return;
        }
        String manageableStatus = request.getHeader(ENABLE_MANAGEABLE);

        Optional<ExtAxProperty> extAxProperty = extAxPropertyService.getByName(applicationName);
        String cacheRepeatKey = REPEAT_KEY + applicationName;
        log.info("repeatApi key: {}", cacheRepeatKey);

        //success为true表示key不存在,执行成功,false表示key存在,执行失败
        Boolean success = RedisUtils.trySetObject(cacheRepeatKey, "", Duration.ofSeconds(60));
        if (success) {
            KEY_CACHE.set(cacheRepeatKey);
            insert(extAxProperty, applicationName, clientVersion, manageableStatus);
        }
    }

    private void update(Optional<ExtAxProperty> extAxProperty, String requestApplicationName, DefaultArtifactVersion clientVersion, String manageableStatus) {
        if (!extAxProperty.isPresent()) {
            return;
        }
        ExtAxProperty property = extAxProperty.get();
        if (Objects.equals(property.getValue(), clientVersion.toString())
            && Objects.equals(property.getManageable().toString(), manageableStatus)) {
            return;
        }
        property.setName(requestApplicationName);
        property.setValue(clientVersion.toString());
        property.setManageable(Boolean.valueOf(manageableStatus));
        extAxPropertyService.update(property);
    }

    private void insert(Optional<ExtAxProperty> extAxProperty, String requestApplicationName, DefaultArtifactVersion clientVersion, String manageableStatus) {
        if (extAxProperty.isPresent()) {
            update(extAxProperty, requestApplicationName, clientVersion, manageableStatus);
        } else {
            extAxPropertyService.add(extAxProperty.orElseGet(() -> {
                ExtAxProperty property = new ExtAxProperty();
                property.setCreated(true);
                property.setName(requestApplicationName);
                property.setValue(clientVersion.toString());
                property.setManageable(Boolean.valueOf(manageableStatus));
                return property;
            }));
        }
    }

    private void printHeader(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        log.info("parse header start, current uri: {}", request.getRequestURI());
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            log.info("headerName: {}, headerValue: {}", headerName, headerValue);
        }
        log.info("parse header end");
    }

}
