<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Shared\Utilities;

/**
 * Enhanced File Scanner Utility for MCP Tools
 *
 * Provides intelligent file system scanning with pattern matching,
 * security analysis, and performance optimization for MCP tools.
 */
final readonly class EnhancedFileScanner
{
    public function __construct(
        private string $projectRoot = '/var/www/html'
    ) {
    }

    /**
     * Scan directory with advanced filtering and analysis
     */
    public function scanDirectory(
        string $path,
        array $options = []
    ): FileScanResult {
        $fullPath = $this->resolvePath($path);
        $this->validatePath($fullPath);

        $defaultOptions = [
            'include_hidden' => false,
            'include_analysis' => true,
            'include_security' => true,
            'max_depth' => 5,
            'patterns' => [],
            'exclude_patterns' => ['.git', 'node_modules', 'vendor', '.cache'],
            'file_types' => [],
            'size_limit' => 50 * 1024 * 1024, // 50MB
        ];

        $options = array_merge($defaultOptions, $options);

        return $this->performScan($fullPath, $options);
    }

    /**
     * Find files by pattern with intelligent matching
     */
    public function findFiles(
        string $pattern,
        string $basePath = '',
        array $options = []
    ): array {
        $fullPath = $this->resolvePath($basePath ?: '.');
        $this->validatePath($fullPath);

        $defaultOptions = [
            'case_sensitive' => false,
            'recursive' => true,
            'max_results' => 1000,
            'include_content' => false,
            'file_types' => [],
        ];

        $options = array_merge($defaultOptions, $options);

        return $this->searchByPattern($pattern, $fullPath, $options);
    }

    /**
     * Analyze file content for patterns and metrics
     */
    public function analyzeFile(string $filePath): FileAnalysisResult
    {
        $fullPath = $this->resolvePath($filePath);
        $this->validatePath($fullPath);

        if (! is_file($fullPath)) {
            throw new \InvalidArgumentException("Path is not a file: {$filePath}");
        }

        if (! is_readable($fullPath)) {
            throw new \InvalidArgumentException("File is not readable: {$filePath}");
        }

        return new FileAnalysisResult(
            path: $fullPath,
            size: filesize($fullPath),
            modified: filemtime($fullPath),
            type: $this->detectFileType($fullPath),
            encoding: $this->detectEncoding($fullPath),
            lineCount: $this->countLines($fullPath),
            complexity: $this->calculateComplexity($fullPath),
            patterns: $this->detectPatterns($fullPath),
            security: $this->analyzeSecurityRisks($fullPath)
        );
    }

    /**
     * Get directory statistics and metrics
     */
    public function getDirectoryStats(string $path): DirectoryStats
    {
        $fullPath = $this->resolvePath($path);
        $this->validatePath($fullPath);

        $stats = [
            'total_files' => 0,
            'total_size' => 0,
            'file_types' => [],
            'largest_files' => [],
            'newest_files' => [],
            'security_issues' => 0,
            'complexity_average' => 0.0,
        ];

        $this->collectDirectoryStats($fullPath, $stats);

        return new DirectoryStats($stats);
    }

    private function performScan(string $path, array $options): FileScanResult
    {
        $files = [];
        $directories = [];
        $issues = [];
        $totalSize = 0;

        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST
        );

        $iterator->setMaxDepth($options['max_depth']);

        foreach ($iterator as $item) {
            if ($this->shouldSkip($item->getPathname(), $options)) {
                continue;
            }

            if ($item->isDir()) {
                $directories[] = $this->createDirectoryInfo($item, $options);
            } else {
                $fileInfo = $this->createFileInfo($item, $options);
                $files[] = $fileInfo;
                $totalSize += $fileInfo['size'];

                if ($fileInfo['size'] > $options['size_limit']) {
                    $issues[] = "Large file detected: {$item->getPathname()} ({$fileInfo['size']} bytes)";
                }
            }
        }

        return new FileScanResult([
            'path' => $path,
            'files' => $files,
            'directories' => $directories,
            'total_files' => count($files),
            'total_directories' => count($directories),
            'total_size' => $totalSize,
            'issues' => $issues,
            'scan_time' => microtime(true),
        ]);
    }

    private function searchByPattern(string $pattern, string $basePath, array $options): array
    {
        $results = [];
        $count = 0;

        $flags = $options['case_sensitive'] ? 0 : \RecursiveDirectoryIterator::SKIP_DOTS;

        $iterator = $options['recursive']
            ? new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($basePath, $flags)
            )
            : new \DirectoryIterator($basePath);

        foreach ($iterator as $item) {
            if ($count >= $options['max_results']) {
                break;
            }

            if ($item->isFile() && $this->matchesPattern($item->getFilename(), $pattern, $options)) {
                $result = [
                    'path' => $item->getPathname(),
                    'name' => $item->getFilename(),
                    'size' => $item->getSize(),
                    'modified' => $item->getMTime(),
                ];

                if ($options['include_content']) {
                    $result['content'] = $this->getFileContent($item->getPathname());
                }

                $results[] = $result;
                $count++;
            }
        }

        return $results;
    }

    private function createFileInfo(\SplFileInfo $file, array $options): array
    {
        $info = [
            'name' => $file->getFilename(),
            'path' => $file->getPathname(),
            'size' => $file->getSize(),
            'modified' => $file->getMTime(),
            'permissions' => substr(sprintf('%o', $file->getPerms()), -4),
            'type' => $this->detectFileType($file->getPathname()),
        ];

        if ($options['include_analysis']) {
            $info['analysis'] = $this->getBasicAnalysis($file->getPathname());
        }

        if ($options['include_security']) {
            $info['security'] = $this->getSecurityAnalysis($file->getPathname());
        }

        return $info;
    }

    private function createDirectoryInfo(\SplFileInfo $dir, array $options): array
    {
        return [
            'name' => $dir->getFilename(),
            'path' => $dir->getPathname(),
            'permissions' => substr(sprintf('%o', $dir->getPerms()), -4),
            'modified' => $dir->getMTime(),
        ];
    }

    private function shouldSkip(string $path, array $options): bool
    {
        $basename = basename($path);

        // Check exclude patterns
        foreach ($options['exclude_patterns'] as $pattern) {
            if (fnmatch($pattern, $basename)) {
                return true;
            }
        }

        // Check hidden files
        if (! $options['include_hidden'] && str_starts_with($basename, '.')) {
            return true;
        }

        return false;
    }

    private function matchesPattern(string $filename, string $pattern, array $options): bool
    {
        $flags = $options['case_sensitive'] ? 0 : FNM_CASEFOLD;

        return fnmatch($pattern, $filename, $flags);
    }

    private function detectFileType(string $path): string
    {
        $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));

        return match ($extension) {
            'php' => 'php',
            'js', 'mjs' => 'javascript',
            'ts' => 'typescript',
            'css', 'scss', 'sass' => 'stylesheet',
            'html', 'htm' => 'html',
            'json' => 'json',
            'xml' => 'xml',
            'yaml', 'yml' => 'yaml',
            'md', 'markdown' => 'markdown',
            'txt' => 'text',
            'sql' => 'sql',
            'env' => 'environment',
            'log' => 'log',
            default => 'unknown'
        };
    }

    private function detectEncoding(string $path): string
    {
        if (! is_readable($path)) {
            return 'unknown';
        }

        $content = file_get_contents($path, false, null, 0, 1024);
        if ($content === false) {
            return 'unknown';
        }

        $encoding = mb_detect_encoding($content, ['UTF-8', 'ISO-8859-1', 'ASCII'], true);

        return $encoding ?: 'unknown';
    }

    private function countLines(string $path): int
    {
        if (! is_readable($path)) {
            return 0;
        }

        $lineCount = 0;
        $handle = fopen($path, 'r');

        if ($handle) {
            while (fgets($handle) !== false) {
                $lineCount++;
            }
            fclose($handle);
        }

        return $lineCount;
    }

    private function calculateComplexity(string $path): float
    {
        if ($this->detectFileType($path) !== 'php') {
            return 0.0;
        }

        $content = $this->getFileContent($path);
        if (! $content) {
            return 0.0;
        }

        $complexity = 1.0; // Base complexity

        // Count control structures
        $patterns = [
            '/\bif\s*\(/' => 1,
            '/\belse\s*\{/' => 1,
            '/\belseif\s*\(/' => 1,
            '/\bwhile\s*\(/' => 1,
            '/\bfor\s*\(/' => 1,
            '/\bforeach\s*\(/' => 1,
            '/\bswitch\s*\(/' => 1,
            '/\bcase\s+/' => 1,
            '/\bcatch\s*\(/' => 1,
            '/\?\s*:/' => 1, // Ternary operator
        ];

        foreach ($patterns as $pattern => $weight) {
            $matches = preg_match_all($pattern, $content);
            $complexity += $matches * $weight;
        }

        return round($complexity, 2);
    }

    private function detectPatterns(string $path): array
    {
        $content = $this->getFileContent($path);
        if (! $content) {
            return [];
        }

        $patterns = [];

        // Framework patterns
        if (preg_match('/#\[Route\(/', $content)) {
            $patterns[] = 'framework_routing';
        }

        if (preg_match('/#\[McpTool\(/', $content)) {
            $patterns[] = 'mcp_tool';
        }

        if (preg_match('/final readonly class/', $content)) {
            $patterns[] = 'readonly_class';
        }

        if (preg_match('/\bpublic function __construct\(/', $content)) {
            $patterns[] = 'constructor_injection';
        }

        // Architecture patterns
        if (preg_match('/implements.*Interface/', $content)) {
            $patterns[] = 'interface_implementation';
        }

        if (preg_match('/extends.*Exception/', $content)) {
            $patterns[] = 'custom_exception';
        }

        return $patterns;
    }

    private function analyzeSecurityRisks(string $path): array
    {
        $content = $this->getFileContent($path);
        if (! $content) {
            return [];
        }

        $risks = [];

        // Check for potential security issues
        $securityPatterns = [
            '/\$_GET\[/' => 'direct_superglobal_access',
            '/\$_POST\[/' => 'direct_superglobal_access',
            '/\$_REQUEST\[/' => 'direct_superglobal_access',
            '/eval\s*\(/' => 'eval_usage',
            '/exec\s*\(/' => 'exec_usage',
            '/shell_exec\s*\(/' => 'shell_exec_usage',
            '/system\s*\(/' => 'system_usage',
            '/passthru\s*\(/' => 'passthru_usage',
            '/file_get_contents\s*\(\s*\$/' => 'dynamic_file_access',
            '/include\s*\$/' => 'dynamic_include',
            '/require\s*\$/' => 'dynamic_require',
        ];

        foreach ($securityPatterns as $pattern => $risk) {
            if (preg_match($pattern, $content)) {
                $risks[] = $risk;
            }
        }

        return array_unique($risks);
    }

    private function getBasicAnalysis(string $path): array
    {
        return [
            'line_count' => $this->countLines($path),
            'complexity' => $this->calculateComplexity($path),
            'patterns' => $this->detectPatterns($path),
        ];
    }

    private function getSecurityAnalysis(string $path): array
    {
        return [
            'risks' => $this->analyzeSecurityRisks($path),
            'permissions' => substr(sprintf('%o', fileperms($path)), -4),
        ];
    }

    private function getFileContent(string $path): string|false
    {
        if (! is_readable($path) || filesize($path) > 1024 * 1024) { // 1MB limit
            return false;
        }

        return file_get_contents($path);
    }

    private function collectDirectoryStats(string $path, array &$stats): void
    {
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS)
        );

        foreach ($iterator as $file) {
            if ($file->isFile()) {
                $stats['total_files']++;
                $stats['total_size'] += $file->getSize();

                $type = $this->detectFileType($file->getPathname());
                $stats['file_types'][$type] = ($stats['file_types'][$type] ?? 0) + 1;

                // Track largest files
                $stats['largest_files'][] = [
                    'path' => $file->getPathname(),
                    'size' => $file->getSize(),
                ];

                // Track newest files
                $stats['newest_files'][] = [
                    'path' => $file->getPathname(),
                    'modified' => $file->getMTime(),
                ];
            }
        }

        // Sort and limit arrays
        usort($stats['largest_files'], fn ($a, $b) => $b['size'] <=> $a['size']);
        $stats['largest_files'] = array_slice($stats['largest_files'], 0, 10);

        usort($stats['newest_files'], fn ($a, $b) => $b['modified'] <=> $a['modified']);
        $stats['newest_files'] = array_slice($stats['newest_files'], 0, 10);
    }

    private function resolvePath(string $path): string
    {
        // Convert relative paths to absolute paths within project
        if (! str_starts_with($path, '/')) {
            $path = $this->projectRoot . '/' . ltrim($path, './');
        }

        return realpath($path) ?: $path;
    }

    private function validatePath(string $path): void
    {
        // Security: Ensure path is within project root
        if (! str_starts_with($path, $this->projectRoot)) {
            throw new \InvalidArgumentException("Path outside project root: {$path}");
        }

        if (! file_exists($path)) {
            throw new \InvalidArgumentException("Path does not exist: {$path}");
        }
    }
}

