<?php

namespace Kylin\Modules\Console\Commands;

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

/**
 * 模块代码质量检查命令
 *
 * 检查模块代码的质量，包括代码风格、复杂度等，帮助开发者确保模块代码的可维护性
 */
class ModuleQualityCommand extends Command
{
    /**
     * 命令名称和签名
     *
     * @var string
     * 
     * 命令参数说明：
     * 1. {module} - 模块名称（必填）
     *    - 数据类型：字符串
     *    - 用途：指定要检查代码质量的模块名称
     *    - 示例：php artisan module:quality game
     * 
     * 命令选项说明：
     * 1. {--phpcs} - 运行 PHP Code Sniffer 检查代码风格
     *    - 完整形式：--phpcs
     *    - 用途：运行 PHP Code Sniffer 检查代码风格
     *    - 示例：php artisan module:quality game --phpcs
     * 
     * 2. {--phpmd} - 运行 PHP Mess Detector 检查代码复杂度和潜在问题
     *    - 完整形式：--phpmd
     *    - 用途：运行 PHP Mess Detector 检查代码复杂度和潜在问题
     *    - 示例：php artisan module:quality game --phpmd
     * 
     * 3. {--phpstan} - 运行 PHPStan 进行静态类型分析
     *    - 完整形式：--phpstan
     *    - 用途：运行 PHPStan 进行静态类型分析
     *    - 示例：php artisan module:quality game --phpstan
     * 
     * 4. {--all} - 运行所有质量检查
     *    - 完整形式：--all
     *    - 用途：运行所有质量检查
     *    - 示例：php artisan module:quality game --all
     * 
     * 5. {--fix} - 尝试自动修复代码风格问题
     *    - 完整形式：--fix
     *    - 用途：尝试自动修复代码风格问题
     *    - 示例：php artisan module:quality game --fix
     * 
     * 6. {--report=} - 生成质量报告文件路径
     *    - 完整形式：--report
     *    - 数据类型：字符串
     *    - 用途：指定生成质量报告的文件路径
     *    - 示例：php artisan module:quality game --report=quality-report.txt
     */
    protected $signature = 'module:quality {module : 模块名称} {--phpcs : 运行 PHP Code Sniffer 检查代码风格} {--phpmd : 运行 PHP Mess Detector 检查代码复杂度和潜在问题} {--phpstan : 运行 PHPStan 进行静态类型分析} {--all : 运行所有质量检查} {--fix : 尝试自动修复代码风格问题} {--report= : 生成质量报告文件路径}';

    /**
     * 命令描述
     *
     * @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');
        $phpcs = $this->option('phpcs');
        $phpmd = $this->option('phpmd');
        $phpstan = $this->option('phpstan');
        $all = $this->option('all');
        $fix = $this->option('fix');
        $report = $this->option('report');

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

        $modulePath = $module->getPath();

        // 确定要运行的检查
        $runPhpcs = $phpcs || $all;
        $runPhpmd = $phpmd || $all;
        $runPhpstan = $phpstan || $all;

        // 如果没有指定检查，默认运行所有检查
        if (!$runPhpcs && !$runPhpmd && !$runPhpstan) {
            $runPhpcs = true;
            $runPhpmd = true;
            $runPhpstan = true;
        }

        $this->info("检查模块 [{$moduleName}] 的代码质量...");
        $this->line('');

        $issues = [];
        $totalIssues = 0;

        // 运行 PHP Code Sniffer
        if ($runPhpcs) {
            $this->info('运行 PHP Code Sniffer 检查代码风格...');
            $phpcsIssues = $this->runPhpCs($modulePath, $fix);
            $issues['phpcs'] = $phpcsIssues;
            $totalIssues += count($phpcsIssues);
            $this->line('');
        }

        // 运行 PHP Mess Detector
        if ($runPhpmd) {
            $this->info('运行 PHP Mess Detector 检查代码复杂度和潜在问题...');
            $phpmdIssues = $this->runPhpMd($modulePath);
            $issues['phpmd'] = $phpmdIssues;
            $totalIssues += count($phpmdIssues);
            $this->line('');
        }

        // 运行 PHPStan
        if ($runPhpstan) {
            $this->info('运行 PHPStan 进行静态类型分析...');
            $phpstanIssues = $this->runPhpStan($modulePath);
            $issues['phpstan'] = $phpstanIssues;
            $totalIssues += count($phpstanIssues);
            $this->line('');
        }

        // 显示总体检查结果
        $this->info('代码质量检查完成:');
        $this->line("  检查的模块: {$moduleName}");
        $this->line("  发现问题数: {$totalIssues}");

        if ($totalIssues > 0) {
            $this->error('请检查并修复发现的代码质量问题');
        } else {
            $this->info('模块代码质量检查通过！');
        }

        // 生成质量报告
        if ($report) {
            $this->generateQualityReport($report, $issues, $moduleName);
            $this->info("质量报告已生成到: {$report}");
        }

        return $totalIssues > 0 ? 1 : 0;
    }

    /**
     * 运行 PHP Code Sniffer
     *
     * @param  string  $modulePath  模块路径
     * @param  bool  $fix  是否尝试修复
     * @return array  问题列表
     */
    protected function runPhpCs(string $modulePath, bool $fix): array
    {
        $issues = [];

        // 检查 phpcs 是否安装
        if (!$this->commandExists('phpcs')) {
            $this->warn('PHP Code Sniffer 未安装，跳过代码风格检查');
            return $issues;
        }

        // 构建命令
        $command = 'phpcs';
        if ($fix) {
            $command = 'phpcbf';
        }
        $command .= " --standard=PSR12 {$modulePath}/src";

        // 执行命令
        $output = [];
        $exitCode = 0;
        exec($command, $output, $exitCode);

        // 处理输出
        if ($exitCode !== 0 && !$fix) {
            foreach ($output as $line) {
                if (preg_match('/^(.*?):(\d+):(\d+):\s+(.*)$/', $line, $matches)) {
                    $issues[] = [
                        'file' => $matches[1],
                        'line' => $matches[2],
                        'column' => $matches[3],
                        'message' => $matches[4],
                    ];
                    $this->line("  {$matches[1]}:{$matches[2]}:{$matches[3]} - {$matches[4]}");
                }
            }
        } elseif ($fix) {
            $this->info('  尝试自动修复代码风格问题...');
            $this->line('  ' . implode("\n  ", $output));
        }

        return $issues;
    }

