<?php


namespace service;

use app\common\Model\MemberAccount;
use think\Db;
use think\facade\Cache;

/**
 * 系统权限节点读取器
 * Class NodeService
 * @package extend
 * @author Vilson
 */
class NodeService
{
    /**
     * 应用项目账号权限节点
     * @return bool
     */
    public static function applyProjectAuthNode()
    {
//        cache('member_need_access_node', null);
        $member = getCurrentMember();
//        $member['nodes'] = [];
//        if (($authorize = $member['authorize'])) {
//            $where = ['status' => '1'];
//            $authorizeIds = Db::name('ProjectAuth')->whereIn('id', explode(',', $authorize))->where($where)->column('id');
//            if (empty($authorizeIds)) {
//                $member['nodes'] = [];
//                return setCurrentMember($member);
//            }
//            $nodes = Db::name('ProjectAuthNode')->whereIn('auth', $authorizeIds)->column('node');
//            $member['nodes'] = $nodes;
//            return setCurrentMember($member);
//        }
//        return setCurrentMember($member);
        $nodes = self::getMemberNodes($member['organization_code'], $member['account_id']);
        $member['nodes'] = $nodes;
        setCurrentMember($member);
        return $nodes;
    }

    public static function getMemberNodes($orgCode, $memberAccountId)
    {
        $cacheKey = 'member:nodes:' . $memberAccountId;
        $tagKey = 'member:codes:' . $orgCode;
//        self::clearMemberNodes($orgCode);
        $nodes = Cache::tag($tagKey)->get($cacheKey);
        if (!$nodes) {
            $member = MemberAccount::get($memberAccountId);
            $authorize = $member['authorize'];
            $authorizeIds = Db::name('ProjectAuth')->whereIn('id', explode(',', $authorize))->where(['status' => '1'])->column('id');
            if (empty($authorizeIds)) {
                $nodes = [];
            } else {
                $nodes = Db::name('ProjectAuthNode')->whereIn('auth', $authorizeIds)->column('node');
            }
            Cache::tag($tagKey)->set($cacheKey, $nodes, 3600 * 24 * 7);
        }
        return $nodes;
    }

    public static function clearMemberNodes($orgCode)
    {
        $tagKey = 'member:codes:' . $orgCode;
        return Cache::clear($tagKey);
    }


    /**
     * 获取项目账号授权节点
     * @return array
     */
    public static function getProjectAuthNode()
    {
        $nodes = cache('member_need_access_node');
        if (empty($nodes)) {
            $nodes = Db::name('ProjectNode')->where(['is_auth' => '1'])->column('node');
            cache('member_need_access_node', $nodes);
        }
        return $nodes;
    }


    /**
     * 检查账号节点权限
     * @param string $node 节点
     * @param $moduleApp string
     * @return bool
     */
    public static function checkAuthNode($node, $moduleApp = 'project')
    {
        list($module, $controller, $action) = explode('/', str_replace(['?', '=', '&'], '/', $node . '///'));
        $currentNode = self::parseNodeStr("{$module}/{$controller}") . strtolower("/{$action}");
        $member = getCurrentMember();
        if ($moduleApp == 'project') {
            //拥有者账号不加入权限判断
            if ($member['is_owner'] == 1) {
                return true;
            }
            if (!in_array($currentNode, self::getProjectAuthNode())) {
                return true;
            }
            $memberNodes = self::getMemberNodes($member['organization_code'], $member['account_id']);
            return in_array($currentNode, !empty($memberNodes) ? (array)$memberNodes : $memberNodes);
        }
        return false;
    }

    /**
     * 获取系统代码节点
     * @param array $nodes
     * @param array $where 查询条件
     * @param string $module 节点模块。目录名应该和模块名一致，如果传入模块，则只搜索该模块目录下的节点。
     * @return array
     */
    public static function get($nodes = [], $where = [], $module = '')
    {
        if ($module == 'project') {
            $alias = Db::name('ProjectNode')->where($where)->column('node,is_menu,is_auth,is_login,title,id');
        }
        $ignore = ['index', 'api', 'project/login', 'project/register', 'project/getCaptcha'];
        $path = env('app_path');
        if ($module) {
            $path .= '/' . $module;
        }
        foreach (self::getNodeTree($path) as $thr) {
            foreach ($ignore as $str) {
                if (stripos($thr, $str) === 0) {
                    continue 2;
                }
            }
            $tmp = explode('/', $thr);
            list($one, $two) = ["{$tmp[0]}", "{$tmp[0]}/{$tmp[1]}"];
            $nodes[$one] = array_merge(isset($alias[$one]) ? $alias[$one] : ['node' => $one, 'title' => '', 'is_menu' => 0, 'is_auth' => 0, 'is_login' => 0], ['pnode' => '']);
            $nodes[$two] = array_merge(isset($alias[$two]) ? $alias[$two] : ['node' => $two, 'title' => '', 'is_menu' => 0, 'is_auth' => 0, 'is_login' => 0], ['pnode' => $one]);
            $nodes[$thr] = array_merge(isset($alias[$thr]) ? $alias[$thr] : ['node' => $thr, 'title' => '', 'is_menu' => 0, 'is_auth' => 0, 'is_login' => 0], ['pnode' => $two]);
        }
        foreach ($nodes as $key => &$node) {
            list($node['is_auth'], $node['is_menu'], $node['is_login']) = [intval($node['is_auth']), intval($node['is_menu']), empty($node['is_auth']) ? intval($node['is_login']) : 1];
        }
        return $nodes;
    }

    /**
     * 获取节点列表
     * @param string $dirPath 路径
     * @param array $nodes 额外数据
     * @return array
     */
    public static function getNodeTree($dirPath, $nodes = [])
    {
        foreach (self::scanDirFile($dirPath) as $filename) {
            $matches = [];
            if (!preg_match('|/(\w+)/controller/(\w+)|', str_replace(DIRECTORY_SEPARATOR, '/', $filename), $matches) || count($matches) !== 3) {
                continue;
            }
            $className = env('app_namespace') . str_replace('/', '\\', $matches[0]);
            if (!class_exists($className)) {
                continue;
            }
            foreach (get_class_methods($className) as $funcName) {
                if (strpos($funcName, '_') !== 0 && $funcName !== 'initialize') {
                    $nodes[] = self::parseNodeStr("{$matches[1]}/{$matches[2]}") . '/' . strtolower($funcName);
                }
            }
        }
        return $nodes;
    }

    /**
     * 驼峰转下划线规则
     * @param string $node
     * @return string
     */
    public static function parseNodeStr($node)
    {
        $tmp = [];
        foreach (explode('/', $node) as $name) {
            $tmp[] = strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
        }
        return trim(join('/', $tmp), '/');
    }

    /**
     * 获取所有PHP文件
     * @param string $dirPath 目录
     * @param array $data 额外数据
     * @param string $ext 有文件后缀
     * @return array
     */
    private static function scanDirFile($dirPath, $data = [], $ext = 'php')
    {
        foreach (scandir($dirPath) as $dir) {
            if (strpos($dir, '.') === 0) {
                continue;
            }
            $tmpPath = realpath($dirPath . DIRECTORY_SEPARATOR . $dir);
            if (is_dir($tmpPath)) {
                $data = array_merge($data, self::scanDirFile($tmpPath));
            } elseif (pathinfo($tmpPath, 4) === $ext) {
                $data[] = $tmpPath;
            }
        }
        return $data;
    }

}
