<?php

namespace Kylin\Modules\Console\Commands;

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

/**
 * 模块事件管理命令
 *
 * 管理模块的事件和监听器，帮助开发者更好地处理模块的事件系统
 */
class ModuleEventCommand extends Command
{
    /**
     * 命令名称和签名
     *
     * @var string
     * 
     * 命令参数说明：
     * 1. {module} - 模块名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定要管理事件的模块名称
     *    - 示例：php artisan module:event game
     * 
     * 命令选项说明：
     * 1. {--list} - 列出模块的所有事件和监听器
     *    - 完整形式：--list
     *    - 用途：列出模块的所有事件和监听器
     *    - 示例：php artisan module:event game --list
     * 
     * 2. {--make=} - 创建新的事件或监听器
     *    - 完整形式：--make
     *    - 数据类型：字符串
     *    - 用途：创建新的事件或监听器（如：event:EventName 或 listener:ListenerName）
     *    - 示例：php artisan module:event game --make=event:UserRegistered
     * 
     * 3. {--register} - 注册模块的事件和监听器
     *    - 完整形式：--register
     *    - 用途：注册模块的事件和监听器
     *    - 示例：php artisan module:event game --register
     * 
     * 4. {--clear} - 清除模块的事件和监听器缓存
     *    - 完整形式：--clear
     *    - 用途：清除模块的事件和监听器缓存
     *    - 示例：php artisan module:event game --clear
     */
    protected $signature = 'module:event {module : 模块名称} {--list : 列出模块的所有事件和监听器} {--make= : 创建新的事件或监听器（如：event:EventName 或 listener:ListenerName）} {--register : 注册模块的事件和监听器} {--clear : 清除模块的事件和监听器缓存}';

    /**
     * 命令描述
     *
     * @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');
        $register = $this->option('register');
        $clear = $this->option('clear');

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

        $modulePath = $module->getPath();
        $srcPath = "{$modulePath}/src";
        $eventsPath = "{$srcPath}/Events";
        $listenersPath = "{$srcPath}/Listeners";

        // 确保目录存在
        $this->ensureDirectoriesExist($eventsPath, $listenersPath);

        // 列出模块的所有事件和监听器
        if ($list) {
            return $this->listEvents($moduleName, $eventsPath, $listenersPath);
        }

        // 创建新的事件或监听器
        if ($make) {
            return $this->makeEventOrListener($moduleName, $srcPath, $make);
        }

        // 注册模块的事件和监听器
        if ($register) {
            return $this->registerEvents($moduleName, $srcPath);
        }

        // 清除模块的事件和监听器缓存
        if ($clear) {
            return $this->clearEventsCache($moduleName);
        }

        // 默认列出模块的所有事件和监听器
        return $this->listEvents($moduleName, $eventsPath, $listenersPath);
    }

    /**
     * 确保目录存在
     *
     * @param  string  ...$paths  目录路径
     */
    protected function ensureDirectoriesExist(string ...$paths): void
    {
        foreach ($paths as $path) {
            if (!($this->files->exists($path))) {
                $this->files->makeDirectory($path, 0755, true);
                $this->info("创建目录: {$path}");
            }
        }
    }

    /**
     * 列出模块的所有事件和监听器
     *
     * @param  string  $moduleName  模块名称
     * @param  string  $eventsPath  事件目录路径
     * @param  string  $listenersPath  监听器目录路径
     * @return int  退出码
     */
    protected function listEvents(string $moduleName, string $eventsPath, string $listenersPath): int
    {
        $this->info("模块 [{$moduleName}] 的事件和监听器:");
        $this->line('');

        // 列出事件
        $this->info('事件:');
        $events = $this->files->files($eventsPath);
        if (empty($events)) {
            $this->line('  没有找到事件');
        } else {
            foreach ($events as $event) {
                $eventName = $event->getFilenameWithoutExtension();
                $this->line("  - {$eventName}");
            }
        }

        $this->line('');

        // 列出监听器
        $this->info('监听器:');
        $listeners = $this->files->files($listenersPath);
        if (empty($listeners)) {
            $this->line('  没有找到监听器');
        } else {
            foreach ($listeners as $listener) {
                $listenerName = $listener->getFilenameWithoutExtension();
                $this->line("  - {$listenerName}");
            }
        }

        return 0;
    }

    /**
     * 创建新的事件或监听器
     *
     * @param  string  $moduleName  模块名称
     * @param  string  $srcPath  源码目录路径
     * @param  string  $make  创建类型和名称
     * @return int  退出码
     */
    protected function makeEventOrListener(string $moduleName, string $srcPath, string $make): int
    {
        $parts = explode(':', $make);
        if (count($parts) !== 2) {
            $this->error('创建格式错误，请使用：--make=event:EventName 或 --make=listener:ListenerName');
            return 1;
        }

        [$type, $name] = $parts;

        if ($type === 'event') {
            return $this->makeEvent($moduleName, $srcPath, $name);
        } elseif ($type === 'listener') {
            return $this->makeListener($moduleName, $srcPath, $name);
        } else {
            $this->error('创建类型错误，请使用 event 或 listener');
            return 1;
        }
    }

