package com.zsc.edu.gateway.modules.operationLog.util;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zsc.edu.gateway.framework.security.UserDetailsImpl;
import com.zsc.edu.gateway.modules.operationLog.entity.FunctionTypeEnum;
import com.zsc.edu.gateway.modules.operationLog.entity.ModuleTypeEnum;
import com.zsc.edu.gateway.modules.operationLog.entity.OperationLog;
import com.zsc.edu.gateway.modules.operationLog.entity.OperationLogAnnotation;
import com.zsc.edu.gateway.modules.operationLog.repo.OperationLogRepository;
import com.zsc.edu.gateway.modules.system.repo.UserRepository;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * @author zhuang
 */
@Slf4j
@Aspect
@Component
public class OperationLogAspect {

    private static final ExpressionEvaluator<String> EVALUATOR = new ExpressionEvaluator<>();

    @Resource
    OperationLogRepository operationLogRepository;
    @Resource
    UserRepository userRepository;

    /**
     * 被执行方法上添加OperationLogAnnotation注解的才会执行这个方法
     */
    @AfterReturning(pointcut = "@annotation(com.zsc.edu.gateway.modules.operationLog.entity.OperationLogAnnotation)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleOperationLog(joinPoint, jsonResult);
    }

    /**
     * 操作日志处理逻辑，包括：格式处理，持久化
     */
    protected void handleOperationLog(final JoinPoint joinPoint, Object jsonResult) {

        OperationLogAnnotation operationLogAnnotation = ((MethodSignature) joinPoint.getSignature()).getMethod().
                getAnnotation(OperationLogAnnotation.class);

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 获取方法名
        String methodName = joinPoint.getSignature().getName().toLowerCase();
        // 根据方法名判定方法类型
        String methodType = FunctionTypeEnum.getMessageByCode(methodName);

        // 获取类名
        String className = joinPoint.getTarget().getClass().getSimpleName().toLowerCase();
        String moduleType = className.substring(0, className.length() - "controller".length());
        moduleType = ModuleTypeEnum.getMessageByCode(moduleType);


        // 使用SecurityContextHolder获取当前用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();

            // content的默认值为返回结果
            String content = "";

            // 获取operationLogAnnotation注解的content内容
            if (StringUtils.isNotBlank(operationLogAnnotation.content())) {
                // 解析EL表达式
                content = this.evalExpression(joinPoint, operationLogAnnotation.content());
                log.info("expression:{}", content);
            }

            // 获取操作类型
            String operationType = operationLogAnnotation.operationType();
            if (StringUtils.isNotBlank(operationType)) {
                content = operationType + " " + content;
            }

            // 添加操作人信息
            content = "操作人:" + userDetails.getUsername() + " " + content;

            // 操作日志保存到数据库内
            OperationLog operationLog = new OperationLog();
            if (StringUtils.isNotBlank(moduleType) && StringUtils.isNotBlank(methodType)) {
                operationLog.setModuleType(ModuleTypeEnum.valueOf(moduleType));
                operationLog.setFunctionType(FunctionTypeEnum.valueOf(methodType));
                operationLog.setContent(content);
                operationLog.setMakeTime(LocalDateTime.now());
                operationLogRepository.insert(operationLog);
            }
        }
    }

    private String evalExpression(JoinPoint point, String expression) {
        MethodSignature ms = (MethodSignature) point.getSignature();
        Method method = ms.getMethod();
        Object[] args = point.getArgs();
        Object target = point.getTarget();
        Class<?> targetClass = target.getClass();
        MethodBasedEvaluationContext context = EVALUATOR.createEvaluationContext(target, target.getClass(), method, args);
        AnnotatedElementKey elementKey = new AnnotatedElementKey(method, targetClass);
        return EVALUATOR.condition(expression, elementKey, context, String.class);
    }
}
