<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Shared\Utilities;

/**
 * Advanced Code Analysis Engine for MCP Tools
 *
 * Provides comprehensive code analysis capabilities including:
 * - Static analysis and complexity metrics
 * - Framework pattern detection and validation
 * - Code quality assessment and recommendations
 * - Dependency analysis and architectural insights
 */
final readonly class CodeAnalysisEngine
{
    public function __construct(
        private EnhancedFileScanner $fileScanner
    ) {
    }

    /**
     * Perform comprehensive code analysis on a file or directory
     */
    public function analyzeCode(
        string $path,
        array $options = []
    ): CodeAnalysisResult {
        $defaultOptions = [
            'include_metrics' => true,
            'include_patterns' => true,
            'include_dependencies' => true,
            'include_suggestions' => true,
            'complexity_threshold' => 10.0,
            'maintainability_threshold' => 70.0,
        ];

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

        if (is_file($path)) {
            return $this->analyzeFile($path, $options);
        } else {
            return $this->analyzeDirectory($path, $options);
        }
    }

    /**
     * Extract and analyze class structures and relationships
     */
    public function analyzeClassStructure(string $filePath): ClassStructureResult
    {
        $content = $this->getFileContent($filePath);
        if (! $content) {
            return new ClassStructureResult([]);
        }

        $classes = $this->extractClasses($content);
        $interfaces = $this->extractInterfaces($content);
        $traits = $this->extractTraits($content);
        $enums = $this->extractEnums($content);

        return new ClassStructureResult([
            'file' => $filePath,
            'classes' => $classes,
            'interfaces' => $interfaces,
            'traits' => $traits,
            'enums' => $enums,
            'dependencies' => $this->extractDependencies($content),
            'namespace' => $this->extractNamespace($content),
            'imports' => $this->extractImports($content),
        ]);
    }

    /**
     * Analyze code quality and maintainability
     */
    public function analyzeQuality(string $filePath): QualityAnalysisResult
    {
        $content = $this->getFileContent($filePath);
        if (! $content) {
            return new QualityAnalysisResult([]);
        }

        $metrics = $this->calculateQualityMetrics($content, $filePath);
        $issues = $this->detectQualityIssues($content, $filePath);
        $suggestions = $this->generateQualitysuggestions($metrics, $issues);

        return new QualityAnalysisResult([
            'file' => $filePath,
            'metrics' => $metrics,
            'issues' => $issues,
            'suggestions' => $suggestions,
            'score' => $this->calculateQualityScore($metrics, $issues),
        ]);
    }

    /**
     * Detect and validate framework patterns
     */
    public function analyzeFrameworkPatterns(string $filePath): FrameworkPatternResult
    {
        $content = $this->getFileContent($filePath);
        if (! $content) {
            return new FrameworkPatternResult([]);
        }

        $patterns = [
            'readonly_classes' => $this->detectReadonlyClasses($content),
            'final_classes' => $this->detectFinalClasses($content),
            'composition_patterns' => $this->detectCompositionPatterns($content),
            'dependency_injection' => $this->detectDependencyInjection($content),
            'value_objects' => $this->detectValueObjects($content),
            'route_attributes' => $this->detectRouteAttributes($content),
            'mcp_tools' => $this->detectMcpTools($content),
            'event_patterns' => $this->detectEventPatterns($content),
        ];

        $compliance = $this->calculateFrameworkCompliance($patterns);
        $violations = $this->detectFrameworkViolations($content);

        return new FrameworkPatternResult([
            'file' => $filePath,
            'patterns' => $patterns,
            'compliance_score' => $compliance,
            'violations' => $violations,
            'recommendations' => $this->generateFrameworkRecommendations($patterns, $violations),
        ]);
    }

    /**
     * Analyze dependencies and coupling
     */
    public function analyzeDependencies(string $path): DependencyAnalysisResult
    {
        $dependencies = [];
        $coupling = [];
        $cohesion = [];

        if (is_file($path)) {
            $dependencies = $this->analyzeSingleFileDependencies($path);
        } else {
            $dependencies = $this->analyzeDirectoryDependencies($path);
            $coupling = $this->calculateCoupling($dependencies);
            $cohesion = $this->calculateCohesion($dependencies);
        }

        return new DependencyAnalysisResult([
            'path' => $path,
            'dependencies' => $dependencies,
            'coupling_metrics' => $coupling,
            'cohesion_metrics' => $cohesion,
            'architecture_insights' => $this->generateArchitectureInsights($dependencies, $coupling),
        ]);
    }

    private function analyzeFile(string $filePath, array $options): CodeAnalysisResult
    {
        $fileAnalysis = $this->fileScanner->analyzeFile($filePath);

        $result = [
            'type' => 'file',
            'path' => $filePath,
            'basic_info' => $fileAnalysis->toArray(),
        ];

        if ($options['include_metrics']) {
            $result['quality'] = $this->analyzeQuality($filePath)->toArray();
        }

        if ($options['include_patterns']) {
            $result['patterns'] = $this->analyzeFrameworkPatterns($filePath)->toArray();
            $result['structure'] = $this->analyzeClassStructure($filePath)->toArray();
        }

        if ($options['include_dependencies']) {
            $result['dependencies'] = $this->analyzeDependencies($filePath)->toArray();
        }

        if ($options['include_suggestions']) {
            $result['suggestions'] = $this->generateFileSuggestions($result, $options);
        }

        return new CodeAnalysisResult($result);
    }

    private function analyzeDirectory(string $dirPath, array $options): CodeAnalysisResult
    {
        $scanResult = $this->fileScanner->scanDirectory($dirPath, [
            'include_analysis' => true,
            'include_security' => true,
        ]);

        $files = $scanResult->getFiles();
        $phpFiles = array_filter($files, fn ($file) => $file['type'] === 'php');

        $aggregatedMetrics = $this->aggregateMetrics($phpFiles);
        $architectureAnalysis = $this->analyzeArchitecture($phpFiles);

        $result = [
            'type' => 'directory',
            'path' => $dirPath,
            'summary' => [
                'total_files' => count($files),
                'php_files' => count($phpFiles),
                'total_size' => $scanResult->getTotalSize(),
                'issues_found' => count($scanResult->getIssues()),
            ],
            'aggregated_metrics' => $aggregatedMetrics,
            'architecture_analysis' => $architectureAnalysis,
        ];

        if ($options['include_suggestions']) {
            $result['suggestions'] = $this->generateDirectorySuggestions($result, $options);
        }

        return new CodeAnalysisResult($result);
    }

    private function extractClasses(string $content): array
    {
        $classes = [];
        $pattern = '/(?:final\s+)?(?:readonly\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([^{]+))?/';

        if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $classes[] = [
                    'name' => $match[1],
                    'extends' => $match[2] ?? null,
                    'implements' => isset($match[3]) ? array_map('trim', explode(',', $match[3])) : [],
                    'is_final' => str_contains($match[0], 'final'),
                    'is_readonly' => str_contains($match[0], 'readonly'),
                ];
            }
        }

        return $classes;
    }

    private function extractInterfaces(string $content): array
    {
        $interfaces = [];
        $pattern = '/interface\s+(\w+)(?:\s+extends\s+([^{]+))?/';

        if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $interfaces[] = [
                    'name' => $match[1],
                    'extends' => isset($match[2]) ? array_map('trim', explode(',', $match[2])) : [],
                ];
            }
        }

        return $interfaces;
    }

    private function extractTraits(string $content): array
    {
        $traits = [];
        $pattern = '/trait\s+(\w+)/';

        if (preg_match_all($pattern, $content, $matches)) {
            foreach ($matches[1] as $trait) {
                $traits[] = ['name' => $trait];
            }
        }

        return $traits;
    }

    private function extractEnums(string $content): array
    {
        $enums = [];
        $pattern = '/enum\s+(\w+)(?:\s*:\s*(\w+))?/';

        if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $enums[] = [
                    'name' => $match[1],
                    'backed_type' => $match[2] ?? null,
                ];
            }
        }

        return $enums;
    }

    private function extractDependencies(string $content): array
    {
        $dependencies = [];

        // Extract use statements
        $pattern = '/use\s+([^;]+);/';
        if (preg_match_all($pattern, $content, $matches)) {
            foreach ($matches[1] as $use) {
                $dependencies[] = [
                    'type' => 'import',
                    'name' => trim($use),
                ];
            }
        }

        return $dependencies;
    }

    private function extractNamespace(string $content): ?string
    {
        if (preg_match('/namespace\s+([^;]+);/', $content, $matches)) {
            return trim($matches[1]);
        }

        return null;
    }

    private function extractImports(string $content): array
    {
        $imports = [];
        $pattern = '/use\s+([^;]+);/';

        if (preg_match_all($pattern, $content, $matches)) {
            foreach ($matches[1] as $import) {
                $imports[] = trim($import);
            }
        }

        return $imports;
    }

    private function calculateQualityMetrics(string $content, string $filePath): array
    {
        return [
            'lines_of_code' => substr_count($content, "\n") + 1,
            'complexity' => $this->calculateCyclomaticComplexity($content),
            'maintainability_index' => $this->calculateMaintainabilityIndex($content),
            'technical_debt_ratio' => $this->calculateTechnicalDebtRatio($content),
            'comment_ratio' => $this->calculateCommentRatio($content),
            'method_count' => $this->countMethods($content),
            'class_count' => $this->countClasses($content),
        ];
    }

    private function detectQualityIssues(string $content, string $filePath): array
    {
        $issues = [];

        // Long methods
        if ($this->hasLongMethods($content)) {
            $issues[] = [
                'type' => 'long_method',
                'severity' => 'medium',
                'message' => 'Methods with high line count detected',
            ];
        }

        // Deep nesting
        if ($this->hasDeepNesting($content)) {
            $issues[] = [
                'type' => 'deep_nesting',
                'severity' => 'medium',
                'message' => 'Deep nesting detected (>3 levels)',
            ];
        }

        // Missing documentation
        if ($this->hasInsufficientDocumentation($content)) {
            $issues[] = [
                'type' => 'missing_docs',
                'severity' => 'low',
                'message' => 'Insufficient documentation detected',
            ];
        }

        return $issues;
    }

    private function generateQualitysuggestions(array $metrics, array $issues): array
    {
        $suggestions = [];

        if ($metrics['complexity'] > 10) {
            $suggestions[] = [
                'type' => 'reduce_complexity',
                'priority' => 'high',
                'message' => 'Consider breaking down complex methods into smaller functions',
            ];
        }

        if ($metrics['maintainability_index'] < 70) {
            $suggestions[] = [
                'type' => 'improve_maintainability',
                'priority' => 'medium',
                'message' => 'Improve code structure and reduce complexity',
            ];
        }

        return $suggestions;
    }

    private function calculateQualityScore(array $metrics, array $issues): float
    {
        $score = 100.0;

        // Deduct for complexity
        if ($metrics['complexity'] > 10) {
            $score -= ($metrics['complexity'] - 10) * 2;
        }

        // Deduct for issues
        foreach ($issues as $issue) {
            $deduction = match ($issue['severity']) {
                'high' => 15,
                'medium' => 10,
                'low' => 5,
                default => 0,
            };
            $score -= $deduction;
        }

        return max(0.0, round($score, 1));
    }

    private function detectReadonlyClasses(string $content): array
    {
        $pattern = '/readonly\s+class\s+(\w+)/';
        preg_match_all($pattern, $content, $matches);

        return $matches[1] ?? [];
    }

    private function detectFinalClasses(string $content): array
    {
        $pattern = '/final\s+class\s+(\w+)/';
        preg_match_all($pattern, $content, $matches);

        return $matches[1] ?? [];
    }

    private function detectCompositionPatterns(string $content): array
    {
        // Look for constructor injection patterns
        $pattern = '/public function __construct\s*\([^)]*private readonly[^)]*\)/';

        return preg_match($pattern, $content) ? ['constructor_injection'] : [];
    }

    private function detectDependencyInjection(string $content): array
    {
        $patterns = [];

        if (preg_match('/private readonly\s+\w+\s+\$\w+/', $content)) {
            $patterns[] = 'constructor_injection';
        }

        return $patterns;
    }

    private function detectValueObjects(string $content): array
    {
        $valueObjects = [];

        // Look for readonly classes with simple structure
        if (preg_match('/readonly class\s+(\w+)/', $content, $matches) &&
            preg_match('/public readonly\s+/', $content)) {
            $valueObjects[] = $matches[1];
        }

        return $valueObjects;
    }

    private function detectRouteAttributes(string $content): array
    {
        $pattern = '/#\[Route\([^]]+\)\]/';
        preg_match_all($pattern, $content, $matches);

        return $matches[0] ?? [];
    }

    private function detectMcpTools(string $content): array
    {
        $pattern = '/#\[McpTool\([^]]+\)\]/';
        preg_match_all($pattern, $content, $matches);

        return $matches[0] ?? [];
    }

    private function detectEventPatterns(string $content): array
    {
        $patterns = [];

        if (preg_match('/implements.*Event/', $content)) {
            $patterns[] = 'event_implementation';
        }

        if (preg_match('/EventDispatcher/', $content)) {
            $patterns[] = 'event_dispatching';
        }

        return $patterns;
    }

    private function calculateFrameworkCompliance(array $patterns): float
    {
        $totalChecks = 8;
        $passedChecks = 0;

        foreach ($patterns as $pattern => $detected) {
            if (! empty($detected)) {
                $passedChecks++;
            }
        }

        return round(($passedChecks / $totalChecks) * 100, 1);
    }

    private function detectFrameworkViolations(string $content): array
    {
        $violations = [];

        // Check for inheritance usage
        if (preg_match('/class\s+\w+\s+extends\s+/', $content) &&
            ! preg_match('/extends\s+Exception/', $content)) {
            $violations[] = [
                'type' => 'inheritance_usage',
                'severity' => 'medium',
                'message' => 'Inheritance detected - prefer composition',
            ];
        }

        // Check for mutable classes
        if (preg_match('/class\s+\w+/', $content) &&
            ! preg_match('/readonly\s+class/', $content)) {
            $violations[] = [
                'type' => 'mutable_class',
                'severity' => 'low',
                'message' => 'Consider making class readonly',
            ];
        }

        return $violations;
    }

    private function generateFrameworkRecommendations(array $patterns, array $violations): array
    {
        $recommendations = [];

        if (empty($patterns['readonly_classes']) && empty($violations)) {
            $recommendations[] = [
                'type' => 'use_readonly',
                'priority' => 'medium',
                'message' => 'Consider using readonly classes for immutability',
            ];
        }

        if (empty($patterns['dependency_injection'])) {
            $recommendations[] = [
                'type' => 'use_di',
                'priority' => 'high',
                'message' => 'Implement constructor dependency injection',
            ];
        }

        return $recommendations;
    }

    // Additional helper methods for calculations
    private function calculateCyclomaticComplexity(string $content): float
    {
        $complexity = 1; // Base complexity

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

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

        return (float) $complexity;
    }

    private function calculateMaintainabilityIndex(string $content): float
    {
        $loc = substr_count($content, "\n") + 1;
        $complexity = $this->calculateCyclomaticComplexity($content);
        $volume = $loc * log($loc > 0 ? $loc : 1);

        // Simplified maintainability index calculation
        $index = max(0, (171 - 5.2 * log($volume) - 0.23 * $complexity - 16.2 * log($loc)) * 100 / 171);

        return round($index, 1);
    }

    private function calculateTechnicalDebtRatio(string $content): float
    {
        // Simplified technical debt calculation based on code smells
        $debt = 0;
        $total = substr_count($content, "\n") + 1;

        // Add debt for long lines
        $lines = explode("\n", $content);
        foreach ($lines as $line) {
            if (strlen($line) > 120) {
                $debt++;
            }
        }

        return $total > 0 ? round(($debt / $total) * 100, 2) : 0.0;
    }

    private function calculateCommentRatio(string $content): float
    {
        $totalLines = substr_count($content, "\n") + 1;
        $commentLines = preg_match_all('/^\s*\/\/|^\s*\/\*|\*/', $content);

        return $totalLines > 0 ? round(($commentLines / $totalLines) * 100, 1) : 0.0;
    }

    private function countMethods(string $content): int
    {
        return preg_match_all('/function\s+\w+\s*\(/', $content);
    }

    private function countClasses(string $content): int
    {
        return preg_match_all('/class\s+\w+/', $content);
    }

    private function hasLongMethods(string $content): bool
    {
        // Simple check for methods with many lines
        return preg_match('/function\s+\w+\s*\([^{]*\{[^}]{500,}/', $content) > 0;
    }

    private function hasDeepNesting(string $content): bool
    {
        // Check for deep nesting patterns
        return preg_match('/\{\s*[^}]*\{\s*[^}]*\{\s*[^}]*\{/', $content) > 0;
    }

    private function hasInsufficientDocumentation(string $content): bool
    {
        $methodCount = $this->countMethods($content);
        $docblockCount = preg_match_all('/\/\*\*.*?\*\//s', $content);

        return $methodCount > 0 && ($docblockCount / $methodCount) < 0.5;
    }

    private function aggregateMetrics(array $phpFiles): array
    {
        if (empty($phpFiles)) {
            return [];
        }

        $totalComplexity = 0;
        $totalLines = 0;
        $fileCount = count($phpFiles);

        foreach ($phpFiles as $file) {
            $totalComplexity += $file['analysis']['complexity'] ?? 0;
            $totalLines += $file['analysis']['line_count'] ?? 0;
        }

        return [
            'average_complexity' => round($totalComplexity / $fileCount, 2),
            'total_lines' => $totalLines,
            'average_lines_per_file' => round($totalLines / $fileCount, 0),
            'files_analyzed' => $fileCount,
        ];
    }

    private function analyzeArchitecture(array $phpFiles): array
    {
        $namespaces = [];
        $patterns = [];

        foreach ($phpFiles as $file) {
            $analysis = $this->analyzeFrameworkPatterns($file['path']);
            $structure = $this->analyzeClassStructure($file['path']);

            if ($structure->data['namespace']) {
                $namespaces[] = $structure->data['namespace'];
            }

            foreach ($analysis->data['patterns'] as $pattern => $detected) {
                if (! empty($detected)) {
                    $patterns[$pattern] = ($patterns[$pattern] ?? 0) + 1;
                }
            }
        }

        return [
            'unique_namespaces' => count(array_unique($namespaces)),
            'pattern_usage' => $patterns,
            'architecture_score' => $this->calculateArchitectureScore($patterns, count($phpFiles)),
        ];
    }

    private function calculateArchitectureScore(array $patterns, int $totalFiles): float
    {
        $score = 0;
        $maxScore = $totalFiles * 4; // 4 points per file max

        foreach ($patterns as $pattern => $count) {
            $score += match ($pattern) {
                'readonly_classes', 'final_classes' => $count * 2,
                'dependency_injection', 'value_objects' => $count * 1,
                default => 0,
            };
        }

        return $maxScore > 0 ? round(($score / $maxScore) * 100, 1) : 0.0;
    }

    private function analyzeSingleFileDependencies(string $filePath): array
    {
        $content = $this->getFileContent($filePath);

        return $content ? $this->extractDependencies($content) : [];
    }

    private function analyzeDirectoryDependencies(string $dirPath): array
    {
        // Simplified directory dependency analysis
        $scanResult = $this->fileScanner->scanDirectory($dirPath);
        $dependencies = [];

        foreach ($scanResult->getFiles() as $file) {
            if ($file['type'] === 'php') {
                $fileDeps = $this->analyzeSingleFileDependencies($file['path']);
                $dependencies[$file['path']] = $fileDeps;
            }
        }

        return $dependencies;
    }

    private function calculateCoupling(array $dependencies): array
    {
        // Simplified coupling calculation
        $totalDependencies = array_sum(array_map('count', $dependencies));
        $fileCount = count($dependencies);

        return [
            'average_dependencies_per_file' => $fileCount > 0 ? round($totalDependencies / $fileCount, 1) : 0,
            'total_dependencies' => $totalDependencies,
            'coupling_score' => min(100, ($totalDependencies / max($fileCount, 1)) * 10),
        ];
    }

    private function calculateCohesion(array $dependencies): array
    {
        // Simplified cohesion calculation
        return [
            'cohesion_score' => 75.0, // Placeholder
            'module_cohesion' => 'medium',
        ];
    }

    private function generateArchitectureInsights(array $dependencies, array $coupling): array
    {
        $insights = [];

        if ($coupling['coupling_score'] > 50) {
            $insights[] = [
                'type' => 'high_coupling',
                'message' => 'High coupling detected - consider reducing dependencies',
                'priority' => 'medium',
            ];
        }

        return $insights;
    }

    private function generateFileSuggestions(array $analysis, array $options): array
    {
        $suggestions = [];

        if (isset($analysis['quality']['metrics']['complexity']) &&
            $analysis['quality']['metrics']['complexity'] > $options['complexity_threshold']) {
            $suggestions[] = [
                'type' => 'reduce_complexity',
                'message' => 'Consider breaking down complex methods',
                'priority' => 'high',
            ];
        }

        return $suggestions;
    }

    private function generateDirectorySuggestions(array $analysis, array $options): array
    {
        $suggestions = [];

        if (isset($analysis['aggregated_metrics']['average_complexity']) &&
            $analysis['aggregated_metrics']['average_complexity'] > $options['complexity_threshold']) {
            $suggestions[] = [
                'type' => 'overall_complexity',
                'message' => 'Overall code complexity is high - consider refactoring',
                'priority' => 'medium',
            ];
        }

        return $suggestions;
    }

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

        return file_get_contents($filePath);
    }
}

// Result classes
final readonly class CodeAnalysisResult
{
    public function __construct(public array $data)
    {
    }

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

final readonly class ClassStructureResult
{
    public function __construct(public array $data)
    {
    }

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

final readonly class QualityAnalysisResult
{
    public function __construct(public array $data)
    {
    }

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

final readonly class FrameworkPatternResult
{
    public function __construct(public array $data)
    {
    }

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

final readonly class DependencyAnalysisResult
{
    public function __construct(public array $data)
    {
    }

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