    /**
     * 创建新的事件
     *
     * @param  string  $moduleName  模块名称
     * @param  string  $srcPath  源码目录路径
     * @param  string  $name  事件名称
     * @return int  退出码
     */
    protected function makeEvent(string $moduleName, string $srcPath, string $name): int
    {
        $eventsPath = "{$srcPath}/Events";
        $eventFile = "{$eventsPath}/{$name}.php";

        if ($this->files->exists($eventFile)) {
            $this->error("事件 [{$name}] 已存在");
            return 1;
        }

        $namespace = "{$moduleName}\Events";
        $content = "<?php\n\nnamespace {$namespace};\n\nclass {$name}\n{\n    /**\n     * 创建一个新的事件实例\n     *\n     * @return void\n     */\n    public function __construct()\n    {\n        //\n    }\n}\n";

        $this->files->put($eventFile, $content);
        $this->info("创建事件: {$eventFile}");

        return 0;
    }

    /**
     * 创建新的监听器
     *
     * @param  string  $moduleName  模块名称
     * @param  string  $srcPath  源码目录路径
     * @param  string  $name  监听器名称
     * @return int  退出码
     */
    protected function makeListener(string $moduleName, string $srcPath, string $name): int
    {
        $listenersPath = "{$srcPath}/Listeners";
        $listenerFile = "{$listenersPath}/{$name}.php";

        if ($this->files->exists($listenerFile)) {
            $this->error("监听器 [{$name}] 已存在");
            return 1;
        }

        $namespace = "{$moduleName}\Listeners";
        $content = "<?php\n\nnamespace {$namespace};\n\nclass {$name}\n{\n    /**\n     * 创建一个新的监听器实例\n     *\n     * @return void\n     */\n    public function __construct()\n    {\n        //\n    }\n\n    /**\n     * 处理事件\n     *\n     * @param  mixed  $event  事件实例\n     * @return void\n     */\n    public function handle($event)\n    {\n        //\n    }\n}\n";

        $this->files->put($listenerFile, $content);
        $this->info("创建监听器: {$listenerFile}");

        return 0;
    }

    /**
     * 注册模块的事件和监听器
     *
     * @param  string  $moduleName  模块名称
     * @param  string  $srcPath  源码目录路径
     * @return int  退出码
     */
    protected function registerEvents(string $moduleName, string $srcPath): int
    {
        $eventServiceProviderFile = "{$srcPath}/Providers/EventServiceProvider.php";

        // 检查事件服务提供者是否存在
        if (!($this->files->exists($eventServiceProviderFile))) {
            $this->makeEventServiceProvider($moduleName, $srcPath);
        }

        $this->info("模块 [{$moduleName}] 的事件和监听器已注册");

        return 0;
    }

    /**
     * 创建事件服务提供者
     *
     * @param  string  $moduleName  模块名称
     * @param  string  $srcPath  源码目录路径
     */
    protected function makeEventServiceProvider(string $moduleName, string $srcPath): void
    {
        $providersPath = "{$srcPath}/Providers";
        $eventServiceProviderFile = "{$providersPath}/EventServiceProvider.php";

        // 确保提供者目录存在
        if (!($this->files->exists($providersPath))) {
            $this->files->makeDirectory($providersPath, 0755, true);
        }

        $namespace = "{$moduleName}\Providers";
        $content = "<?php\n\nnamespace {$namespace};\n\nuse Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;\n\nclass EventServiceProvider extends ServiceProvider\n{\n    /**\n     * 事件到监听器的映射\n     *\n     * @var array\n     */\n    protected $listen = [\n        //\n    ];\n\n    /**\n     * 注册任何其他事件\n     *\n     * @return void\n     */\n    public function boot()\n    {\n        parent::boot();\n\n        //\n    }\n}\n";

        $this->files->put($eventServiceProviderFile, $content);
        $this->info("创建事件服务提供者: {$eventServiceProviderFile}");
    }

    /**
     * 清除模块的事件和监听器缓存
     *
     * @param  string  $moduleName  模块名称
     * @return int  退出码
     */
    protected function clearEventsCache(string $moduleName): int
    {
        $this->info("清除模块 [{$moduleName}] 的事件和监听器缓存...");

        // 运行全局事件缓存清除命令
        $this->call('event:clear');

        $this->info("模块 [{$moduleName}] 的事件和监听器缓存清除完成");

        return 0;
    }
}
