<?php

namespace Kylin\Modules\Console\Commands;

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

/**
 * 模块队列管理命令
 *
 * 管理模块的队列任务，帮助开发者更好地处理模块的后台任务
 */
class ModuleQueueCommand extends Command
{
    /**
     * 命令名称和签名
     *
     * @var string
     * 
     * 命令参数说明：
     * 1. {module} - 模块名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定要管理队列任务的模块名称
     *    - 示例：php artisan module:queue game
     * 
     * 命令选项说明：
     * 1. {--list} - 列出模块的所有队列任务
     *    - 完整形式：--list
     *    - 用途：列出模块的所有队列任务
     *    - 示例：php artisan module:queue game --list
     * 
     * 2. {--make=} - 创建新的队列任务
     *    - 完整形式：--make
     *    - 数据类型：字符串
     *    - 用途：创建新的队列任务（如：job:JobName）
     *    - 示例：php artisan module:queue game --make=job:ProcessPayment
     * 
     * 3. {--table} - 创建新的失败任务表
     *    - 完整形式：--table
     *    - 用途：创建新的失败任务表
     *    - 示例：php artisan module:queue game --table
     * 
     * 4. {--failed} - 列出失败的队列任务
     *    - 完整形式：--failed
     *    - 用途：列出失败的队列任务
     *    - 示例：php artisan module:queue game --failed
     * 
     * 5. {--retry} - 重试失败的队列任务
     *    - 完整形式：--retry
     *    - 用途：重试失败的队列任务
     *    - 示例：php artisan module:queue game --retry
     * 
     * 6. {--forget=} - 忘记失败的队列任务
     *    - 完整形式：--forget
     *    - 数据类型：字符串
     *    - 用途：忘记失败的队列任务（如：1,2,3）
     *    - 示例：php artisan module:queue game --forget=1,2,3
     * 
     * 7. {--flush} - 清空所有失败的队列任务
     *    - 完整形式：--flush
     *    - 用途：清空所有失败的队列任务
     *    - 示例：php artisan module:queue game --flush
     */
    protected $signature = 'module:queue {module : 模块名称} {--list : 列出模块的所有队列任务} {--make= : 创建新的队列任务（如：job:JobName）} {--table : 创建新的失败任务表} {--failed : 列出失败的队列任务} {--retry : 重试失败的队列任务} {--forget= : 忘记失败的队列任务（如：1,2,3）} {--flush : 清空所有失败的队列任务}';

    /**
     * 命令描述
     *
     * @var string
     */
    protected $description = '管理模块的队列任务，帮助开发者更好地处理模块的后台任务';

    /**
     * 文件系统实例
     *
     * @var Filesystem
     */
    private readonly Filesystem $files;

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

    /**
     * 执行控制台命令
     */
    public function handle(): int
    {
        $moduleName = (string) $this->argument('module');
        $list = $this->option('list');
        $make = $this->option('make');
        $table = $this->option('table');
        $failed = $this->option('failed');
        $retry = $this->option('retry');
        $forget = $this->option('forget');
        $flush = $this->option('flush');

        // 检查模块是否存在
        $module = $this->moduleManager->get($moduleName);
        if (!$module) {
            $this->error("模块 [{$moduleName}] 未找到");
            return 1;
        }

        $modulePath = $module->getPath();
        $srcPath = "{$modulePath}/src";
        $jobsPath = "{$srcPath}/Jobs";

        // 确保目录存在
        if (!($this->files->exists($jobsPath))) {
            $this->files->makeDirectory($jobsPath, 0755, true);
            $this->info("创建队列任务目录: {$jobsPath}");
        }

        // 列出模块的所有队列任务
        if ($list) {
            return $this->listJobs($moduleName, $jobsPath);
        }

        // 创建新的队列任务
        if ($make) {
            return $this->makeJob($moduleName, $srcPath, $make);
        }

        // 创建新的失败任务表
        if ($table) {
            return $this->createFailedJobsTable();
        }

        // 列出失败的队列任务
        if ($failed) {
            return $this->listFailedJobs();
        }

        // 重试失败的队列任务
        if ($retry) {
            return $this->retryFailedJobs();
        }

        // 忘记失败的队列任务
        if ($forget) {
            return $this->forgetFailedJobs((string) $forget);
        }

        // 清空所有失败的队列任务
        if ($flush) {
            return $this->flushFailedJobs();
        }

        // 默认列出模块的所有队列任务
        return $this->listJobs($moduleName, $jobsPath);
    }

