package com.tongran.common.security.aspect;

import com.tongran.common.security.annotation.RequiresLogin;
import com.tongran.common.security.annotation.RequiresPermissions;
import com.tongran.common.security.annotation.RequiresRoles;
import com.tongran.common.security.auth.AuthUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 基于 Spring Aop 的注解鉴权
 *
 * @author tongran
 */
@Aspect
@Component
public class PreAuthorizeAspect
{
    /**
     * 构建
     */
    public PreAuthorizeAspect()
    {
    }

    /**
     * 定义AOP签名 (切入所有使用鉴权注解的方法)
     * 支持：1. 方法上的注解
     *      2. 类上的注解
     *      3. 父类或接口上的注解
     */
    public static final String POINTCUT_SIGN = " (@annotation(com.tongran.common.security.annotation.RequiresLogin) || "
            + "@annotation(com.tongran.common.security.annotation.RequiresPermissions) || "
            + "@annotation(com.tongran.common.security.annotation.RequiresRoles) || "
            + "@within(com.tongran.common.security.annotation.RequiresLogin) || "
            + "@within(com.tongran.common.security.annotation.RequiresPermissions) || "
            + "@within(com.tongran.common.security.annotation.RequiresRoles))"
            + "&& !@annotation(com.tongran.common.security.annotation.InnerAuth)";

    /**
     * 声明AOP签名
     */
    @Pointcut(POINTCUT_SIGN)
    public void pointcut()
    {
    }

    /**
     * 环绕切入
     *
     * @param joinPoint 切面对象
     * @return 底层方法执行后的返回值
     * @throws Throwable 底层方法抛出的异常
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable
    {
        // 获取方法签名和方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object target = joinPoint.getTarget();
        Class<?> targetClass = target.getClass();

        // 注解鉴权：先检查类上的注解，再检查方法上的注解
        checkAnnotation(targetClass, method);

        // 执行原有逻辑
        return joinPoint.proceed();
    }

    /**
     * 对类和方法进行注解检查
     * 检查顺序：1. 先检查类上的注解
     *         2. 再检查方法上的注解
     */
    public void checkAnnotation(Class<?> targetClass, Method method)
    {
        // 检查类上的注解（包括继承的注解）
        checkClassAnnotation(targetClass);

        // 检查方法上的注解
        checkMethodAnnotation(method);
    }

    /**
     * 检查类上的安全注解
     * 使用 Spring 的 AnnotationUtils 支持查找父类和接口上的注解
     */
    public void checkClassAnnotation(Class<?> clazz)
    {
        // 校验类上的 @RequiresLogin 注解
        RequiresLogin requiresLogin = AnnotationUtils.findAnnotation(clazz, RequiresLogin.class);
        if (requiresLogin != null)
        {
            AuthUtil.checkLogin();
        }

        // 校验类上的 @RequiresRoles 注解
        RequiresRoles requiresRoles = AnnotationUtils.findAnnotation(clazz, RequiresRoles.class);
        if (requiresRoles != null)
        {
            AuthUtil.checkRole(requiresRoles);
        }

        // 校验类上的 @RequiresPermissions 注解
        RequiresPermissions requiresPermissions = AnnotationUtils.findAnnotation(clazz, RequiresPermissions.class);
        if (requiresPermissions != null)
        {
            AuthUtil.checkPermi(requiresPermissions);
        }
    }

    /**
     * 对一个Method对象进行注解检查
     * 使用 Spring 的 AnnotationUtils 支持查找父类和接口上的注解
     */
    public void checkMethodAnnotation(Method method)
    {
        // 校验方法上的 @RequiresLogin 注解
        RequiresLogin requiresLogin = AnnotationUtils.findAnnotation(method, RequiresLogin.class);
        if (requiresLogin != null)
        {
            AuthUtil.checkLogin();
        }

        // 校验方法上的 @RequiresRoles 注解
        RequiresRoles requiresRoles = AnnotationUtils.findAnnotation(method, RequiresRoles.class);
        if (requiresRoles != null)
        {
            AuthUtil.checkRole(requiresRoles);
        }

        // 校验方法上的 @RequiresPermissions 注解
        RequiresPermissions requiresPermissions = AnnotationUtils.findAnnotation(method, RequiresPermissions.class);
        if (requiresPermissions != null)
        {
            AuthUtil.checkPermi(requiresPermissions);
        }
    }

