<?php

namespace plugin\admin\app\controller;

use Cache;
use Exception;
use Illuminate\Support\Facades\Date;
use plugin\admin\app\common\Auth;
use plugin\admin\app\common\Tree;
use plugin\admin\app\common\Util;
use plugin\admin\app\model\AdminRole;
use plugin\admin\app\model\AdminRule;
use support\exception\BusinessException;
use support\Request;
use support\Response;
use Throwable;

/**
 * 权限菜单
 */
class AdminRuleController extends Crud
{
    /**
     * 不需要权限的方法
     *
     * @var string[]
     */
    protected $noNeedAuth = ['get', 'permission'];

    /**
     * @var AdminRule
     */
    protected $model = null;

    /**
     * 构造函数
     */
    function __construct()
    {
        $this->model = new AdminRule;
    }

    /**
     * 浏览
     * @return Response
     * @throws Throwable
     */
    public function index(Request $request): Response
    {
        return view('admin_rule/index');
    }

    /**
     * 格式化树
     * @param $items
     * @return Response
     */
    protected function formatTree($items): Response
    {
        $format_items = [];
        //$primary_key = $this->model->getPk();
        $primary_key = $this->model->getPk();
        foreach ($items as $item) {
            $item->text = $item->title;
            //$item->value = (string)$item->$primary_key;
            $item->id = $item->$primary_key;
            $item->parent = $item->pid;
            if($item->pid == 0){
                $item->parent = '#';
            }
            unset($item->pid);
            $format_items[] = $item;
        }
        $tree = new Tree($format_items);
        return $this->success("操作成功", $tree->getTree());
    }

    /**
     * 权限树形
     * @param Request $request
     * @return Response
     * @throws BusinessException
     */
    public function roletree(Request $request): Response
    {
        [$where, $format, $limit, $field, $order] = $this->selectInput($request);
        $query = $this->doSelect($where, $field, $order);
        $methods = [
            'select' => 'formatSelect',
            'tree' => 'formatTree',
            'table_tree' => 'formatTableTree',
            'normal' => 'formatNormal',
        ];
        $format = 'normal';
        $items = $query->field('id,title as text,pid as parent,"menu" as type')->select();
        /**
         * @var AdminRule $item
         */ 
        foreach ($items as $key => $item) {
            $items[$key]->state = [
                "selected"  => false,
            ];
            if($items[$key]->parent == 0){
                $items[$key]->parent = '#';
            }
        };
        if (method_exists($this, "afterQuery")) {
            $items = call_user_func([$this, "afterQuery"], $items);
        }
        $format_function = $methods[$format] ?? 'formatNormal';
        
        return call_user_func([$this, $format_function], $items, 0);
    }
    /**
     * 查询
     * @param Request $request
     * @return Response
     * @throws BusinessException
     */
    public function select(Request $request): Response
    {
        [$where, $format, $limit, $field, $order] = $this->selectInput($request);
        $query = $this->doSelect($where, $field, $order);
        return $this->doFormat($query, $format, 'all');
    }
    /**
     * 刷新缓存
     * @param \support\Request $request
     * @return Response
     */
    public function buildcache(Request $request){
        //缓存
        $list = $this->get($request)->rawBody();
        $list = json_decode($list,true);
        $fn = base_path() . '/plugin/admin/config/menu.php';
        file_put_contents($fn, "<?php \n return ".var_export($list['data'],true).';');
        return $this->success('ok');
    }
    /**
     * 获取菜单
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    function get(Request $request): Response
    {
        
        $is_supper_admin = Auth::isSuperAdmin();
        if($is_supper_admin){
            $rules = ['*'];
        }else{
            $rules = $this->getRules(admin('roles'));
        }
        //return $this->success("操作成功", admin('roles'));
        $types = $request->get('type', '0,1');
        $types = is_string($types) ? explode(',', $types) : [0, 1];
        $items = AdminRule::order('weight', 'desc')->select();

        $formatted_items = [];
        /**
         * @var AdminRule $item
         */ 
        foreach ($items as $item) {
            $item['pid'] = (int)$item['pid'];
            $item['name'] = $item['title'];
            $item['value'] = $item['id'];
            $item['icon'] = $item->icon ? "{$item->icon}" : '';
            $formatted_items[] = $item;
        }