    /**
     * 运行 PHP Mess Detector
     *
     * @param  string  $modulePath  模块路径
     * @return array  问题列表
     */
    protected function runPhpMd(string $modulePath): array
    {
        $issues = [];

        // 检查 phpmd 是否安装
        if (!$this->commandExists('phpmd')) {
            $this->warn('PHP Mess Detector 未安装，跳过代码复杂度检查');
            return $issues;
        }

        // 构建命令
        $command = "phpmd {$modulePath}/src text cleancode,codesize,controversial,design,naming,unusedcode";

        // 执行命令
        $output = [];
        $exitCode = 0;
        exec($command, $output, $exitCode);

        // 处理输出
        if ($exitCode !== 0) {
            foreach ($output as $line) {
                if (preg_match('/^(.*?)\s+:\s+(\d+)\s+-\s+(.*)$/', $line, $matches)) {
                    $issues[] = [
                        'file' => $matches[1],
                        'line' => $matches[2],
                        'message' => $matches[3],
                    ];
                    $this->line("  {$matches[1]}:{$matches[2]} - {$matches[3]}");
                }
            }
        }

        return $issues;
    }

    /**
     * 运行 PHPStan
     *
     * @param  string  $modulePath  模块路径
     * @return array  问题列表
     */
    protected function runPhpStan(string $modulePath): array
    {
        $issues = [];

        // 检查 phpstan 是否安装
        if (!$this->commandExists('phpstan')) {
            $this->warn('PHPStan 未安装，跳过静态类型分析');
            return $issues;
        }

        // 构建命令
        $command = "phpstan analyze {$modulePath}/src";

        // 执行命令
        $output = [];
        $exitCode = 0;
        exec($command, $output, $exitCode);

        // 处理输出
        if ($exitCode !== 0) {
            foreach ($output as $line) {
                if (preg_match('/^(.*?):(\d+):(\d+):\s+(.*)$/', $line, $matches)) {
                    $issues[] = [
                        'file' => $matches[1],
                        'line' => $matches[2],
                        'column' => $matches[3],
                        'message' => $matches[4],
                    ];
                    $this->line("  {$matches[1]}:{$matches[2]}:{$matches[3]} - {$matches[4]}");
                }
            }
        }

        return $issues;
    }

    /**
     * 检查命令是否存在
     *
     * @param  string  $command  命令名称
     * @return bool  是否存在
     */
    protected function commandExists(string $command): bool
    {
        $output = [];
        $exitCode = 0;
        exec("{$command} --version", $output, $exitCode);
        return $exitCode === 0;
    }

    /**
     * 生成质量报告
     *
     * @param  string  $reportPath  报告文件路径
     * @param  array  $issues  问题列表
     * @param  string  $moduleName  模块名称
     */
    protected function generateQualityReport(string $reportPath, array $issues, string $moduleName): void
    {
        $report = "# 模块代码质量检查报告\n\n";
        $report .= "生成时间: " . date('Y-m-d H:i:s') . "\n";
        $report .= "模块名称: {$moduleName}\n\n";

        $totalIssues = 0;
        foreach ($issues as $tool => $toolIssues) {
            $totalIssues += count($toolIssues);
        }

        $report .= "## 检查结果\n\n";
        $report .= "发现问题数: {$totalIssues}\n\n";

        // 按工具分组显示问题
        foreach ($issues as $tool => $toolIssues) {
            if (!empty($toolIssues)) {
                $report .= "### {$this->getToolName($tool)}\n\n";
                $report .= "发现问题数: " . count($toolIssues) . "\n\n";

                foreach ($toolIssues as $issue) {
                    $report .= "- **文件**: {$issue['file']}\n";
                    $report .= "- **行号**: {$issue['line']}\n";
                    if (isset($issue['column'])) {
                        $report .= "- **列号**: {$issue['column']}\n";
                    }
                    $report .= "- **描述**: {$issue['message']}\n\n";
                }
            }
        }

        $this->files->put($reportPath, $report);
    }

    /**
     * 获取工具名称
     *
     * @param  string  $tool  工具标识
     * @return string  工具名称
     */
    protected function getToolName(string $tool): string
    {
        $names = [
            'phpcs' => 'PHP Code Sniffer (代码风格)',
            'phpmd' => 'PHP Mess Detector (代码复杂度)',
            'phpstan' => 'PHPStan (静态类型分析)',
        ];

        return $names[$tool] ?? $tool;
    }
}
