<?php

namespace Kylin\Modules\Console\Commands;

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

/**
 * 创建模块工厂命令
 *
 * 用于为指定模块创建新的工厂类
 */
class ModuleMakeFactoryCommand extends Command
{
    /**
     * 命令名称和签名
     *
     * @var string
     * 
     * 命令参数说明：
     * 1. {module} - 模块名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定工厂类所属的模块名称
     *    - 示例：php artisan module:make-factory game UserFactory
     * 
     * 2. {name} - 工厂类名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定要创建的工厂类名称
     *    - 示例：php artisan module:make-factory game UserFactory
     * 
     * 命令选项说明：
     * 1. {--m|model=} - 工厂对应的模型
     *    - 简写：-m
     *    - 完整形式：--model
     *    - 数据类型：字符串
     *    - 用途：指定与工厂关联的模型名称
     *    - 示例：php artisan module:make-factory game UserFactory --model=User
     */
    protected $signature = 'module:make-factory {module : 模块名称} {name : 工厂类名称} {--m|model= : 工厂对应的模型}';

    /**
     * 命令描述
     *
     * @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');
        $factoryName = (string) $this->argument('name');
        $model = $this->option('model');

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

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

        $factoryPath = $this->getFactoryPath($module, $factoryName);
        $factoryNamespace = $this->getFactoryNamespace($module);
        $factoryClass = $this->getFactoryClass($factoryName);

        if ($this->files->exists($factoryPath)) {
            $this->error("工厂 [{$factoryClass}] 在模块 [{$moduleName}] 中已存在。");
            return 1;
        }

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

        $stub = $this->getStub();
        $content = $this->replaceStubVariables($stub, $factoryNamespace, $factoryClass, $model, $module);

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

        $this->info("工厂 [{$factoryClass}] 在模块 [{$moduleName}] 中创建成功！");
        $this->info("工厂路径: {$factoryPath}");

        return 0;
    }

    /**
     * 获取工厂路径
     *
     * @param  \Kylin\Modules\Module  $module  模块实例
     * @param  string  $factoryName  工厂名称
     * @return string
     */
    protected function getFactoryPath($module, string $factoryName): string
    {
        return $module->getPath('database/factories/' . $factoryName . '.php');
    }

    /**
     * 获取工厂命名空间
     *
     * @param  \Kylin\Modules\Module  $module  模块实例
     * @return string
     */
    protected function getFactoryNamespace($module): string
    {
        return $module->getNamespace() . '\\Database\\Factories';
    }

    /**
     * 获取工厂类名
     *
     * @param  string  $factoryName  工厂名称
     * @return string
     */
    protected function getFactoryClass(string $factoryName): string
    {
        return $factoryName;
    }

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

    /**
     * 替换模板变量
     *
     * @param  string  $stub  模板内容
     * @param  string  $namespace  命名空间
     * @param  string  $class  类名
     * @param  string|null  $model  模型名称
     * @param  \Kylin\Modules\Module  $module  模块实例
     * @return string
     */
    protected function replaceStubVariables(string $stub, string $namespace, string $class, mixed $model, $module): string
    {
        $stub = str_replace('{{namespace}}', $namespace, $stub);
        $stub = str_replace('{{class}}', $class, $stub);

        if ($model) {
            $modelNamespace = $module->getNamespace() . '\\Models\\' . $model;
            $stub = str_replace('{{modelNamespace}}', $modelNamespace, $stub);
            $stub = str_replace('{{model}}', $model, $stub);
            $stub = str_replace('{{modelVariable}}', Str::camel((string) $model), $stub);
        } else {
            // 默认使用通用模型
            $stub = str_replace('{{modelNamespace}}', '\\Illuminate\\Database\\Eloquent\\Model', $stub);
            $stub = str_replace('{{model}}', 'Model', $stub);
            $stub = str_replace('{{modelVariable}}', 'model', $stub);
        }

        return $stub;
    }
}