<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Tools\Agents;

use App\Framework\DI\Container;
use App\Framework\DI\Initializer;
use App\Framework\Discovery\UnifiedDiscoveryService;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Mcp\Attributes\McpTool;
use ReflectionClass;
use ReflectionException;

/**
 * MCP Agent for Framework Initializer Management and Analysis
 *
 * Provides comprehensive tooling for analyzing, monitoring, and managing
 * all Framework Initializers across the entire system.
 */
final readonly class FrameworkInitializerAgent
{
    public function __construct(
        private Container $container,
        private UnifiedDiscoveryService $discoveryService,
        private Logger $logger
    ) {
    }

    #[McpTool(
        name: 'analyze_all_initializers',
        description: 'Analyze all Framework Initializers and their dependencies'
    )]
    public function analyzeAllInitializers(): array
    {
        $this->logger->info(
            'Starting comprehensive initializer analysis',
            LogContext::withData([
                'operation' => 'analyze_all_initializers',
                'component' => 'FrameworkInitializerAgent',
            ])
        );

        $initializerFiles = $this->discoverInitializerFiles();
        $initializerClasses = $this->loadInitializerClasses($initializerFiles);
        $analysis = $this->analyzeInitializers($initializerClasses);

        return [
            'timestamp' => date('Y-m-d H:i:s'),
            'total_initializers' => count($initializerClasses),
            'total_files' => count($initializerFiles),
            'categories' => $this->categorizeInitializers($initializerClasses),
            'dependency_graph' => $this->buildDependencyGraph($initializerClasses),
            'health_status' => $this->assessInitializerHealth($initializerClasses),
            'detailed_analysis' => $analysis,
        ];
    }

    #[McpTool(
        name: 'initializer_dependencies',
        description: 'Analyze dependency relationships between initializers'
    )]
    public function analyzeInitializerDependencies(): array
    {
        $initializerFiles = $this->discoverInitializerFiles();
        $initializerClasses = $this->loadInitializerClasses($initializerFiles);

        return [
            'timestamp' => date('Y-m-d H:i:s'),
            'dependency_map' => $this->buildDetailedDependencyMap($initializerClasses),
            'circular_dependencies' => $this->detectCircularDependencies($initializerClasses),
            'initialization_order' => $this->calculateInitializationOrder($initializerClasses),
            'orphaned_initializers' => $this->findOrphanedInitializers($initializerClasses),
            'critical_path' => $this->findCriticalPath($initializerClasses),
        ];
    }

    #[McpTool(
        name: 'initializer_health_check',
        description: 'Health check for all Framework Initializers'
    )]
    public function checkInitializerHealth(): array
    {
        $initializerFiles = $this->discoverInitializerFiles();
        $initializerClasses = $this->loadInitializerClasses($initializerFiles);

        $healthChecks = [];
        $overallHealth = 'healthy';

        foreach ($initializerClasses as $class) {
            $health = $this->checkSingleInitializerHealth($class);
            $healthChecks[$class] = $health;

            if ($health['status'] === 'unhealthy') {
                $overallHealth = 'unhealthy';
            } elseif ($health['status'] === 'warning' && $overallHealth === 'healthy') {
                $overallHealth = 'warning';
            }
        }

        return [
            'timestamp' => date('Y-m-d H:i:s'),
            'overall_health' => $overallHealth,
            'total_checked' => count($initializerClasses),
            'healthy_count' => count(array_filter($healthChecks, fn ($h) => $h['status'] === 'healthy')),
            'warning_count' => count(array_filter($healthChecks, fn ($h) => $h['status'] === 'warning')),
            'unhealthy_count' => count(array_filter($healthChecks, fn ($h) => $h['status'] === 'unhealthy')),
            'detailed_results' => $healthChecks,
        ];
    }

    #[McpTool(
        name: 'initializer_performance',
        description: 'Analyze performance characteristics of initializers'
    )]
    public function analyzeInitializerPerformance(): array
    {
        $initializerFiles = $this->discoverInitializerFiles();
        $initializerClasses = $this->loadInitializerClasses($initializerFiles);

        $performanceMetrics = [];
        $totalComplexity = 0;

        foreach ($initializerClasses as $class) {
            $metrics = $this->analyzeInitializerComplexity($class);
            $performanceMetrics[$class] = $metrics;
            $totalComplexity += $metrics['complexity_score'];
        }

        return [
            'timestamp' => date('Y-m-d H:i:s'),
            'total_initializers' => count($initializerClasses),
            'average_complexity' => count($initializerClasses) > 0 ? round($totalComplexity / count($initializerClasses), 2) : 0,
            'most_complex' => $this->findMostComplex($performanceMetrics),
            'performance_bottlenecks' => $this->identifyBottlenecks($performanceMetrics),
            'optimization_opportunities' => $this->findOptimizationOpportunities($performanceMetrics),
            'detailed_metrics' => $performanceMetrics,
        ];
    }

    #[McpTool(
        name: 'initializer_by_category',
        description: 'Group and analyze initializers by framework category'
    )]
    public function analyzeInitializersByCategory(): array
    {
        $initializerFiles = $this->discoverInitializerFiles();
        $initializerClasses = $this->loadInitializerClasses($initializerFiles);
        $categories = $this->categorizeInitializers($initializerClasses);

        $categoryAnalysis = [];
        foreach ($categories as $category => $initializers) {
            $categoryAnalysis[$category] = [
                'count' => count($initializers),
                'initializers' => $initializers,
                'health_status' => $this->assessCategoryHealth($initializers),
                'complexity_average' => $this->calculateCategoryComplexity($initializers),
                'dependencies' => $this->analyzeCategoryDependencies($initializers),
            ];
        }

        return [
            'timestamp' => date('Y-m-d H:i:s'),
            'total_categories' => count($categories),
            'category_distribution' => array_map(fn ($cat) => count($cat), $categories),
            'category_analysis' => $categoryAnalysis,
            'cross_category_dependencies' => $this->findCrossCategoryDependencies($categories),
        ];
    }

    #[McpTool(
        name: 'test_initializer_bootstrap',
        description: 'Test bootstrap process and identify issues'
    )]
    public function testInitializerBootstrap(): array
    {
        $startTime = microtime(true);
        $results = [];
        $errors = [];

        try {
            // Test discovery process
            $discoveryStart = microtime(true);
            $initializerFiles = $this->discoverInitializerFiles();
            $discoveryTime = microtime(true) - $discoveryStart;

            $results['discovery'] = [
                'status' => 'success',
                'time_ms' => round($discoveryTime * 1000, 2),
                'files_found' => count($initializerFiles),
            ];

            // Test class loading
            $loadingStart = microtime(true);
            $initializerClasses = $this->loadInitializerClasses($initializerFiles);
            $loadingTime = microtime(true) - $loadingStart;

            $results['class_loading'] = [
                'status' => 'success',
                'time_ms' => round($loadingTime * 1000, 2),
                'classes_loaded' => count($initializerClasses),
            ];

            // Test dependency resolution
            $depStart = microtime(true);
            $dependencies = $this->buildDependencyGraph($initializerClasses);
            $depTime = microtime(true) - $depStart;

            $results['dependency_resolution'] = [
                'status' => 'success',
                'time_ms' => round($depTime * 1000, 2),
                'dependencies_resolved' => count($dependencies),
            ];

        } catch (\Exception $e) {
            $errors[] = [
                'stage' => 'bootstrap_test',
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ];
        }

        $totalTime = microtime(true) - $startTime;

        return [
            'timestamp' => date('Y-m-d H:i:s'),
            'total_time_ms' => round($totalTime * 1000, 2),
            'overall_status' => empty($errors) ? 'success' : 'failed',
            'test_results' => $results,
            'errors' => $errors,
            'performance_assessment' => $this->assessBootstrapPerformance($totalTime, $results),
        ];
    }

    // Private helper methods

    private function discoverInitializerFiles(): array
    {
        $pattern = '**/*Initializer.php';
        $basePath = __DIR__ . '/../../..';

        $files = [];
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($basePath)
        );

        foreach ($iterator as $file) {
            if ($file->isFile() && str_ends_with($file->getFilename(), 'Initializer.php')) {
                $files[] = $file->getPathname();
            }
        }

        return $files;
    }

    private function loadInitializerClasses(array $files): array
    {
        $classes = [];

        foreach ($files as $file) {
            try {
                $content = file_get_contents($file);
                $namespace = $this->extractNamespace($content);
                $className = $this->extractClassName($content);

                if ($namespace && $className) {
                    $fullClassName = $namespace . '\\' . $className;

                    if (class_exists($fullClassName)) {
                        $reflection = new ReflectionClass($fullClassName);

                        // Check if it has methods with Initializer attribute
                        if ($this->hasInitializerMethods($reflection)) {
                            $classes[] = $fullClassName;
                        }
                    }
                }
            } catch (\Exception $e) {
                $this->logger->warning(
                    "Failed to load initializer from file: {$file}",
                    LogContext::withData([
                        'operation' => 'load_initializer',
                        'component' => 'FrameworkInitializerAgent',
                        'file' => $file,
                        'error' => $e->getMessage(),
                    ])
                );
            }
        }

        return $classes;
    }

    private function analyzeInitializers(array $classes): array
    {
        $analysis = [];

        foreach ($classes as $class) {
            try {
                $reflection = new ReflectionClass($class);
                $analysis[$class] = [
                    'file' => $reflection->getFileName(),
                    'namespace' => $reflection->getNamespaceName(),
                    'short_name' => $reflection->getShortName(),
                    'is_final' => $reflection->isFinal(),
                    'is_readonly' => $reflection->isReadOnly(),
                    'constructor_params' => $this->analyzeConstructorParams($reflection),
                    'methods' => $this->analyzeMethods($reflection),
                    'dependencies' => $this->extractDependencies($reflection),
                    'framework_compliance' => $this->checkFrameworkCompliance($reflection),
                ];
            } catch (ReflectionException $e) {
                $analysis[$class] = [
                    'error' => $e->getMessage(),
                    'status' => 'failed_analysis',
                ];
            }
        }

        return $analysis;
    }

    private function categorizeInitializers(array $classes): array
    {
        $categories = [];

        foreach ($classes as $class) {
            $category = $this->determineCategory($class);
            if (! isset($categories[$category])) {
                $categories[$category] = [];
            }
            $categories[$category][] = $class;
        }

        return $categories;
    }

    private function determineCategory(string $class): string
    {
        $parts = explode('\\', $class);

        // Find the framework module (usually the part after 'Framework')
        $frameworkIndex = array_search('Framework', $parts);
        if ($frameworkIndex !== false && isset($parts[$frameworkIndex + 1])) {
            return $parts[$frameworkIndex + 1];
        }

        return 'Unknown';
    }

    private function buildDependencyGraph(array $classes): array
    {
        $graph = [];

        foreach ($classes as $class) {
            try {
                $reflection = new ReflectionClass($class);
                $dependencies = $this->extractDependencies($reflection);
                $graph[$class] = $dependencies;
            } catch (ReflectionException $e) {
                $graph[$class] = [];
            }
        }

        return $graph;
    }

    private function extractDependencies(ReflectionClass $reflection): array
    {
        $dependencies = [];
        $constructor = $reflection->getConstructor();

        if ($constructor) {
            foreach ($constructor->getParameters() as $param) {
                $type = $param->getType();
                if ($type && ! $type->isBuiltin()) {
                    $dependencies[] = $type->getName();
                }
            }
        }

        return $dependencies;
    }

    private function assessInitializerHealth(array $classes): string
    {
        $unhealthyCount = 0;
        $warningCount = 0;

        foreach ($classes as $class) {
            $health = $this->checkSingleInitializerHealth($class);
            if ($health['status'] === 'unhealthy') {
                $unhealthyCount++;
            } elseif ($health['status'] === 'warning') {
                $warningCount++;
            }
        }

        if ($unhealthyCount > 0) {
            return 'unhealthy';
        } elseif ($warningCount > count($classes) * 0.2) { // More than 20% warnings
            return 'warning';
        }

        return 'healthy';
    }

    private function checkSingleInitializerHealth(string $class): array
    {
        try {
            $reflection = new ReflectionClass($class);
            $issues = [];

            // Check framework compliance
            if (! $reflection->isFinal()) {
                $issues[] = 'Not final class';
            }

            if (! $reflection->isReadOnly()) {
                $issues[] = 'Not readonly class';
            }

            // Check if it has initializer methods with attribute
            if (! $this->hasInitializerMethods($reflection)) {
                $issues[] = 'No methods with #[Initializer] attribute';
            }

            // Check constructor complexity
            $constructor = $reflection->getConstructor();
            if ($constructor && count($constructor->getParameters()) > 10) {
                $issues[] = 'Too many constructor dependencies (>10)';
            }

            $status = 'healthy';
            if (count($issues) > 3) {
                $status = 'unhealthy';
            } elseif (count($issues) > 0) {
                $status = 'warning';
            }

            return [
                'status' => $status,
                'issues' => $issues,
                'issue_count' => count($issues),
            ];

        } catch (\Exception $e) {
            return [
                'status' => 'unhealthy',
                'issues' => ['Failed to analyze: ' . $e->getMessage()],
                'issue_count' => 1,
            ];
        }
    }

    private function analyzeInitializerComplexity(string $class): array
    {
        try {
            $reflection = new ReflectionClass($class);
            $complexityScore = 0;

            // Constructor complexity
            $constructor = $reflection->getConstructor();
            if ($constructor) {
                $complexityScore += count($constructor->getParameters()) * 2;
            }

            // Method complexity
            foreach ($reflection->getMethods() as $method) {
                if ($method->isPublic()) {
                    $complexityScore += count($method->getParameters()) + 1;
                }
            }

            // Dependency complexity
            $dependencies = $this->extractDependencies($reflection);
            $complexityScore += count($dependencies) * 3;

            return [
                'complexity_score' => $complexityScore,
                'constructor_complexity' => $constructor ? count($constructor->getParameters()) : 0,
                'method_count' => count($reflection->getMethods()),
                'dependency_count' => count($dependencies),
                'assessment' => $this->assessComplexity($complexityScore),
            ];

        } catch (\Exception $e) {
            return [
                'complexity_score' => 999,
                'assessment' => 'error',
                'error' => $e->getMessage(),
            ];
        }
    }

    private function assessComplexity(int $score): string
    {
        if ($score < 10) {
            return 'low';
        }
        if ($score < 25) {
            return 'medium';
        }
        if ($score < 50) {
            return 'high';
        }

        return 'very_high';
    }

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

        return null;
    }

    private function extractClassName(string $content): ?string
    {
        if (preg_match('/class\s+(\w+)/', $content, $matches)) {
            return $matches[1];
        }

        return null;
    }

    private function analyzeConstructorParams(ReflectionClass $reflection): array
    {
        $constructor = $reflection->getConstructor();
        if (! $constructor) {
            return [];
        }

        $params = [];
        foreach ($constructor->getParameters() as $param) {
            $params[] = [
                'name' => $param->getName(),
                'type' => $param->getType()?->getName(),
                'is_optional' => $param->isOptional(),
                'has_default' => $param->isDefaultValueAvailable(),
            ];
        }

        return $params;
    }

    private function analyzeMethods(ReflectionClass $reflection): array
    {
        $methods = [];
        foreach ($reflection->getMethods() as $method) {
            if ($method->getDeclaringClass()->getName() === $reflection->getName()) {
                $methods[] = [
                    'name' => $method->getName(),
                    'visibility' => $method->isPublic() ? 'public' : ($method->isProtected() ? 'protected' : 'private'),
                    'is_static' => $method->isStatic(),
                    'parameter_count' => count($method->getParameters()),
                ];
            }
        }

        return $methods;
    }

    private function checkFrameworkCompliance(ReflectionClass $reflection): array
    {
        $compliance = [];

        $compliance['is_final'] = $reflection->isFinal();
        $compliance['is_readonly'] = $reflection->isReadOnly();
        $compliance['has_initializer_methods'] = $this->hasInitializerMethods($reflection);
        $compliance['has_initialize_method'] = $reflection->hasMethod('initialize');

        $compliance['score'] = count(array_filter($compliance)) / count($compliance) * 100;

        return $compliance;
    }

    private function hasInitializerMethods(ReflectionClass $reflection): bool
    {
        foreach ($reflection->getMethods() as $method) {
            $attributes = $method->getAttributes(Initializer::class);
            if (! empty($attributes)) {
                return true;
            }
        }

        return false;
    }

    private function buildDetailedDependencyMap(array $classes): array
    {
        // Implementation for detailed dependency mapping
        return [];
    }

    private function detectCircularDependencies(array $classes): array
    {
        // Implementation for circular dependency detection
        return [];
    }

    private function calculateInitializationOrder(array $classes): array
    {
        // Implementation for initialization order calculation
        return [];
    }

    private function findOrphanedInitializers(array $classes): array
    {
        // Implementation for finding orphaned initializers
        return [];
    }

    private function findCriticalPath(array $classes): array
    {
        // Implementation for critical path analysis
        return [];
    }

    private function findMostComplex(array $metrics): array
    {
        $maxComplexity = 0;
        $mostComplex = null;

        foreach ($metrics as $class => $metric) {
            if ($metric['complexity_score'] > $maxComplexity) {
                $maxComplexity = $metric['complexity_score'];
                $mostComplex = $class;
            }
        }

        return $mostComplex ? [$mostComplex => $metrics[$mostComplex]] : [];
    }

    private function identifyBottlenecks(array $metrics): array
    {
        return array_filter($metrics, fn ($m) => $m['complexity_score'] > 50);
    }

    private function findOptimizationOpportunities(array $metrics): array
    {
        return array_filter($metrics, fn ($m) => $m['complexity_score'] > 25 && $m['assessment'] !== 'error');
    }

    private function assessCategoryHealth(array $initializers): string
    {
        $unhealthyCount = 0;
        foreach ($initializers as $initializer) {
            $health = $this->checkSingleInitializerHealth($initializer);
            if ($health['status'] === 'unhealthy') {
                $unhealthyCount++;
            }
        }

        if ($unhealthyCount > 0) {
            return 'unhealthy';
        }

        return 'healthy';
    }

    private function calculateCategoryComplexity(array $initializers): float
    {
        if (count($initializers) === 0) {
            return 0.0;
        }

        $totalComplexity = 0;
        foreach ($initializers as $initializer) {
            $metrics = $this->analyzeInitializerComplexity($initializer);
            $totalComplexity += $metrics['complexity_score'];
        }

        return round($totalComplexity / count($initializers), 2);
    }

    private function analyzeCategoryDependencies(array $initializers): array
    {
        // Implementation for category dependency analysis
        return [];
    }

    private function findCrossCategoryDependencies(array $categories): array
    {
        // Implementation for cross-category dependency analysis
        return [];
    }

    private function assessBootstrapPerformance(float $totalTime, array $results): array
    {
        $assessment = 'excellent';

        if ($totalTime > 1.0) {
            $assessment = 'poor';
        } elseif ($totalTime > 0.5) {
            $assessment = 'acceptable';
        } elseif ($totalTime > 0.2) {
            $assessment = 'good';
        }

        return [
            'assessment' => $assessment,
            'time_rating' => $totalTime < 0.1 ? 'very_fast' : ($totalTime < 0.5 ? 'fast' : 'slow'),
            'recommendations' => $this->generatePerformanceRecommendations($totalTime, $results),
        ];
    }

    private function generatePerformanceRecommendations(float $totalTime, array $results): array
    {
        $recommendations = [];

        if ($totalTime > 0.5) {
            $recommendations[] = 'Consider caching initializer discovery results';
        }

        if (isset($results['class_loading']['time_ms']) && $results['class_loading']['time_ms'] > 200) {
            $recommendations[] = 'Optimize class loading with better autoloading';
        }

        return $recommendations;
    }
}
