<?php
declare (strict_types=1);

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\facade\Db;

class Generate extends Command
{
    protected string $controller;
    protected string $name;
    protected string $title;

    protected string $namespace;//控制器命名空间
    protected string $viewDir;//view目录
    protected string $jsFullDir;//js完整路径目录
    protected string $jsDir;//js相对路径目录
    protected string $controllerFullPath;//控制器完整路径
    protected string $className;//控制器类名

    protected function configure()
    {
        // 指令配置
        $this->setName('generate')
            ->setDescription('生成后台基础crud')
            ->addOption('title', 't', Option::VALUE_REQUIRED, '标题')
            ->addOption('name', null, Option::VALUE_REQUIRED, '权限标识')
            ->addOption('controller', null, Option::VALUE_REQUIRED, '控制器命名空间');
    }

    protected function setInfo(string $controller, string $name, string $title)
    {
        $this->name       = $name;
        $this->controller = $controller;
        $this->title      = $title;

        $this->controllerFullPath = $this->getControllerPathName();
        $this->viewDir            = $this->getViewDirName();
        $this->jsFullDir          = $this->getJsFullDirName();
        $this->jsDir              = $this->getJsDirName();

        $controllerPath  = explode('/', trim($controller, '/'));
        $this->className = $controllerPath[count($controllerPath) - 1];

        $namespace       = 'app\\controller\\'.implode('\\', array_slice($controllerPath, 0, count($controllerPath) - 1));
        $this->namespace = $namespace;
    }

    protected function execute(Input $input, Output $output)
    {
        $title      = $input->getOption('title') ? trim($input->getOption('title')) : '';
        $name       = $input->getOption('name') ? trim($input->getOption('name')) : '';
        $controller = $input->getOption('controller') ? trim($input->getOption('controller')) : '';

        $this->setInfo($controller, $name, $title);
        try {
            $output->writeln('开始创建');
            $output->writeln('1.菜单表生成数据...');
            $dbRes = $this->createDb([
                'title' => $title, 'name' => $name, 'controller' => $controller
            ]);

            $successList = [];

            if ($dbRes) {
                $output->info('菜单表数据生成成功!');
                $successList[] = '菜单数据生成!';
            } else {
                $output->warning('菜单表数据生成失败');
            }

            $output->writeln('2.生成控制器...');
            if (is_file($this->controllerFullPath)) {
                $output->error('控制器: '.$controller.' 已存在!');
            }

            if (!is_dir(dirname($this->controllerFullPath))) {
                mkdir(dirname($this->controllerFullPath), 0755, true);
            }

            // 生成控制器
            file_put_contents($this->controllerFullPath, $this->buildController());
            $output->info($this->controllerFullPath.' 控制器生成成功!');
            $successList[] = '创建文件:'.$this->controllerFullPath;

            $output->writeln('3.生成视图文件...');
            if (!is_dir($this->viewDir)) {
                mkdir($this->viewDir, 0755, true);
            }

            foreach (['index', 'add', 'edit'] as $action) {
                $actionHtml = sprintf('%s/%s.html', $this->viewDir, $action);

                if (is_file($actionHtml)) {
                    $output->error($actionHtml.' 视图文件已存在!');
                }

                file_put_contents($actionHtml, $this->buildView($action));
                $output->info($actionHtml.' 文件创建成功');
                $successList[] = '创建文件:'.$actionHtml;
            }

            $output->info('视图文件生成成功!');

            $jsPath = sprintf('%s/%s.js', $this->jsFullDir, unCamelize($this->className, '_'));
            $output->writeln('4.生成js文件...');
            if (!is_dir($this->jsFullDir)) {
                mkdir($this->jsFullDir, 0755, true);
            }

            file_put_contents($jsPath, $this->buildJs());
            $output->info($jsPath.' js文件创建成功!');
            $successList[] = '创建文件:'.$jsPath;

            $output->newLine();
            $output->writeln('信息汇总');
            // 汇总信息
            foreach ($successList as $success) {
                $output->info($success);
            }

            $output->newLine();
            $output->info(sprintf('此时可访问路径查看是否创建成功 /%s/index', unCamelize($controller)));
            $output->newLine();
            $output->warning('请注意,控制器中 $noNeedRight 默认开启了所有权限,如需纳入权限控制,请删除并在角色中选择对应权限');

            // 指令输出
            $output->info('创建完成');
        } catch (\Exception $e) {
            $output->error($e->getMessage());
        }
    }

    /**
     * 构建控制器内容
     *
     * @return array|false|string|string[]
     */
    protected function buildController()
    {
        $controllerStubPath = __DIR__.DIRECTORY_SEPARATOR.'stubs/controller.stub';
        $stub               = file_get_contents($controllerStubPath);

        return str_replace(['{%className%}', '{%namespace%}'], [
            $this->className,
            $this->namespace,
        ], $stub);
    }

