<?php

namespace Kylin\Modules\Console\Commands;

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

/**
 * 模块搜索命令
 *
 * 搜索模块中的文件或内容
 */
class ModuleSearchCommand extends Command
{
    /**
     * 命令名称和签名
     *
     * @var string
     * 
     * 命令参数说明：
     * 1. {term} - 搜索关键词（必填）
     *    - 数据类型：字符串
     *    - 用途：指定要搜索的关键词
     *    - 示例：php artisan module:search User
     * 
     * 命令选项说明：
     * 1. {--module=} - 模块名称
     *    - 完整形式：--module
     *    - 数据类型：字符串
     *    - 用途：指定要搜索的模块名称，默认搜索所有模块
     *    - 示例：php artisan module:search User --module=game
     * 
     * 2. {--path=} - 搜索路径
     *    - 完整形式：--path
     *    - 数据类型：字符串
     *    - 用途：指定要搜索的路径
     *    - 示例：php artisan module:search User --path=app/Http/Controllers
     * 
     * 3. {--extension=*} - 文件扩展名
     *    - 完整形式：--extension
     *    - 数据类型：字符串数组
     *    - 用途：指定要搜索的文件扩展名
     *    - 示例：php artisan module:search User --extension=php,blade.php
     * 
     * 4. {--case-insensitive} - 大小写不敏感搜索
     *    - 完整形式：--case-insensitive
     *    - 用途：进行大小写不敏感的搜索
     *    - 示例：php artisan module:search user --case-insensitive
     * 
     * 5. {--count} - 只显示匹配数量
     *    - 完整形式：--count
     *    - 用途：只显示匹配的数量，不显示具体匹配内容
     *    - 示例：php artisan module:search User --count
     */
    protected $signature = 'module:search {term : 搜索关键词} {--module= : 模块名称（可选，默认搜索所有模块）} {--path= : 搜索路径（可选）} {--extension=* : 文件扩展名（可选）} {--case-insensitive : 大小写不敏感搜索} {--count : 只显示匹配数量}';

    /**
     * 命令描述
     *
     * @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
    {
        $term = (string) $this->argument('term');
        $moduleName = $this->option('module');
        $path = $this->option('path');
        $extensions = $this->option('extension') ?: [];
        $caseInsensitive = (bool) $this->option('case-insensitive');
        $countOnly = (bool) $this->option('count');

        // 确定要搜索的模块
        $modules = [];
        if ($moduleName) {
            $module = $this->moduleManager->get($moduleName);
            if ($module) {
                $modules[] = $module;
            } else {
                $this->error("模块 [{$moduleName}] 未找到。");
                return 1;
            }
        } else {
            $modules = $this->moduleManager->all();
        }

        if (empty($modules)) {
            $this->error('没有找到要搜索的模块');
            return 1;
        }

        $this->info('正在搜索模块...');
        $this->line('');

        $totalMatches = 0;

        foreach ($modules as $module) {
            $modulePath = $module->getPath();
            $searchPath = $path ? $modulePath . '/' . $path : $modulePath;

            if (!$this->files->exists($searchPath)) {
                $this->warn("模块 [{$module->getName()}] 中不存在路径: {$path}");
                continue;
            }

            $matches = $this->searchDirectory($searchPath, $term, $extensions, $caseInsensitive);
            $matchCount = count($matches);
            $totalMatches += $matchCount;

            if ($matchCount > 0) {
                $this->info("模块 [{$module->getName()}] 中找到 {$matchCount} 个匹配:");
                
                if (!$countOnly) {
                    foreach ($matches as $file => $fileMatches) {
                        $relativePath = str_replace($modulePath . '/', '', $file);
                        $this->line("  {$relativePath}:");
                        
                        foreach ($fileMatches as $lineNumber => $line) {
                            $this->line("    {$lineNumber}: {$line}");
                        }
                    }
                }
                
                $this->line('');
            }
        }

        $this->info("搜索完成，共找到 {$totalMatches} 个匹配。");

        return 0;
    }

    /**
     * 搜索目录
     *
     * @param  string  $directory  搜索目录
     * @param  string  $term  搜索关键词
     * @param  array  $extensions  文件扩展名
     * @param  bool  $caseInsensitive  是否大小写不敏感
     * @return array  匹配结果
     */
    protected function searchDirectory(string $directory, string $term, array $extensions, bool $caseInsensitive): array
    {
        $matches = [];

        // 遍历目录
        $this->files->walk($directory, function ($file) use (&$matches, $term, $extensions, $caseInsensitive) {
            // 跳过目录
            if (!$file->isFile()) {
                return;
            }

            // 检查文件扩展名
            if (!empty($extensions)) {
                $extension = $file->getExtension();
                if (!in_array($extension, $extensions)) {
                    return;
                }
            }

            // 读取文件内容
            try {
                $content = $this->files->get($file->getPathname());
            } catch (\Exception $e) {
                return;
            }

            // 搜索内容
            $fileMatches = $this->searchContent($content, $term, $caseInsensitive);
            if (!empty($fileMatches)) {
                $matches[$file->getPathname()] = $fileMatches;
            }
        });

        return $matches;
    }

    /**
     * 搜索内容
     *
     * @param  string  $content  文件内容
     * @param  string  $term  搜索关键词
     * @param  bool  $caseInsensitive  是否大小写不敏感
     * @return array  匹配结果
     */
    protected function searchContent(string $content, string $term, bool $caseInsensitive): array
    {
        $matches = [];
        $lines = explode("\n", $content);

        foreach ($lines as $lineNumber => $line) {
            $lineNumber++;

            if ($caseInsensitive) {
                if (stristr($line, $term) !== false) {
                    $matches[$lineNumber] = $line;
                }
            } else {
                if (strpos($line, $term) !== false) {
                    $matches[$lineNumber] = $line;
                }
            }
        }

        return $matches;
    }
}
