<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Tools\Categories\Analysis;

use App\Framework\Attributes\Singleton;
use App\Framework\DI\Container;
use App\Framework\Discovery\UnifiedDiscoveryService;
use App\Framework\Mcp\Core\Services\McpToolContext;
use App\Framework\Mcp\McpTool;

/**
 * Container and Dependency Injection Inspector
 *
 * Provides deep insights into the DI container and service dependencies
 * Refactored to use Composition Pattern with McpToolContext
 */
final readonly class ContainerInspector
{
    public function __construct(
        private McpToolContext $context,
        private Container $container,
        private UnifiedDiscoveryService $discoveryService
    ) {
    }

    /**
     * Inspect container bindings and dependencies
     */
    #[McpTool(
        name: 'inspect_container',
        description: 'Inspect dependency injection container bindings and resolution',
        category: 'analysis',
        tags: ['dependency-injection', 'container', 'bindings'],
        cacheable: true,
        defaultCacheTtl: 600
    )]
    public function inspectContainer(
        ?string $service = null,
        bool $includeSingletons = true,
        bool $includeBindings = true,
        bool $analyzeDependencies = false,
        bool $showMemoryUsage = false,
        string $format = 'array'
    ): array {
        try {
            // If specific service requested
            if ($service !== null) {
                $result = $this->inspectSpecificService($service, $analyzeDependencies, $showMemoryUsage);

                return $this->context->formatResult($result, $format);
            }

            $inspection = [
                'container_summary' => $this->getContainerSummary(),
                'bindings' => [],
                'singletons' => [],
                'discovered_singletons' => $this->discoverSingletonServices(),
            ];

            // Get container internals using reflection
            $reflection = new \ReflectionClass($this->container);

            // Inspect bindings
            if ($includeBindings) {
                $inspection['bindings'] = $this->inspectBindings($reflection, $analyzeDependencies, $showMemoryUsage);
            }

            // Inspect singletons
            if ($includeSingletons) {
                $inspection['singletons'] = $this->inspectSingletons($reflection, $showMemoryUsage);
            }

            // Build dependency graph if requested
            if ($analyzeDependencies) {
                $inspection['dependency_graph'] = $this->buildCompleteDependencyGraph();
                $inspection['circular_dependencies'] = $this->detectCircularDependencies();
            }

            return $this->context->formatResult($inspection, $format);

        } catch (\Throwable $e) {
            return $this->context->formatResult([
                'error' => $this->context->handleValidationError(['exception' => $e->getMessage()]),
                'service' => $service,
                'tool' => 'inspect_container',
            ], $format);
        }
    }

    /**
     * Analyze dependency graph for a specific service
     */
    #[McpTool(
        name: 'analyze_dependencies',
        description: 'Analyze dependency graph and detect circular dependencies',
        category: 'analysis',
        tags: ['dependencies', 'graph', 'circular'],
        cacheable: true,
        defaultCacheTtl: 900
    )]
    public function analyzeDependencies(
        ?string $service = null,
        int $maxDepth = 5,
        bool $includeCircular = true,
        string $format = 'array'
    ): array {
        try {
            $analysis = [
                'service' => $service,
                'dependency_tree' => [],
                'complexity_metrics' => [],
                'recommendations' => [],
            ];

            if ($service) {
                $analysis['dependency_tree'] = $this->buildDependencyTree($service, $maxDepth);
                $analysis['complexity_metrics'] = $this->calculateComplexityMetrics($service);
            } else {
                $analysis['global_dependency_analysis'] = $this->analyzeGlobalDependencies();
            }

            if ($includeCircular) {
                $analysis['circular_dependencies'] = $this->detectCircularDependencies();
            }

            $analysis['recommendations'] = $this->generateDependencyRecommendations($analysis);

            return $this->context->formatResult($analysis, $format);

        } catch (\Throwable $e) {
            return $this->context->formatResult([
                'error' => $this->context->handleValidationError(['exception' => $e->getMessage()]),
                'service' => $service,
                'tool' => 'analyze_dependencies',
            ], $format);
        }
    }

    /**
     * Show container performance metrics
     */
    #[McpTool(
        name: 'container_performance',
        description: 'Show container performance metrics and memory usage',
        category: 'analysis',
        tags: ['performance', 'memory', 'metrics'],
        cacheable: false
    )]
    public function containerPerformance(
        bool $includeMemoryDetails = true,
        bool $analyzeInstantiationTime = false,
        string $format = 'array'
    ): array {
        try {
            $performance = [
                'memory_usage' => $this->getMemoryUsage(),
                'service_count' => $this->getServiceCount(),
                'singleton_count' => $this->getSingletonCount(),
                'binding_count' => $this->getBindingCount(),
            ];

            if ($includeMemoryDetails) {
                $performance['memory_breakdown'] = $this->getMemoryBreakdown();
            }

            if ($analyzeInstantiationTime) {
                $performance['instantiation_analysis'] = $this->analyzeInstantiationPerformance();
            }

            $performance['recommendations'] = $this->generatePerformanceRecommendations($performance);

            return $this->context->formatResult($performance, $format);

        } catch (\Throwable $e) {
            return $this->context->formatResult([
                'error' => $this->context->handleValidationError(['exception' => $e->getMessage()]),
                'tool' => 'container_performance',
            ], $format);
        }
    }

    // Helper methods

    private function inspectSpecificService(string $service, bool $analyzeDependencies, bool $showMemoryUsage): array
    {
        $serviceInfo = [
            'service' => $service,
            'exists' => $this->container->has($service),
            'is_singleton' => false,
            'memory_usage' => null,
            'dependencies' => [],
            'dependent_services' => [],
        ];

        if (! $serviceInfo['exists']) {
            return [
                'error' => $this->context->handleNotFoundError("Service '{$service}' not found in container"),
                'service' => $service,
                'available_services' => $this->getAvailableServices(),
            ];
        }

        try {
            // Check if it's a singleton
            $serviceInfo['is_singleton'] = $this->isSingleton($service);

            // Get memory usage if requested
            if ($showMemoryUsage) {
                $serviceInfo['memory_usage'] = $this->getServiceMemoryUsage($service);
            }

            // Analyze dependencies if requested
            if ($analyzeDependencies) {
                $serviceInfo['dependencies'] = $this->getServiceDependencies($service);
                $serviceInfo['dependent_services'] = $this->getServiceDependents($service);
                $serviceInfo['dependency_depth'] = $this->calculateDependencyDepth($service);
            }

            // Additional service metadata
            $serviceInfo['metadata'] = $this->getServiceMetadata($service);

        } catch (\Throwable $e) {
            $serviceInfo['error'] = $e->getMessage();
        }

        return $serviceInfo;
    }

    private function getContainerSummary(): array
    {
        return [
            'total_services' => $this->getServiceCount(),
            'singleton_services' => $this->getSingletonCount(),
            'binding_count' => $this->getBindingCount(),
            'memory_usage' => $this->getMemoryUsage(),
            'container_class' => get_class($this->container),
        ];
    }

    private function discoverSingletonServices(): array
    {
        $singletonServices = [];

        try {
            $discoveredClasses = $this->discoveryService->getDiscoveredResults();

            foreach ($discoveredClasses as $class => $info) {
                if (isset($info['attributes']) && is_array($info['attributes'])) {
                    foreach ($info['attributes'] as $attribute) {
                        if ($attribute instanceof Singleton ||
                            (is_array($attribute) && isset($attribute['class']) && $attribute['class'] === Singleton::class)) {
                            $singletonServices[] = [
                                'class' => $class,
                                'file' => $info['file'] ?? 'unknown',
                                'discovered' => true,
                            ];
                        }
                    }
                }
            }
        } catch (\Throwable $e) {
            // If discovery fails, return empty array with error
            return [
                'error' => 'Failed to discover singleton services: ' . $e->getMessage(),
                'services' => [],
            ];
        }

        return $singletonServices;
    }

    private function inspectBindings(\ReflectionClass $reflection, bool $analyzeDependencies, bool $showMemoryUsage): array
    {
        $bindings = [];

        try {
            // This would need to be adapted to your specific container implementation
            // Since Container interface doesn't expose bindings directly, we'll use reflection
            if ($reflection->hasProperty('bindings')) {
                $bindingsProperty = $reflection->getProperty('bindings');
                $bindingsProperty->setAccessible(true);
                $containerBindings = $bindingsProperty->getValue($this->container);

                foreach ($containerBindings as $service => $binding) {
                    $bindingInfo = [
                        'service' => $service,
                        'type' => $this->getBindingType($binding),
                        'registered' => true,
                    ];

                    if ($showMemoryUsage) {
                        $bindingInfo['memory_usage'] = $this->getServiceMemoryUsage($service);
                    }

                    if ($analyzeDependencies) {
                        $bindingInfo['dependencies'] = $this->getServiceDependencies($service);
                    }

                    $bindings[] = $bindingInfo;
                }
            }
        } catch (\Throwable $e) {
            return [
                'error' => 'Failed to inspect bindings: ' . $e->getMessage(),
                'bindings' => [],
            ];
        }

        return $bindings;
    }

    private function inspectSingletons(\ReflectionClass $reflection, bool $showMemoryUsage): array
    {
        $singletons = [];

        try {
            if ($reflection->hasProperty('singletons')) {
                $singletonsProperty = $reflection->getProperty('singletons');
                $singletonsProperty->setAccessible(true);
                $containerSingletons = $singletonsProperty->getValue($this->container);

                foreach ($containerSingletons as $service => $instance) {
                    $singletonInfo = [
                        'service' => $service,
                        'class' => get_class($instance),
                        'instantiated' => true,
                    ];

                    if ($showMemoryUsage) {
                        $singletonInfo['memory_usage'] = $this->getObjectMemoryUsage($instance);
                    }

                    $singletons[] = $singletonInfo;
                }
            }
        } catch (\Throwable $e) {
            return [
                'error' => 'Failed to inspect singletons: ' . $e->getMessage(),
                'singletons' => [],
            ];
        }

        return $singletons;
    }

    private function buildCompleteDependencyGraph(): array
    {
        // Build a complete dependency graph for all services
        $graph = [];
        $services = $this->getAvailableServices();

        foreach ($services as $service) {
            try {
                $graph[$service] = $this->getServiceDependencies($service);
            } catch (\Throwable $e) {
                $graph[$service] = ['error' => $e->getMessage()];
            }
        }

        return $graph;
    }

    private function detectCircularDependencies(): array
    {
        $circular = [];
        $services = $this->getAvailableServices();
        $visited = [];
        $recursionStack = [];

        foreach ($services as $service) {
            if (! isset($visited[$service])) {
                $path = $this->detectCircularDependenciesRecursive($service, $visited, $recursionStack, []);
                if (! empty($path)) {
                    $circular[] = [
                        'cycle' => $path,
                        'length' => count($path),
                        'severity' => count($path) > 3 ? 'high' : 'medium',
                    ];
                }
            }
        }

        return $circular;
    }

    private function detectCircularDependenciesRecursive(string $service, array &$visited, array &$recursionStack, array $currentPath): array
    {
        $visited[$service] = true;
        $recursionStack[$service] = true;
        $currentPath[] = $service;

        try {
            $dependencies = $this->getServiceDependencies($service);

            foreach ($dependencies as $dependency) {
                if (! isset($visited[$dependency])) {
                    $cycle = $this->detectCircularDependenciesRecursive($dependency, $visited, $recursionStack, $currentPath);
                    if (! empty($cycle)) {
                        return $cycle;
                    }
                } elseif (isset($recursionStack[$dependency])) {
                    // Found a cycle
                    $cycleStart = array_search($dependency, $currentPath);

                    return array_slice($currentPath, $cycleStart);
                }
            }
        } catch (\Throwable $e) {
            // Skip services that can't be analyzed
        }

        unset($recursionStack[$service]);

        return [];
    }

    private function buildDependencyTree(string $service, int $maxDepth, int $currentDepth = 0): array
    {
        if ($currentDepth >= $maxDepth) {
            return ['max_depth_reached' => true];
        }

        $tree = [
            'service' => $service,
            'depth' => $currentDepth,
            'dependencies' => [],
        ];

        try {
            $dependencies = $this->getServiceDependencies($service);

            foreach ($dependencies as $dependency) {
                $tree['dependencies'][] = $this->buildDependencyTree($dependency, $maxDepth, $currentDepth + 1);
            }
        } catch (\Throwable $e) {
            $tree['error'] = $e->getMessage();
        }

        return $tree;
    }

    private function calculateComplexityMetrics(string $service): array
    {
        $metrics = [
            'direct_dependencies' => 0,
            'total_dependencies' => 0,
            'dependency_depth' => 0,
            'complexity_score' => 0,
        ];

        try {
            $dependencies = $this->getServiceDependencies($service);
            $metrics['direct_dependencies'] = count($dependencies);
            $metrics['dependency_depth'] = $this->calculateDependencyDepth($service);
            $metrics['total_dependencies'] = $this->countTotalDependencies($service);

            // Simple complexity score based on dependencies and depth
            $metrics['complexity_score'] = ($metrics['direct_dependencies'] * 2) +
                                         ($metrics['dependency_depth'] * 1.5) +
                                         ($metrics['total_dependencies'] * 0.5);

        } catch (\Throwable $e) {
            $metrics['error'] = $e->getMessage();
        }

        return $metrics;
    }

    private function analyzeGlobalDependencies(): array
    {
        $services = $this->getAvailableServices();
        $analysis = [
            'total_services' => count($services),
            'dependency_distribution' => [],
            'most_depended_on' => [],
            'heaviest_dependents' => [],
        ];

        $dependencyCount = [];
        $dependentCount = [];

        foreach ($services as $service) {
            try {
                $dependencies = $this->getServiceDependencies($service);
                $dependencyCount[$service] = count($dependencies);

                foreach ($dependencies as $dependency) {
                    $dependentCount[$dependency] = ($dependentCount[$dependency] ?? 0) + 1;
                }
            } catch (\Throwable $e) {
                // Skip services that can't be analyzed
            }
        }

        // Analyze distribution
        $distribution = array_count_values($dependencyCount);
        ksort($distribution);
        $analysis['dependency_distribution'] = $distribution;

        // Most depended on services
        arsort($dependentCount);
        $analysis['most_depended_on'] = array_slice($dependentCount, 0, 10, true);

        // Services with most dependencies
        arsort($dependencyCount);
        $analysis['heaviest_dependents'] = array_slice($dependencyCount, 0, 10, true);

        return $analysis;
    }

    private function generateDependencyRecommendations(array $analysis): array
    {
        $recommendations = [];

        if (isset($analysis['circular_dependencies']) && ! empty($analysis['circular_dependencies'])) {
            $recommendations[] = [
                'type' => 'circular_dependencies',
                'priority' => 'high',
                'message' => 'Circular dependencies detected. Consider using interfaces or event-driven patterns.',
                'count' => count($analysis['circular_dependencies']),
            ];
        }

        if (isset($analysis['complexity_metrics']['complexity_score']) && $analysis['complexity_metrics']['complexity_score'] > 20) {
            $recommendations[] = [
                'type' => 'high_complexity',
                'priority' => 'medium',
                'message' => 'Service has high complexity. Consider splitting into smaller services.',
                'score' => $analysis['complexity_metrics']['complexity_score'],
            ];
        }

        return $recommendations;
    }

    private function generatePerformanceRecommendations(array $performance): array
    {
        $recommendations = [];

        if (isset($performance['memory_usage']['total_mb']) && $performance['memory_usage']['total_mb'] > 100) {
            $recommendations[] = [
                'type' => 'high_memory_usage',
                'priority' => 'medium',
                'message' => 'Container using significant memory. Consider lazy loading strategies.',
                'memory_mb' => $performance['memory_usage']['total_mb'],
            ];
        }

        if (isset($performance['singleton_count']) && $performance['singleton_count'] > 50) {
            $recommendations[] = [
                'type' => 'many_singletons',
                'priority' => 'low',
                'message' => 'High number of singletons. Ensure they are all necessary.',
                'count' => $performance['singleton_count'],
            ];
        }

        return $recommendations;
    }

    // Utility methods that would need to be implemented based on your container structure

    private function getServiceCount(): int
    {
        // This would need to be implemented based on your container's internal structure
        return 0;
    }

    private function getSingletonCount(): int
    {
        // This would need to be implemented based on your container's internal structure
        return 0;
    }

    private function getBindingCount(): int
    {
        // This would need to be implemented based on your container's internal structure
        return 0;
    }

    private function getMemoryUsage(): array
    {
        return [
            'current_mb' => round(memory_get_usage() / 1024 / 1024, 2),
            'peak_mb' => round(memory_get_peak_usage() / 1024 / 1024, 2),
        ];
    }

    private function getMemoryBreakdown(): array
    {
        // Implementation would depend on your specific requirements
        return [];
    }

    private function analyzeInstantiationPerformance(): array
    {
        // Implementation for timing service instantiation
        return [];
    }

    private function getAvailableServices(): array
    {
        // Return list of available services from container
        return [];
    }

    private function isSingleton(string $service): bool
    {
        // Check if service is registered as singleton
        return false;
    }

    private function getServiceMemoryUsage(string $service): ?int
    {
        // Calculate memory usage for specific service
        return null;
    }

    private function getObjectMemoryUsage(object $instance): int
    {
        // Calculate memory usage for object instance
        return 0;
    }

    private function getServiceDependencies(string $service): array
    {
        // Get dependencies for a service using reflection
        return [];
    }

    private function getServiceDependents(string $service): array
    {
        // Get services that depend on this service
        return [];
    }

    private function calculateDependencyDepth(string $service): int
    {
        // Calculate the depth of dependency tree
        return 0;
    }

    private function countTotalDependencies(string $service): int
    {
        // Count total dependencies including transitive ones
        return 0;
    }

    private function getServiceMetadata(string $service): array
    {
        // Get metadata about the service
        return [];
    }

    private function getBindingType(mixed $binding): string
    {
        // Determine the type of binding (closure, class, instance, etc.)
        return 'unknown';
    }
}