    /**
     * 构建视图文件内容
     *
     * @param  string  $action
     * @return array|false|string|string[]
     */
    protected function buildView(string $action)
    {
        $actionStubPath = __DIR__.DIRECTORY_SEPARATOR.'stubs/'.$action.'.stub';
        $stub           = file_get_contents($actionStubPath);

        return str_replace(['{%path%}', '{%jsPath%}'], [
            '/'.unCamelize($this->controller),
            $this->jsDir.'/'.unCamelize($this->className, '_').'.js',
        ], $stub);
    }

    /**
     * 构建js文件内容
     *
     * @return false|string
     */
    protected function buildJs()
    {
        $actionStubPath = __DIR__.DIRECTORY_SEPARATOR.'stubs/js.stub';
        return file_get_contents($actionStubPath);
    }

    /**
     * 获取控制器完整路径
     *
     * @return string
     */
    protected function getControllerPathName(): string
    {
        return $this->app->getBasePath().'controller/'.ltrim(str_replace('\\', '/', $this->controller), '/').'.php';
    }

    /**
     * 获取对应view完整目录
     *
     * @return string
     */
    protected function getViewDirName(): string
    {
        return $this->app->getRootPath().'view/'.ltrim(unCamelize($this->controller, '_'), '/');
    }

    /**
     * 获取对应js完整目录
     *
     * @return string
     */
    protected function getJsFullDirName(): string
    {
        $dir = $this->app->getRootPath().'public/static/'.ltrim(unCamelize($this->controller, '_'), '/');
        return str_replace('/manager/', '/manager/js/', $dir);
    }

    /**
     * 获取对应js相对目录
     *
     * @return string
     */
    protected function getJsDirName(): string
    {
        return explode('/manager/', $this->jsFullDir)[1] ?? '';
    }

    /**
     * 自动添加菜单表数据
     *
     * @param  array  $data
     * @return bool
     * @throws \Exception
     */
    public function createDb(array $data): bool
    {
        $menuTable = 'menu';
        $now       = date('Y-m-d H:i:s');

        if (!isset($data['title']) || !isset($data['name']) || !isset($data['controller'])) {
            throw new \Exception('参数错误 请添加:--title 标题 --name 权限标识,如aaa/bbb --controller 控制器 如manager/aaa/Bbb');
        }

        if (empty($data['title']) || empty($data['name']) || empty($data['controller'])) {
            throw new \Exception('参数错误 请添加:--title 标题 --name 权限标识,如aaa/bbb --controller 控制器 如manager/aaa/Bbb');
        }

        // 检查权限标识是否已存在
        $already = Db::name('menu')->where('name', $data['name'])->whereOr('title', $data['title'])->count();
        if ($already) {
            throw new \Exception('菜单名称或标识已存在');
        }

        $href         = unCamelize($data['controller']);
        $insertMaster = [
            'pid'        => 0,
            'title'      => $data['title'], //名称
            'icon'       => $data['icon'] ?? 'fa-align-justify', //菜单图标
            'name'       => $data['name'], //权限标识
            'href'       => $href.'/index',//链接
            'type'       => 'menu',
            'is_show'    => 1,
            'status'     => 1,
            'remark'     => 'command generate',
            'created_at' => $now,
        ];

        $masterId = Db::name($menuTable)->insertGetId($insertMaster);

        $masterPath = sprintf(',0,%d,', $masterId);
        Db::name($menuTable)->where('id', $masterId)->update([
            'path' => $masterPath
        ]);

        $insertName = trim($data['name'], '/');
        $insertHref = '/'.trim($href, '/');

        $actionArr = $this->actionList();

        // 基于权限标识生成下级列表 默认index add del edit modify
        $insertAll = [];
        foreach (array_keys($actionArr) as $action) {
            $insertAll[] = [
                'pid'        => $masterId,
                'title'      => $actionArr[$action]['title'], //名称
                'icon'       => '', //菜单图标
                'name'       => $insertName.':'.$action, //权限标识
                'href'       => $insertHref.'/'.$action,//链接
                'type'       => 'action',
                'is_show'    => 1,
                'status'     => 1,
                'remark'     => 'command generate',
                'path'       => $masterPath,//此时的path为负极path
                'created_at' => $now,
            ];
        }

        // 插入自己菜单
        Db::name($menuTable)->insertAll($insertAll);

        // 批量更新自己菜单path
        Db::execute("update bee_$menuTable set `path`=concat(`path`, `id`, ',') where pid=$masterId");

        return true;
    }

    protected function actionList(): array
    {
        return [
            'index'  => ['title' => '查看'],
            'add'    => ['title' => '添加'],
            'edit'   => ['title' => '编辑'],
            'del'    => ['title' => '删除'],
            'modify' => ['title' => '属性设置'],
            'sort'   => ['title' => '排序'],
        ];
    }
}