<?php

namespace Kylin\Modules\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use Kylin\Modules\ModuleManager;

/**
 * 创建模块控制器命令
 *
 * 用于为指定模块创建新的控制器类
 */
class ModuleMakeControllerCommand extends Command
{
    /**
     * 命令名称和签名
     *
     * @var string
     * 
     * 命令参数说明：
     * 1. {module} - 模块名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定控制器所属的模块名称
     *    - 示例：php artisan module:make-controller game UserController
     * 
     * 2. {name} - 控制器名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定要创建的控制器名称
     *    - 示例：php artisan module:make-controller game UserController
     * 
     * 命令选项说明：
     * 1. {--r|resource} - 生成资源控制器
     *    - 简写：-r
     *    - 完整形式：--resource
     *    - 用途：生成一个包含CRUD操作的资源控制器
     *    - 示例：php artisan module:make-controller game UserController -r
     * 
     * 2. {--m|model=} - 为给定模型生成资源控制器
     *    - 简写：-m
     *    - 完整形式：--model
     *    - 数据类型：字符串
     *    - 用途：指定与控制器关联的模型名称
     *    - 示例：php artisan module:make-controller game UserController -m User
     * 
     * 3. {--i|invokable} - 生成单一动作控制器
     *    - 简写：-i
     *    - 完整形式：--invokable
     *    - 用途：生成一个只有__invoke方法的单一动作控制器
     *    - 示例：php artisan module:make-controller game DashboardController -i
     * 
     * 4. {--p|parent=} - 生成嵌套资源控制器
     *    - 简写：-p
     *    - 完整形式：--parent
     *    - 数据类型：字符串
     *    - 用途：指定父资源控制器名称
     *    - 示例：php artisan module:make-controller game PostCommentController -p PostController
     * 
     * 5. {--a|api} - 生成 API 控制器
     *    - 简写：-a
     *    - 完整形式：--api
     *    - 用途：生成一个适合API的控制器，不包含create和edit方法
     *    - 示例：php artisan module:make-controller game UserController -a
     */
    protected $signature = 'module:make-controller {module : 模块名称} {name : 控制器名称} {--r|resource : 生成资源控制器} {--m|model= : 为给定模型生成资源控制器} {--i|invokable : 生成单一动作控制器} {--p|parent= : 生成嵌套资源控制器} {--a|api : 生成 API 控制器}';

    /**
     * 命令描述
     *
     * @var string
     */
    protected $description = '为模块创建新的控制器类';

    /**
     * 创建新命令实例
     *
     * @param  Filesystem  $files  文件系统实例
     * @param  ModuleManager  $moduleManager  模块管理器实例
     */
    public function __construct(
        private readonly Filesystem $files,
        private readonly ModuleManager $moduleManager
    ) {
        parent::__construct();
    }

    /**
     * 执行控制台命令
     */
    public function handle(): int
    {
        $moduleName = (string) $this->argument('module');
        $controllerName = (string) $this->argument('name');
        $resource = (bool) $this->option('resource');
        $model = $this->option('model');
        $invokable = (bool) $this->option('invokable');
        $parent = $this->option('parent');
        $api = (bool) $this->option('api');

        $module = $this->moduleManager->get($moduleName);

        if (!$module) {
            $this->error("模块 [{$moduleName}] 不存在。");
            return 1;
        }

        $controllerPath = $this->getControllerPath($module, $controllerName);
        $controllerNamespace = $this->getControllerNamespace($module, $controllerName);
        $controllerClass = $this->getControllerClass($controllerName);

        if ($this->files->exists($controllerPath)) {
            $this->error("控制器 [{$controllerClass}] 在模块 [{$moduleName}] 中已存在。");
            return 1;
        }

        if (!$this->files->isDirectory(dirname($controllerPath))) {
            $this->files->makeDirectory(dirname($controllerPath), 0755, true);
        }

        $stub = $this->getStub($resource, $model, $invokable, $parent, $api);
        $content = $this->replaceStubVariables($stub, $controllerNamespace, $controllerClass, $model, $parent);

        $this->files->put($controllerPath, $content);

        $this->info("控制器 [{$controllerClass}] 在模块 [{$moduleName}] 中创建成功！");
        $this->info("控制器路径: {$controllerPath}");

        return 0;
    }

