<?php

namespace Kylin\Modules\Console\Commands;

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

/**
 * 模块导出命令
 *
 * 将模块导出为压缩文件
 */
class ModuleExportCommand extends Command
{
    /**
     * 命令名称和签名
     *
     * @var string
     * 
     * 命令参数说明：
     * 1. {name} - 模块名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定要导出的模块名称
     *    - 示例：php artisan module:export game
     * 
     * 命令选项说明：
     * 1. {--path=} - 导出路径
     *    - 完整形式：--path
     *    - 数据类型：字符串
     *    - 用途：指定导出路径，默认为 storage/app/modules
     *    - 示例：php artisan module:export game --path=custom/path
     * 
     * 2. {--filename=} - 导出文件名
     *    - 完整形式：--filename
     *    - 数据类型：字符串
     *    - 用途：指定导出文件名，默认为模块名称
     *    - 示例：php artisan module:export game --filename=game_export
     */
    protected $signature = 'module:export {name : 模块名称} {--path= : 导出路径，默认为 storage/app/modules} {--filename= : 导出文件名，默认为模块名称}';

    /**
     * 命令描述
     *
     * @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
    {
        $name = (string) $this->argument('name');
        $path = $this->option('path') ?: storage_path('app/modules');
        $filename = $this->option('filename') ?: $name;

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

        // 确保导出目录存在
        if (!$this->files->exists($path)) {
            $this->files->makeDirectory($path, 0755, true);
        }

        $modulePath = $module->getPath();
        $exportFile = $path . '/' . $filename . '.zip';

        try {
            $this->info("正在导出模块 [{$name}]...");

            // 创建临时目录
            $tempDir = sys_get_temp_dir() . '/module-export-' . uniqid();
            $this->files->makeDirectory($tempDir, 0755, true);

            // 复制模块文件到临时目录
            $this->copyDirectory($modulePath, $tempDir);

            // 创建压缩文件
            $this->createZipArchive($tempDir, $exportFile);

            // 清理临时目录
            $this->files->deleteDirectory($tempDir);

            $this->info("模块 [{$name}] 已成功导出到: {$exportFile}");

            return 0;
        } catch (\Exception $e) {
            $this->error("导出模块失败: {$e->getMessage()}");
            return 1;
        }
    }

    /**
     * 复制目录
     *
     * @param  string  $source  源目录
     * @param  string  $target  目标目录
     */
    protected function copyDirectory(string $source, string $target): void
    {
        if (!$this->files->isDirectory($source)) {
            return;
        }

        // 创建目标目录
        if (!$this->files->exists($target)) {
            $this->files->makeDirectory($target, 0755, true);
        }

        // 获取源目录中的文件和子目录
        $files = $this->files->files($source);
        $directories = $this->files->directories($source);

        // 复制文件
        foreach ($files as $file) {
            $targetFile = $target . '/' . $file->getFilename();
            $this->files->copy($file, $targetFile);
        }

        // 递归复制子目录
        foreach ($directories as $directory) {
            $dirName = basename($directory);
            $targetDir = $target . '/' . $dirName;
            $this->copyDirectory($directory, $targetDir);
        }
    }

    /**
     * 创建 ZIP 压缩文件
     *
     * @param  string  $source  源目录
     * @param  string  $target  目标 ZIP 文件
     * @throws \Exception
     */
    protected function createZipArchive(string $source, string $target): void
    {
        $zip = new \ZipArchive();

        if (!$zip->open($target, \ZipArchive::CREATE | \ZipArchive::OVERWRITE)) {
            throw new \Exception("无法创建压缩文件: {$target}");
        }

        // 递归添加文件到 ZIP
        $this->addFilesToZip($zip, $source, '');

        if (!$zip->close()) {
            throw new \Exception("无法关闭压缩文件: {$target}");
        }
    }

    /**
     * 添加文件到 ZIP 压缩文件
     *
     * @param  \ZipArchive  $zip  ZIP 实例
     * @param  string  $source  源目录
     * @param  string  $parentPath  父路径
     */
    protected function addFilesToZip(\ZipArchive $zip, string $source, string $parentPath): void
    {
        $files = $this->files->files($source);
        $directories = $this->files->directories($source);

        // 添加文件
        foreach ($files as $file) {
            $filePath = $parentPath . '/' . $file->getFilename();
            $zip->addFile($file, ltrim($filePath, '/'));
        }

        // 递归添加子目录
        foreach ($directories as $directory) {
            $dirName = basename($directory);
            $newParentPath = $parentPath . '/' . $dirName;
            $this->addFilesToZip($zip, $directory, $newParentPath);
        }
    }
}
