<?php

namespace Kylin\Modules\Console\Commands;

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

/**
 * 模块测试命令
 *
 * 运行模块的测试用例，帮助开发者确保模块的质量
 */
class ModuleTestCommand extends Command
{
    /**
     * 命令名称和签名
     *
     * @var string
     * 
     * 命令参数说明：
     * 1. {module} - 模块名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定要运行测试的模块名称
     *    - 示例：php artisan module:test game
     * 
     * 命令选项说明：
     * 1. {--filter=} - 过滤测试方法
     *    - 完整形式：--filter
     *    - 数据类型：字符串
     *    - 用途：指定要运行的测试方法名称
     *    - 示例：php artisan module:test game --filter=testUserCreation
     * 
     * 2. {--testsuite=} - 指定测试套件
     *    - 完整形式：--testsuite
     *    - 数据类型：字符串
     *    - 用途：指定要运行的测试套件
     *    - 示例：php artisan module:test game --testsuite=Feature
     * 
     * 3. {--group=} - 指定测试组
     *    - 完整形式：--group
     *    - 数据类型：字符串
     *    - 用途：指定要运行的测试组
     *    - 示例：php artisan module:test game --group=user
     * 
     * 4. {--exclude-group=} - 排除测试组
     *    - 完整形式：--exclude-group
     *    - 数据类型：字符串
     *    - 用途：指定要排除的测试组
     *    - 示例：php artisan module:test game --exclude-group=slow
     * 
     * 5. {--coverage} - 生成代码覆盖率报告
     *    - 完整形式：--coverage
     *    - 用途：生成代码覆盖率报告
     *    - 示例：php artisan module:test game --coverage
     * 
     * 6. {--coverage-html=} - 生成 HTML 格式的代码覆盖率报告
     *    - 完整形式：--coverage-html
     *    - 数据类型：字符串
     *    - 用途：指定 HTML 格式的代码覆盖率报告生成路径
     *    - 示例：php artisan module:test game --coverage-html=coverage
     * 
     * 7. {--coverage-clover=} - 生成 Clover 格式的代码覆盖率报告
     *    - 完整形式：--coverage-clover
     *    - 数据类型：字符串
     *    - 用途：指定 Clover 格式的代码覆盖率报告生成路径
     *    - 示例：php artisan module:test game --coverage-clover=coverage/clover.xml
     * 
     * 8. {--verbose} - 显示详细输出
     *    - 完整形式：--verbose
     *    - 用途：显示详细的测试输出
     *    - 示例：php artisan module:test game --verbose
     * 
     * 9. {--debug} - 显示调试信息
     *    - 完整形式：--debug
     *    - 用途：显示调试信息
     *    - 示例：php artisan module:test game --debug
     */
    protected $signature = 'module:test {module : 模块名称} {--filter= : 过滤测试方法} {--testsuite= : 指定测试套件} {--group= : 指定测试组} {--exclude-group= : 排除测试组} {--coverage : 生成代码覆盖率报告} {--coverage-html= : 生成 HTML 格式的代码覆盖率报告} {--coverage-clover= : 生成 Clover 格式的代码覆盖率报告} {--verbose : 显示详细输出} {--debug : 显示调试信息}';

    /**
     * 命令描述
     *
     * @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');
        $filter = $this->option('filter');
        $testsuite = $this->option('testsuite');
        $group = $this->option('group');
        $excludeGroup = $this->option('exclude-group');
        $coverage = $this->option('coverage');
        $coverageHtml = $this->option('coverage-html');
        $coverageClover = $this->option('coverage-clover');
        $verbose = $this->option('verbose');
        $debug = $this->option('debug');

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

        $modulePath = $module->getPath();
        $testsPath = "{$modulePath}/tests";

        // 检查测试目录是否存在
        if (!($this->files->exists($testsPath))) {
            $this->error("模块 [{$moduleName}] 没有测试目录");
            return 1;
        }

        // 构建测试命令
        $command = $this->buildTestCommand($modulePath, $filter, $testsuite, $group, $excludeGroup, $coverage, $coverageHtml, $coverageClover, $verbose, $debug);

        // 执行测试命令
        $this->info("运行模块 [{$moduleName}] 的测试用例...");
        $this->line('');

        $exitCode = $this->executeCommand($command, $modulePath);

        // 显示测试结果
        $this->line('');
        if ($exitCode === 0) {
            $this->info("模块 [{$moduleName}] 的测试通过！");
        } else {
            $this->error("模块 [{$moduleName}] 的测试失败！");
        }

        return $exitCode;
    }

    /**
     * 构建测试命令
     *
     * @param  string  $modulePath  模块路径
     * @param  string|null  $filter  过滤测试方法
     * @param  string|null  $testsuite  指定测试套件
     * @param  string|null  $group  指定测试组
     * @param  string|null  $excludeGroup  排除测试组
     * @param  bool  $coverage  生成代码覆盖率报告
     * @param  string|null  $coverageHtml  生成 HTML 格式的代码覆盖率报告
     * @param  string|null  $coverageClover  生成 Clover 格式的代码覆盖率报告
     * @param  bool  $verbose  显示详细输出
     * @param  bool  $debug  显示调试信息
     * @return string  测试命令
     */
    protected function buildTestCommand(string $modulePath, ?string $filter, ?string $testsuite, ?string $group, ?string $excludeGroup, bool $coverage, ?string $coverageHtml, ?string $coverageClover, bool $verbose, bool $debug): string
    {
        $command = 'vendor/bin/phpunit';

        // 添加测试目录
        $command .= " tests";

        // 添加过滤参数
        if ($filter) {
            $command .= " --filter={$filter}";
        }

        // 添加测试套件
        if ($testsuite) {
            $command .= " --testsuite={$testsuite}";
        }

        // 添加测试组
        if ($group) {
            $command .= " --group={$group}";
        }

        // 添加排除测试组
        if ($excludeGroup) {
            $command .= " --exclude-group={$excludeGroup}";
        }

        // 添加代码覆盖率
        if ($coverage) {
            $command .= " --coverage";
        }

        // 添加 HTML 格式的代码覆盖率报告
        if ($coverageHtml) {
            $command .= " --coverage-html={$coverageHtml}";
        }

        // 添加 Clover 格式的代码覆盖率报告
        if ($coverageClover) {
            $command .= " --coverage-clover={$coverageClover}";
        }

        // 添加详细输出
        if ($verbose) {
            $command .= " --verbose";
        }

        // 添加调试信息
        if ($debug) {
            $command .= " --debug";
        }

        return $command;
    }

    /**
     * 执行命令
     *
     * @param  string  $command  命令
     * @param  string  $workingDirectory  工作目录
     * @return int  退出码
     */
    protected function executeCommand(string $command, string $workingDirectory): int
    {
        $process = proc_open(
            $command,
            [
                0 => STDIN,
                1 => STDOUT,
                2 => STDERR,
            ],
            $pipes,
            $workingDirectory
        );

        if (!is_resource($process)) {
            $this->error('无法启动测试进程');
            return 1;
        }

        $exitCode = proc_close($process);

        return $exitCode;
    }
}