    /**
     * 检查方法是否有指定类型的权限注解
     * 支持从方法、类、父类、接口上查找注解
     *
     * @param method 目标方法
     * @return 如果找到注解返回true，否则返回false
     */
    private boolean hasPermissionAnnotation(Method method)
    {
        // 检查是否有@RequiresLogin注解
        if (AnnotationUtils.findAnnotation(method, RequiresLogin.class) != null)
        {
            return true;
        }

        // 检查类上是否有@RequiresLogin注解
        Class<?> declaringClass = method.getDeclaringClass();
        if (AnnotationUtils.findAnnotation(declaringClass, RequiresLogin.class) != null)
        {
            return true;
        }

        // 检查是否有@RequiresPermissions注解
        if (AnnotationUtils.findAnnotation(method, RequiresPermissions.class) != null)
        {
            return true;
        }

        // 检查类上是否有@RequiresPermissions注解
        if (AnnotationUtils.findAnnotation(declaringClass, RequiresPermissions.class) != null)
        {
            return true;
        }

        // 检查是否有@RequiresRoles注解
        if (AnnotationUtils.findAnnotation(method, RequiresRoles.class) != null)
        {
            return true;
        }

        // 检查类上是否有@RequiresRoles注解
        if (AnnotationUtils.findAnnotation(declaringClass, RequiresRoles.class) != null)
        {
            return true;
        }

        return false;
    }

    /**
     * 获取方法上的@RequiresPermissions注解
     * 支持从方法、类、父类、接口上查找注解
     *
     * @param method 目标方法
     * @return 找到的注解，如果没找到返回null
     */
    private RequiresPermissions getRequiresPermissionsFromMethod(Method method)
    {
        return AnnotationUtils.findAnnotation(method, RequiresPermissions.class);
    }

    /**
     * 获取类上的@RequiresPermissions注解
     * 支持从类、父类、接口上查找注解
     *
     * @param clazz 目标类
     * @return 找到的注解，如果没找到返回null
     */
    private RequiresPermissions getRequiresPermissionsFromClass(Class<?> clazz)
    {
        return AnnotationUtils.findAnnotation(clazz, RequiresPermissions.class);
    }

    /**
     * 获取方法上的@RequiresRoles注解
     * 支持从方法、类、父类、接口上查找注解
     *
     * @param method 目标方法
     * @return 找到的注解，如果没找到返回null
     */
    private RequiresRoles getRequiresRolesFromMethod(Method method)
    {
        return AnnotationUtils.findAnnotation(method, RequiresRoles.class);
    }

    /**
     * 获取类上的@RequiresRoles注解
     * 支持从类、父类、接口上查找注解
     *
     * @param clazz 目标类
     * @return 找到的注解，如果没找到返回null
     */
    private RequiresRoles getRequiresRolesFromClass(Class<?> clazz)
    {
        return AnnotationUtils.findAnnotation(clazz, RequiresRoles.class);
    }

    /**
     * 获取方法上的@RequiresLogin注解
     * 支持从方法、类、父类、接口上查找注解
     *
     * @param method 目标方法
     * @return 找到的注解，如果没找到返回null
     */
    private RequiresLogin getRequiresLoginFromMethod(Method method)
    {
        return AnnotationUtils.findAnnotation(method, RequiresLogin.class);
    }

    /**
     * 获取类上的@RequiresLogin注解
     * 支持从类、父类、接口上查找注解
     *
     * @param clazz 目标类
     * @return 找到的注解，如果没找到返回null
     */
    private RequiresLogin getRequiresLoginFromClass(Class<?> clazz)
    {
        return AnnotationUtils.findAnnotation(clazz, RequiresLogin.class);
    }
}