<?php

namespace Kylin\Modules\Console\Commands;

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

/**
 * 创建模块命令命令
 *
 * 用于为指定模块创建新的 Artisan 命令
 */
class ModuleMakeCommandCommand extends Command
{
    /**
     * 命令名称和签名
     *
     * @var string
     * 
     * 命令参数说明：
     * 1. {module} - 模块名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定 Artisan 命令所属的模块名称
     *    - 示例：php artisan module:make-command game TestCommand
     * 
     * 2. {name} - 命令类名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定要创建的 Artisan 命令类名称
     *    - 示例：php artisan module:make-command game TestCommand
     * 
     * 命令选项说明：
     * 1. {--c|command=} - 要注册的终端命令
     *    - 简写：-c
     *    - 完整形式：--command
     *    - 数据类型：字符串
     *    - 用途：指定终端中要执行的命令名称
     *    - 示例：php artisan module:make-command game TestCommand --command=test:command
     */
    protected $signature = 'module:make-command {module : 模块名称} {name : 命令类名称} {--c|command= : 要注册的终端命令}';

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

    /**
     * 创建新命令实例
     *
     * @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');
        $commandName = (string) $this->argument('name');
        $commandSignature = $this->option('command') ?: 'module:' . Str::kebab($commandName);

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

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

        $commandPath = $this->getCommandPath($module, $commandName);
        $commandNamespace = $this->getCommandNamespace($module, $commandName);
        $commandClass = $this->getCommandClass($commandName);

        if ($this->files->exists($commandPath)) {
            $this->error("命令 [{$commandClass}] 在模块 [{$moduleName}] 中已存在。");
            return 1;
        }

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

        $stub = $this->getStub();
        $content = $this->replaceStubVariables($stub, $commandNamespace, $commandClass, (string) $commandSignature);

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

        $this->info("命令 [{$commandClass}] 在模块 [{$moduleName}] 中创建成功！");
        $this->info("命令路径: {$commandPath}");
        $this->info("命令签名: {$commandSignature}");

        return 0;
    }

    /**
     * 获取命令路径
     *
     * @param  \Kylin\Modules\Module  $module  模块实例
     * @param  string  $commandName  命令名称
     * @return string
     */
    protected function getCommandPath($module, string $commandName): string
    {
        $commandPath = str_replace('\\', '/', $commandName);
        return $module->getPath('src/Console/Commands/' . $commandPath . '.php');
    }

    /**
     * 获取命令命名空间
     *
     * @param  \Kylin\Modules\Module  $module  模块实例
     * @param  string  $commandName  命令名称
     * @return string
     */
    protected function getCommandNamespace($module, string $commandName): string
    {
        $namespace = $module->getNamespace();
        $commandPath = str_replace('/', '\\', dirname(str_replace('\\', '/', $commandName)));
        
        if ($commandPath != '.') {
            $namespace .= '\\Console\\Commands\\' . $commandPath;
        } else {
            $namespace .= '\\Console\\Commands';
        }
        
        return $namespace;
    }

    /**
     * 获取命令类名
     *
     * @param  string  $commandName  命令名称
     * @return string
     */
    protected function getCommandClass(string $commandName): string
    {
        return basename(str_replace('\\', '/', $commandName));
    }

    /**
     * 获取模板文件
     *
     * @return string
     */
    protected function getStub(): string
    {
        $stubPath = __DIR__ . '/../../../stubs/command.stub';
        return $this->files->get($stubPath);
    }

    /**
     * 替换模板变量
     *
     * @param  string  $stub  模板内容
     * @param  string  $namespace  命名空间
     * @param  string  $class  类名
     * @param  string  $signature  命令签名
     * @return string
     */
    protected function replaceStubVariables(string $stub, string $namespace, string $class, string $signature): string
    {
        $stub = str_replace('{{namespace}}', $namespace, $stub);
        $stub = str_replace('{{class}}', $class, $stub);
        $stub = str_replace('{{signature}}', $signature, $stub);

        return $stub;
    }
}