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

import cn.axzo.workflow.core.common.event.ApiLogEvent;
import cn.axzo.workflow.server.common.annotation.EnvConfig;
import cn.axzo.workflow.server.common.annotation.ErrorReporter;
import cn.axzo.workflow.server.common.config.property.WorkflowProperties;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Objects;

import static cn.azxo.framework.common.constatns.Constants.CTX_LOG_ID_MDC;

/**
 * 异常错误信息报告器
 *
 * @author wangli
 * @since 2024/4/2 11:15
 */
@Component
@Aspect
@Slf4j
public class ErrorReportAspect implements Ordered {
    @Value("${spring.profiles.active}")
    private String profile;
    @Value("${workflow.sendDingTalk:true}")
    private Boolean sendDingTalk;
    @Value("${workflow.api.timeout:10}")
    private Long apiTimeout;
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    @Resource
    private WorkflowProperties workflowProperties;

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 3;
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @Around(value = "@within(org.springframework.web.bind.annotation.RestController) || @within(org.springframework.stereotype.Service)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        StopWatch watch = new StopWatch("running controller time");
        watch.start(signature.toShortString());
        Object result = joinPoint.proceed();
        watch.stop();
        log.info("StopWatch '" + watch.getLastTaskName() + "': running time = " + watch.getTotalTimeSeconds() + " s");

        if (!signature.toShortString().contains("ExtAxApiLogServiceImpl")) {
            String type = getType(joinPoint);
            ApiLogEvent event = new ApiLogEvent(MDC.get(CTX_LOG_ID_MDC),
                    signature.toShortString(),
                    Objects.equals(type, "Controller") ? joinPoint.getArgs() : null,
                    Objects.equals(type, "Controller") ? result : null,
                    watch.getTotalTimeSeconds(),
                    type);

            applicationEventPublisher.publishEvent(event);
        }
        return result;
    }

    private String getType(ProceedingJoinPoint joinPoint) {
        if (isController(joinPoint)) {
            return "Controller";
        } else if (isService(joinPoint)) {
            return "Service";
        } else {
            return "";
        }
    }

    private Boolean isController(ProceedingJoinPoint joinPoint) {
        return getTargetAnnotation(joinPoint).isAnnotationPresent(RestController.class);
    }

    private Boolean isService(ProceedingJoinPoint joinPoint) {
        return getTargetAnnotation(joinPoint).isAnnotationPresent(Service.class);
    }

    private Class<?> getTargetAnnotation(ProceedingJoinPoint joinPoint) {
        return joinPoint.getTarget().getClass();
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(pointcut = "@within(errorReporter) && @annotation(operation)", throwing = "e", argNames = "joinPoint,errorReporter,operation,e")
    public void doAfterThrowing(JoinPoint joinPoint, ErrorReporter errorReporter, Operation operation, Exception e) {
        log.info("ErrorReportAspect 记录异常信息: {}", e.getMessage());
        EnvConfig[] envConfigs = errorReporter.envConfig();
        for (EnvConfig envConfig : envConfigs) {
            if (Arrays.asList(envConfig.profiles()).contains(profile)) {
                envConfig.type().executeAction(profile, operation.summary(), sendDingTalk, joinPoint.getArgs(), joinPoint.getSignature().toShortString(), e,
                        workflowProperties.getFilterOperations().contains(operation.summary()));

            }
        }
    }
}