    /**
     * 列出模块的所有队列任务
     *
     * @param  string  $moduleName  模块名称
     * @param  string  $jobsPath  队列任务目录路径
     * @return int  退出码
     */
    protected function listJobs(string $moduleName, string $jobsPath): int
    {
        $this->info("模块 [{$moduleName}] 的队列任务:");

        $jobs = $this->files->files($jobsPath);

        if (empty($jobs)) {
            $this->info('  没有找到队列任务');
            return 0;
        }

        foreach ($jobs as $job) {
            $jobName = $job->getFilenameWithoutExtension();
            $this->line("  - {$jobName}");
        }

        return 0;
    }

    /**
     * 创建新的队列任务
     *
     * @param  string  $moduleName  模块名称
     * @param  string  $srcPath  源码目录路径
     * @param  string  $make  创建类型和名称
     * @return int  退出码
     */
    protected function makeJob(string $moduleName, string $srcPath, string $make): int
    {
        $parts = explode(':', $make);
        if (count($parts) !== 2 || $parts[0] !== 'job') {
            $this->error('创建格式错误，请使用：--make=job:JobName');
            return 1;
        }

        $jobName = $parts[1];
        $jobsPath = "{$srcPath}/Jobs";
        $jobFile = "{$jobsPath}/{$jobName}.php";

        if ($this->files->exists($jobFile)) {
            $this->error("队列任务 [{$jobName}] 已存在");
            return 1;
        }

        $namespace = "{$moduleName}\Jobs";
        $content = "<?php\n\nnamespace {$namespace};\n\nuse Illuminate\Bus\Queueable;\nuse Illuminate\Contracts\Queue\ShouldQueue;\nuse Illuminate\Foundation\Bus\Dispatchable;\nuse Illuminate\Queue\InteractsWithQueue;\nuse Illuminate\Queue\SerializesModels;\n\nclass {$jobName} implements ShouldQueue\n{\n    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;\n\n    /**\n     * 创建一个新的任务实例\n     *\n     * @return void\n     */\n    public function __construct()\n    {\n        //\n    }\n\n    /**\n     * 执行任务\n     *\n     * @return void\n     */\n    public function handle()\n    {\n        //\n    }\n}\n";

        $this->files->put($jobFile, $content);
        $this->info("创建队列任务: {$jobFile}");

        return 0;
    }

    /**
     * 创建新的失败任务表
     *
     * @return int  退出码
     */
    protected function createFailedJobsTable(): int
    {
        $this->info('创建失败任务表...');

        // 运行全局失败任务表创建命令
        $this->call('queue:failed-table');

        $this->info('失败任务表创建完成');

        return 0;
    }

    /**
     * 列出失败的队列任务
     *
     * @return int  退出码
     */
    protected function listFailedJobs(): int
    {
        $this->info('列出失败的队列任务...');

        // 运行全局失败任务列出命令
        $this->call('queue:failed');

        return 0;
    }

    /**
     * 重试失败的队列任务
     *
     * @return int  退出码
     */
    protected function retryFailedJobs(): int
    {
        $this->info('重试失败的队列任务...');

        // 运行全局失败任务重试命令
        $this->call('queue:retry', ['ids' => 'all']);

        return 0;
    }

    /**
     * 忘记失败的队列任务
     *
     * @param  string  $ids  失败任务的 ID 列表
     * @return int  退出码
     */
    protected function forgetFailedJobs(string $ids): int
    {
        $this->info("忘记失败的队列任务: {$ids}");

        // 运行全局失败任务忘记命令
        $this->call('queue:forget', ['id' => $ids]);

        return 0;
    }

    /**
     * 清空所有失败的队列任务
     *
     * @return int  退出码
     */
    protected function flushFailedJobs(): int
    {
        $this->info('清空所有失败的队列任务...');

        // 运行全局失败任务清空命令
        $this->call('queue:flush');

        return 0;
    }
}