        $tree = new Tree($formatted_items);
        $tree_items = $tree->getTree();
        // 超级管理员权限为 *
        if (!in_array('*', $rules)) {
            $this->removeNotContain($tree_items, 'id', $rules);
        }
        $this->removeNotContain($tree_items, 'type', $types);
        $menus = $this->empty_filter(Tree::arrayValues($tree_items));
        return $this->success("操作成功", $menus);
    }

    private function empty_filter($menus)
    {
        return array_map(
            function ($menu) {
                if (isset($menu['children'])) {
                    $menu['children'] = $this->empty_filter($menu['children']);
                }
                return $menu;
            },
            array_values(array_filter(
                $menus,
                function ($menu) {
                    return $menu['type'] != 0 || isset($menu['children']) && count($this->empty_filter($menu['children'])) > 0;
                }
            ))
        );
    }

    /**
     * 获取权限
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function permission(Request $request): Response
    {
        $rules = $this->getRules(admin('roles'));
        // 超级管理员
        if (in_array('*', $rules)) {
            return $this->success("操作成功", ['*']);
        }
        $keys = AdminRule::whereIn('id', $rules)->column('key');
        $permissions = [];
        foreach ($keys as $key) {
            if (!$key = Util::controllerToUrlPath($key)) {
                continue;
            }
            $code = str_replace('/', '.', trim($key, '/'));
            $permissions[] = $code;
        }
        return $this->success("操作成功", $permissions);
    }

    /**
     * 同步规则
     * @return void
     */
    public function syncRules(): Response 
    {
        //$items = $this->model->where('key', 'like', '%\\\\%')->get()->keyBy('key');
        $items = $this->model->whereLike('key', '%\\\\%')->select();
        $methods_in_db = [];
        $methods_in_files = [];
        /**
         * @var AdminRule $item
         */ 
        foreach ($items as $item) {
            $class = $item->key;
            if (strpos($class, '@')) {
                $methods_in_db[$class] = $class;
                continue;
            }
            if (class_exists('\\plugin\\admin\\'.$class)) {
                $reflection = new \ReflectionClass('\\plugin\\admin\\'.$class);
                $properties = $reflection->getDefaultProperties();
                $no_need_auth = array_merge($properties['noNeedLogin'] ?? [], $properties['noNeedAuth'] ?? []);
                $class = str_replace('plugin\admin\\','',$reflection->getName());
                $pid = $item->id;
                $methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
                foreach ($methods as $method) {
                    $method_name = $method->getName();
                    if (strtolower($method_name) === 'index' || strpos($method_name, '__') === 0 || in_array($method_name, $no_need_auth)) {
                        continue;
                    }
                    $name = "$class@$method_name";

                    $methods_in_files[$name] = $name;
                    $title = Util::getCommentFirstLine($method->getDocComment()) ?: $method_name;
                    /**
                     * @var AdminRule $menu
                     */ 
                    $menu = $items[$name] ?? [];
                    if ($menu) {
                        if ($menu->title != $title) {
                            AdminRule::where('key', $name)->update(['title' => $title]);
                        }
                        continue;
                    }
                    if(!AdminRule::where('key', $name)->count('id')) {
                        $menu = new AdminRule;
                        $menu->pid = $pid;
                        $menu->key = $name;
                        $menu->title = $title;
                        $menu->type = 2;
                        $menu->save();
                    }
                }
            }
        }
        // 从数据库中删除已经不存在的方法
        $menu_names_to_del = array_diff($methods_in_db, $methods_in_files);
        if ($menu_names_to_del) {
            AdminRule::whereIn('key', $menu_names_to_del)->delete();
        }
        return $this->success("操作成功");
    }

    /**
     * 查询前置方法
     * @param Request $request
     * @return array
     * @throws BusinessException
     */
    protected function selectInput(Request $request): array
    {
        [$where, $format, $limit, $field, $order] = parent::selectInput($request);
        // 允许通过type=0,1格式传递菜单类型
        $types = $request->get('type');
        if ($types && is_string($types)) {
            $where['type'] = ['symbol'=>'in', 'value1'=>explode(',', $types)];
        }
        // 默认weight排序
        if (!$field) {
            $field = 'weight';
            $order = 'desc';
        }
        return [$where, $format, $limit, $field, $order];
    }

    /**
     * 添加
     * @param Request $request
     * @return Response
     * @throws BusinessException|Throwable
     */
    public function insert(Request $request): Response
    {
        if ($request->method() === 'GET') {
            $RuleList = $this->model->where('status',1)->order('id asc')->select();
            $tree = new Tree($RuleList);
            return view('admin_rule/update',[
                'RuleList'  => $tree->getTree()
            ]);
        }
        $data = $this->insertInput($request);
        if (empty($data['type'])) {
            $data['type'] = strpos($data['key'], '\\') ? 1 : 0;
        }
        $data['key'] = str_replace('\\\\', '\\', $data['key']);
        $key = $data['key'] ?? '';
        if ($this->model->where('key', $key)->count('id')) {
            return $this->fail("菜单标识 $key 已经存在");
        }
        $data['pid'] = empty($data['pid']) ? 0 : $data['pid'];
        $this->doInsert($data);
        return $this->success("操作成功");
    }

    /**
     * 更新
     * @param Request $request
     * @return Response
     * @throws BusinessException|Throwable
     */
    public function update(Request $request): Response
    {
        if ($request->method() === 'GET') {
            $RuleList = $this->model->order('id asc')->select();
            $ids = Request()->get('ids');
            $tree = new Tree($RuleList);
            return view('admin_rule/update',[
                'RuleList'  => $tree->getTree(),
                'row'   => $this->model->where('id',$ids)->find()
            ]);
        }
        [$id, $data] = $this->updateInput($request);
        if (!$row = $this->model->find($id)) {
            return $this->json(2, '记录不存在');
        }
        if (isset($data['pid'])) {
            $data['pid'] = $data['pid'] ?: 0;
            if ($data['pid'] == $row['id']) {
                return $this->json(2, '不能将自己设置为上级菜单');
            }
        }
        if (isset($data['key'])) {
            $data['key'] = str_replace('\\\\', '\\', $data['key']);
        }
        $this->doUpdate($id, $data);
        return $this->success("操作成功");
    }
    
    /**
     * 删除
     * @param Request $request
     * @return Response
     */
    public function delete(Request $request): Response
    {
        $ids = $this->deleteInput($request);
        // 子规则一起删除
        $delete_ids = $children_ids = $ids;
        while($children_ids) {
            $children_ids = $this->model->whereIn('pid', $children_ids)->column('id');
            $delete_ids = array_merge($delete_ids, $children_ids);
        }
        $this->doDelete($delete_ids);
        return $this->success("操作成功");
    }

    /**
     * 移除不包含某些数据的数组
     * @param $array
     * @param $key
     * @param $values
     * @return void
     */
    protected function removeNotContain(&$array, $key, $values)
    {
        foreach ($array as $k => &$item) {
            if (!is_array($item)) {
                continue;
            }
            if (!$this->arrayContain($item, $key, $values)) {
                unset($array[$k]);
            } else {
                if (!isset($item['children'])) {
                    continue;
                }
                $this->removeNotContain($item['children'], $key, $values);
            }
        }
    }

    /**
     * 判断数组是否包含某些数据
     * @param $array
     * @param $key
     * @param $values
     * @return bool
     */
    protected function arrayContain(&$array, $key, $values): bool
    {
        if (!is_array($array)) {
            return false;
        }
        if (isset($array[$key]) && in_array($array[$key], $values)) {
            return true;
        }
        if (!isset($array['children'])) {
            return false;
        }
        foreach ($array['children'] as $item) {
            if ($this->arrayContain($item, $key, $values)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取权限规则
     * @param $roles
     * @return array
     */
    protected function getRules($roles): array
    {
        $rules_strings = $roles ? AdminRole::whereIn('id', $roles)->column('rules') : [];
        $rules = [];
        foreach ($rules_strings as $rule_string) {
            if (!$rule_string) {
                continue;
            }
            $rules = array_merge($rules, explode(',', $rule_string));
        }
        return $rules;
    }

}
