<?php

namespace app\admin\middleware;

use app\admin\attr\Permission;
use app\common\exception\CatchException;
use app\common\model\Admin;
use app\common\model\Role;
use think\Request;

class CheckPermissionAttr
{
    public function handle(Request $request, \Closure $next)
    {
        // 通过依赖注入获取admin
        $admin = app(Admin::class);
        $role = $admin->role;
        $codes = $role->codes;

        // 超级管理员可以做任何事
        if (in_array(Role::CODE_SUPER_ADMIN, $codes)) {
            return $next($request);
        }

        // 获取权限注解
        $controller = $request->controller();
        $controllerNameSpace = 'app\\admin\\controller\\' . $controller;
        $ref = new \ReflectionClass($controllerNameSpace);
        $attrs = $ref->getAttributes(Permission::class);
        $methodName = $request->action();
        $method = $ref->getMethod($methodName);
        $methodAttrs = $method->getAttributes(Permission::class);

        // 如果有具体的方法权限
        if ($methodAttrs) {
            foreach ($methodAttrs as $attrRaw) {
                /**
                 * @var Permission
                 */
                $attr = $attrRaw->newInstance();
                // 忽略权限要求
                if ($attr->ignore) {
                    return $next($request);
                }

                /*
                 * 权限值设置
                 */
                $permission = $attr->value;
                // 继承权限
                if ($attr->inherit) {
                    if (count($attrs) > 1) {
                        throw new \InvalidArgumentException('使用了继承权限值,但是controller的权限Attribute不止一个');
                    }
                    $controllerAttr = $attrs[0]->newInstance();
                    $controllerPermission = $controllerAttr->value;
                    // 是否使用控制器名
                    // 尽管在方法上没有权限Attr的话,不会进入foreach循环中,但是还是有可能传一个空的permission进来
                    if (!$permission) {
                        if ($controllerAttr->useMethodName) {
                            $permission = "$controllerPermission.$methodName";
                        } else {
                            $permission = $controllerPermission;
                        }
                    }
                } else {
                    // 不继承权限,直接使用$permission, 但是检查权限值是否为空
                    if (!$permission) {
                        if (count($attrs) > 1) {
                            throw new \InvalidArgumentException('没有使用继承,而且权限值为空,尝试使用controller权限值规则,但是controller的权限Attribute不止一个');
                        }
                        $controllerAttr = $attrs[0]->newInstance();
                        $controllerPermission = $controllerAttr->value;
                        // 使用控制器attr规则
                        // 使用方法名
                        if ($controllerAttr->useMethodName) {
                            $permission = $methodName;
                        } else {
                            // 使用控制器名
                            $permission = strtolower($controller);
                        }
                    }
                    // 直接使用$permission
                }
                /*
                * 检查权限
                */
                if (!in_array($permission, $codes)) {
                    throw new CatchException("未具有权限$permission, 禁止访问", 403);
                }
                // 成功
                return $next($request);
            }
        }

        // 只有控制器权限
        /**
         * @var \ReflectionAttribute $attrRaw 
         */
        foreach ($attrs as $attrRaw) {
            /**
             * @var Permission
             */
            $attr = $attrRaw->newInstance();
            $permission = $attr->value;
            // 自动使用方法名作为权限值
            if ($attr->useMethodName) {
                $permission = "$permission.$methodName";
            } elseif (!$permission) {
                // 没有标注要什么权限而且useMethodName设为了false,使用controller的小写作为权限值
                $permission = strtolower($controller);
            }
            if (!in_array($permission, $codes)) {
                throw new CatchException("未具有权限$permission, 禁止访问", 403);
            }
        }

        return $next($request);
    }
}