<?php

namespace Kylin\Modules\Console\Commands;

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

/**
 * 创建模块模型命令
 *
 * 用于为指定模块创建新的模型类
 */
class ModuleMakeModelCommand extends Command
{
    /**
     * 命令名称和签名
     *
     * @var string
     * 
     * 命令参数说明：
     * 1. {module} - 模块名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定模型所属的模块名称
     *    - 示例：php artisan module:make-model game User
     * 
     * 2. {name} - 模型名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定要创建的模型名称
     *    - 示例：php artisan module:make-model game User
     * 
     * 命令选项说明：
     * 1. {--m|migration} - 为模型创建新的迁移文件
     *    - 简写：-m
     *    - 完整形式：--migration
     *    - 用途：生成一个与模型对应的数据库迁移文件
     *    - 示例：php artisan module:make-model game User -m
     * 
     * 2. {--c|controller} - 为模型创建新的控制器
     *    - 简写：-c
     *    - 完整形式：--controller
     *    - 用途：生成一个与模型对应的控制器文件
     *    - 示例：php artisan module:make-model game User -c
     * 
     * 3. {--r|resource} - 为模型创建新的资源控制器
     *    - 简写：-r
     *    - 完整形式：--resource
     *    - 用途：生成一个与模型对应的资源控制器文件，包含CRUD操作
     *    - 示例：php artisan module:make-model game User -r
     * 
     * 4. {--f|factory} - 为模型创建新的工厂
     *    - 简写：-f
     *    - 完整形式：--factory
     *    - 用途：生成一个与模型对应的工厂文件，用于测试数据生成
     *    - 示例：php artisan module:make-model game User -f
     * 
     * 5. {--s|seed} - 为模型创建新的种子
     *    - 简写：-s
     *    - 完整形式：--seed
     *    - 用途：生成一个与模型对应的种子文件，用于数据库初始化数据
     *    - 示例：php artisan module:make-model game User -s
     * 
     * 6. {--pivot} - 指示模型是否为关联模型
     *    - 完整形式：--pivot
     *    - 用途：指定模型为关联模型（多对多关系）
     *    - 示例：php artisan module:make-model game UserRole --pivot
     */
    protected $signature = 'module:make-model {module : 模块名称} {name : 模型名称} {--m|migration : 为模型创建新的迁移文件} {--c|controller : 为模型创建新的控制器} {--r|resource : 为模型创建新的资源控制器} {--f|factory : 为模型创建新的工厂} {--s|seed : 为模型创建新的种子} {--pivot : 指示模型是否为关联模型}';

    /**
     * 命令描述
     *
     * @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');
        $modelName = (string) $this->argument('name');
        $migration = (bool) $this->option('migration');
        $controller = (bool) $this->option('controller');
        $resource = (bool) $this->option('resource');
        $factory = (bool) $this->option('factory');
        $seed = (bool) $this->option('seed');
        $pivot = (bool) $this->option('pivot');

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

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

        $modelPath = $this->getModelPath($module, $modelName);
        $modelNamespace = $this->getModelNamespace($module, $modelName);
        $modelClass = $this->getModelClass($modelName);

        if ($this->files->exists($modelPath)) {
            $this->error("模型 [{$modelClass}] 在模块 [{$moduleName}] 中已存在。");
            return 1;
        }

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

        $stub = $this->getStub($pivot);
        $content = $this->replaceStubVariables($stub, $modelNamespace, $modelClass);

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

        $this->info("模型 [{$modelClass}] 在模块 [{$moduleName}] 中创建成功！");
        $this->info("模型路径: {$modelPath}");

        // 如果请求，创建迁移
        if ($migration) {
            $this->call('module:make-migration', [
                'module' => $moduleName,
                'name' => 'create_' . Str::snake(Str::plural($modelClass)) . '_table',
                '--create' => Str::snake(Str::plural($modelClass)),
            ]);
        }

        // 如果请求，创建控制器
        if ($controller || $resource) {
            $controllerCommand = $resource ? 'module:make-controller' : 'module:make-controller';
            $controllerArgs = [
                'module' => $moduleName,
                'name' => $modelClass . 'Controller',
            ];

            if ($resource) {
                $controllerArgs['--resource'] = true;
                $controllerArgs['--model'] = $modelClass;
            }

            $this->call($controllerCommand, $controllerArgs);
        }

        // 如果请求，创建工厂
        if ($factory) {
            $this->call('module:make-factory', [
                'module' => $moduleName,
                'name' => $modelClass . 'Factory',
                '--model' => $modelClass,
            ]);
        }

        // 如果请求，创建种子
        if ($seed) {
            $this->call('module:make-seeder', [
                'module' => $moduleName,
                'name' => $modelClass . 'Seeder',
            ]);
        }

        return 0;
    }

    /**
     * 获取模型路径
     *
     * @param  \Kylin\Modules\Module  $module  模块实例
     * @param  string  $modelName  模型名称
     * @return string
     */
    protected function getModelPath($module, string $modelName): string
    {
        $modelPath = str_replace('\\', '/', $modelName);
        return $module->getPath('src/Models/' . $modelPath . '.php');
    }

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

    /**
     * 获取模型类名
     *
     * @param  string  $modelName  模型名称
     * @return string
     */
    protected function getModelClass(string $modelName): string
    {
        return basename(str_replace('\\', '/', $modelName));
    }

    /**
     * 获取模板文件
     *
     * @param  bool  $pivot  是否为关联模型
     * @return string
     */
    protected function getStub(bool $pivot): string
    {
        if ($pivot) {
            $stubPath = __DIR__ . '/../../../stubs/model.pivot.stub';
            return $this->files->get($stubPath);
        }

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

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

        return $stub;
    }
}