    /**
     * 获取控制器路径
     *
     * @param  \Kylin\Modules\Module  $module  模块实例
     * @param  string  $controllerName  控制器名称
     * @return string
     */
    protected function getControllerPath($module, string $controllerName): string
    {
        $controllerPath = str_replace('\\', '/', $controllerName);
        return $module->getPath('src/Http/Controllers/' . $controllerPath . '.php');
    }

    /**
     * 获取控制器命名空间
     *
     * @param  \Kylin\Modules\Module  $module  模块实例
     * @param  string  $controllerName  控制器名称
     * @return string
     */
    protected function getControllerNamespace($module, string $controllerName): string
    {
        $namespace = $module->getNamespace();
        $controllerPath = str_replace('/', '\\', dirname(str_replace('\\', '/', $controllerName)));
        
        if ($controllerPath != '.') {
            $namespace .= '\\Http\\Controllers\\' . $controllerPath;
        } else {
            $namespace .= '\\Http\\Controllers';
        }
        
        return $namespace;
    }

    /**
     * 获取控制器类名
     *
     * @param  string  $controllerName  控制器名称
     * @return string
     */
    protected function getControllerClass(string $controllerName): string
    {
        return basename(str_replace('\\', '/', $controllerName));
    }

    /**
     * 获取模板文件
     *
     * @param  bool  $resource  是否生成资源控制器
     * @param  string|null  $model  模型名称
     * @param  bool  $invokable  是否生成单一动作控制器
     * @param  string|null  $parent  父控制器名称
     * @param  bool  $api  是否生成 API 控制器
     * @return string
     */
    protected function getStub(bool $resource, mixed $model, bool $invokable, mixed $parent, bool $api): string
    {
        if ($invokable) {
            $stubPath = __DIR__ . '/../../../stubs/controller.invokable.stub';
            return $this->files->get($stubPath);
        }

        if ($model && $resource) {
            $stubPath = __DIR__ . '/../../../stubs/controller.model.stub';
            return $this->files->get($stubPath);
        }

        if ($parent) {
            $stubPath = __DIR__ . '/../../../stubs/controller.nested.stub';
            return $this->files->get($stubPath);
        }

        if ($api) {
            $stubPath = __DIR__ . '/../../../stubs/controller.api.stub';
            return $this->files->get($stubPath);
        }

        if ($resource) {
            $stubPath = __DIR__ . '/../../../stubs/controller.resource.stub';
            return $this->files->get($stubPath);
        }

        $stubPath = __DIR__ . '/../../../stubs/controller.plain.stub';
        return $this->files->get($stubPath);
    }

    /**
     * 替换模板变量
     *
     * @param  string  $stub  模板内容
     * @param  string  $namespace  命名空间
     * @param  string  $class  类名
     * @param  string|null  $model  模型名称
     * @param  string|null  $parent  父控制器名称
     * @return string
     */
    protected function replaceStubVariables(string $stub, string $namespace, string $class, mixed $model, mixed $parent): string
    {
        $stub = str_replace('{{ namespace }}', $namespace, $stub);
        $stub = str_replace('{{ class }}', $class, $stub);

        if ($model) {
            $modelClass = Str::studly((string) $model);
            $modelVariable = Str::camel((string) $model);
            $stub = str_replace('{{ model }}', $modelClass, $stub);
            $stub = str_replace('{{ modelVariable }}', $modelVariable, $stub);
        }

        if ($parent) {
            $parentClass = Str::studly((string) $parent);
            $parentVariable = Str::camel((string) $parent);
            $stub = str_replace('{{ parent }}', $parentClass, $stub);
            $stub = str_replace('{{ parentVariable }}', $parentVariable, $stub);
        }

        return $stub;
    }
}