package tech.easyflow.approval.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import tech.easyflow.approval.annotation.RequirePublishedAccess;
import tech.easyflow.approval.service.ApprovalActionFacade;

import java.lang.reflect.Method;

/**
 * 已发布访问门禁切面。
 */
@Aspect
@Component
public class RequirePublishedAccessAspect {

    private final ApprovalActionFacade approvalActionFacade;
    private final ExpressionParser expressionParser = new SpelExpressionParser();
    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    public RequirePublishedAccessAspect(ApprovalActionFacade approvalActionFacade) {
        this.approvalActionFacade = approvalActionFacade;
    }

    /**
     * 执行发布态门禁校验。
     *
     * @param joinPoint 切点
     * @param requirePublishedAccess 注解
     * @return 原方法返回值
     * @throws Throwable 执行异常
     */
    @Around("@annotation(requirePublishedAccess)")
    public Object doAround(ProceedingJoinPoint joinPoint,
                           RequirePublishedAccess requirePublishedAccess) throws Throwable {
        Object identifier = resolveIdentifier(joinPoint, requirePublishedAccess.idExpr());
        approvalActionFacade.assertPublishedAccess(
                requirePublishedAccess.resourceType(),
                identifier,
                requirePublishedAccess.denyMessage()
        );
        return joinPoint.proceed();
    }

    private Object resolveIdentifier(ProceedingJoinPoint joinPoint, String idExpr) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(
                joinPoint.getTarget(),
                method,
                joinPoint.getArgs(),
                parameterNameDiscoverer
        );
        return expressionParser.parseExpression(idExpr).getValue(context);
    }
}