/**
 * Result object for file scan operations
 */
final readonly class FileScanResult
{
    public function __construct(
        public array $data
    ) {
    }

    public function toArray(): array
    {
        return $this->data;
    }

    public function getFiles(): array
    {
        return $this->data['files'] ?? [];
    }

    public function getDirectories(): array
    {
        return $this->data['directories'] ?? [];
    }

    public function getTotalSize(): int
    {
        return $this->data['total_size'] ?? 0;
    }

    public function getIssues(): array
    {
        return $this->data['issues'] ?? [];
    }
}

/**
 * Result object for file analysis
 */
final readonly class FileAnalysisResult
{
    public function __construct(
        public string $path,
        public int $size,
        public int $modified,
        public string $type,
        public string $encoding,
        public int $lineCount,
        public float $complexity,
        public array $patterns,
        public array $security
    ) {
    }

    public function toArray(): array
    {
        return [
            'path' => $this->path,
            'size' => $this->size,
            'modified' => $this->modified,
            'type' => $this->type,
            'encoding' => $this->encoding,
            'line_count' => $this->lineCount,
            'complexity' => $this->complexity,
            'patterns' => $this->patterns,
            'security' => $this->security,
        ];
    }
}

/**
 * Directory statistics container
 */
final readonly class DirectoryStats
{
    public function __construct(
        public array $stats
    ) {
    }

    public function toArray(): array
    {
        return $this->stats;
    }

    public function getTotalFiles(): int
    {
        return $this->stats['total_files'] ?? 0;
    }

    public function getTotalSize(): int
    {
        return $this->stats['total_size'] ?? 0;
    }

    public function getFileTypes(): array
    {
        return $this->stats['file_types'] ?? [];
    }
}
