<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Tools;

use App\Framework\DI\Container;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Discovery\UnifiedDiscoveryService;
use App\Framework\Mcp\McpTool;
use App\Framework\Router\CompiledRoutes;

final readonly class FrameworkTools
{
    public function __construct(
        private Container $container,
        private UnifiedDiscoveryService $discoveryService,
        private CompiledRoutes $compiledRoutes,
        private DiscoveryRegistry $discoveryRegistry
    ) {
    }

    #[McpTool(
        name: 'analyze_routes',
        description: 'Get all registered routes in the framework'
    )]
    public function analyzeRoutes(): array
    {
        // Try compiled routes first (web context)
        try {
            $namedRoutes = $this->compiledRoutes->getAllNamedRoutes();
            $staticRoutes = $this->compiledRoutes->getStaticRoutes();

            $namedRoutesArray = [];
            foreach ($namedRoutes as $name => $route) {
                $namedRoutesArray[] = [
                    'name' => $name,
                    'path' => $route->path,
                    'controller' => $route->controller,
                    'action' => $route->action,
                    'parameters' => $route->parameters,
                    'attributes' => $route->attributes,
                ];
            }

            $staticRoutesArray = [];
            foreach ($staticRoutes as $method => $subdomains) {
                foreach ($subdomains as $subdomain => $paths) {
                    foreach ($paths as $path => $route) {
                        $staticRoutesArray[] = [
                            'method' => $method,
                            'subdomain' => $subdomain,
                            'path' => $path,
                            'controller' => $route->controller,
                            'action' => $route->action,
                            'name' => $route->name ?? null,
                            'parameters' => $route->parameters,
                            'attributes' => $route->attributes,
                        ];
                    }
                }
            }

            $compiledTotal = count($namedRoutesArray) + count($staticRoutesArray);
        } catch (\Exception $e) {
            $namedRoutesArray = [];
            $staticRoutesArray = [];
            $compiledTotal = 0;
        }

        // Also get discovered routes (CLI/MCP context)
        $routeRegistry = $this->discoveryService->getResults();
        $discoveredRoutes = [];

        try {
            $routeAttributes = $routeRegistry->attributes->get(\App\Framework\Attributes\Route::class);

            foreach ($routeAttributes as $attribute) {
                $discoveredRoutes[] = [
                    'class' => $attribute->additionalData['class'] ?? 'unknown',
                    'method' => $attribute->additionalData['method'] ?? 'unknown',
                    'path' => $attribute->arguments['path'] ?? 'unknown',
                    'http_method' => $attribute->arguments['method'] ?? 'unknown',
                    'name' => $attribute->arguments['name'] ?? null,
                    'file' => $attribute->filePath->filename ?? 'unknown',
                    'parameters' => $attribute->additionalData['parameters'] ?? [],
                ];
            }
        } catch (\Exception $e) {
            // Fallback if discovery fails
        }

        return [
            'compiled_routes' => [
                'named_routes' => $namedRoutesArray,
                'static_routes' => $staticRoutesArray,
                'total' => $compiledTotal,
            ],
            'discovered_routes' => [
                'routes' => $discoveredRoutes,
                'total' => count($discoveredRoutes),
            ],
            'summary' => [
                'compiled_total' => $compiledTotal,
                'discovered_total' => count($discoveredRoutes),
                'context' => $compiledTotal > 0 ? 'web' : 'cli/mcp',
            ],
        ];
    }

    #[McpTool(
        name: 'analyze_container_bindings',
        description: 'Analyze DI container bindings and registrations'
    )]
    public function analyzeContainerBindings(): array
    {
        try {
            $int = $this->container->introspector;

            $bindings = $int->listBindings();
            $singletons = $int->listSingletons();
            $instances = $int->listInstances();

            $bindingDetails = [];
            $issues = [];

            foreach ($bindings as $abstract) {
                $binding = $int->getBinding($abstract);

                $type = null;
                $target = null;

                if ($binding !== null) {
                    if (is_callable($binding)) {
                        $type = 'callable';
                    } elseif (is_string($binding)) {
                        $type = 'string';
                        $target = $binding;
                    } else {
                        $type = 'object';
                        $target = get_class($binding);
                    }
                }

                $isSingleton = $int->isSingleton($abstract);

                $desc = $int->describe($abstract);

                $unresolvedParams = [];
                $params = $desc['constructor']['parameters'] ?? [];
                foreach ($params as $p) {
                    $isBuiltin = $p['is_builtin'] ?? false;
                    $resolvable = $p['resolvable'] ?? true;
                    if (($p['type'] ?? null) && ! $isBuiltin && $resolvable === false) {
                        $unresolvedParams[] = ($p['type'] ?? 'mixed') . ' $' . ($p['name'] ?? '?');
                    }
                }

                $targetInstantiable = null;
                if (is_string($target)) {
                    $targetInstantiable = $int->isInstantiable($target);
                    if ($type === 'string' && ! $targetInstantiable && ! $this->container->has($target)) {
                        $issues[] = "Alias binding '{$abstract}' -> '{$target}' is not instantiable or bound";
                    }
                }

                if (! empty($unresolvedParams)) {
                    $issues[] = "{$abstract} has unresolved constructor deps: " . implode(', ', $unresolvedParams);
                }

                $bindingDetails[] = [
                    'abstract' => $abstract,
                    'binding_type' => $type,
                    'target' => $target,
                    'is_singleton' => $isSingleton,
                    'exists' => $desc['exists'] ?? false,
                    'instantiable' => $desc['instantiable'] ?? false,
                    'constructor' => $desc['constructor'] ?? [],
                    'resolution_chain' => $desc['resolution_chain'] ?? [],
                    'target_instantiable' => $targetInstantiable,
                ];
            }

            // Detect classes instantiated without explicit bindings (directly resolved)
            $unboundInstantiated = array_values(array_diff($instances, $bindings));
            foreach ($unboundInstantiated as $cls) {
                if (! $int->isInstantiable($cls)) {
                    $issues[] = "Instantiated '{$cls}' is not instantiable (abstract/interface?)";
                }
            }

            $summary = [
                'bindings' => count($bindings),
                'singletons_marked' => count($singletons),
                'instances' => count($instances),
                'issues' => count($issues),
            ];

            $recommendations = [];
            if ($summary['issues'] > 0) {
                $recommendations[] = 'Add bindings for unresolved constructor dependencies';
                $recommendations[] = 'For alias bindings, ensure target is instantiable or has its own binding';
                $recommendations[] = 'Mark stateless services as singleton to reduce allocations';
            }
            if ($summary['bindings'] === 0) {
                $recommendations[] = 'No bindings detected; ensure initializer registrations are executed';
            }

            return [
                'summary' => $summary,
                'bindings' => $bindingDetails,
                'singletons' => $singletons,
                'instances' => $instances,
                'issues' => $issues,
                'recommendations' => array_values(array_unique($recommendations)),
            ];
        } catch (\Throwable $e) {
            return [
                'error' => $e->getMessage(),
            ];
        }
    }

    #[McpTool(
        name: 'discovery_cache_status',
        description: 'Get discovery cache health and metrics'
    )]
    public function discoveryCacheStatus(): array
    {
        try {
            return $this->discoveryService->getCacheStatus();
        } catch (\Throwable $e) {
            return ['error' => $e->getMessage()];
        }
    }

    #[McpTool(
        name: 'discovery_cache_warm',
        description: 'Warm discovery cache for default or provided paths',
        inputSchema: [
            'type' => 'object',
            'properties' => [
                'paths' => [
                    'type' => 'array',
                    'items' => ['type' => 'string'],
                    'description' => 'Optional list of paths to warm cache for',
                ],
            ],
        ]
    )]
    public function discoveryCacheWarm(?array $paths = null): array
    {
        try {
            return $this->discoveryService->warmCache($paths);
        } catch (\Throwable $e) {
            return ['error' => $e->getMessage()];
        }
    }

    #[McpTool(
        name: 'discovery_cache_clear',
        description: 'Clear all discovery cache entries'
    )]
    public function discoveryCacheClear(): array
    {
        try {
            $cleared = $this->discoveryService->clearCache();

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

    #[McpTool(
        name: 'discover_attributes',
        description: 'Discover all attributes in the framework by type',
        inputSchema: [
            'type' => 'object',
            'properties' => [
                'attribute_class' => [
                    'type' => 'string',
                    'description' => 'The attribute class to discover (e.g., App\\Framework\\Attributes\\Route)',
                ],
            ],
            'required' => ['attribute_class'],
        ]
    )]
    public function discoverAttributes(string $attribute_class): array
    {
        try {
            // Use the unified discovery service to get discovery registry
            $discoveryRegistry = $this->discoveryService->discover();
            $results = $discoveryRegistry->attributes->get($attribute_class);

            return [
                'attribute_class' => $attribute_class,
                'count' => count($results),
                'discoveries' => array_slice($results, 0, 10), // Limit to first 10 for readability
                'total_found' => count($results),
            ];
        } catch (\Throwable $e) {
            return [
                'error' => $e->getMessage(),
                'attribute_class' => $attribute_class,
            ];
        }
    }

    #[McpTool(
        name: 'framework_health_check',
        description: 'Perform a basic health check of the framework components'
    )]
    public function frameworkHealthCheck(): array
    {
        $health = [
            'container' => 'healthy',
            'routes' => 'healthy',
            'discovery_service' => 'healthy',
        ];

        try {
            // Check if container is responsive
            $this->container->get(UnifiedDiscoveryService::class);
        } catch (\Throwable $e) {
            $health['container'] = 'error: ' . $e->getMessage();
        }

        try {
            // Check if routes are compiled
            $namedRoutesCount = count($this->compiledRoutes->getAllNamedRoutes());
            $health['routes'] = "named routes: $namedRoutesCount";
        } catch (\Throwable $e) {
            $health['routes'] = 'error: ' . $e->getMessage();
        }

        return [
            'status' => 'completed',
            'components' => $health,
            'timestamp' => date('Y-m-d H:i:s'),
        ];
    }

    #[McpTool(
        name: 'list_framework_modules',
        description: 'List all available framework modules and their components'
    )]
    public function listFrameworkModules(): array
    {
        $frameworkPath = dirname(__DIR__);
        $modules = [];

        if (is_dir($frameworkPath)) {
            $directories = scandir($frameworkPath);
            foreach ($directories as $dir) {
                if ($dir === '.' || $dir === '..') {
                    continue;
                }

                $fullPath = $frameworkPath . '/' . $dir;
                if (is_dir($fullPath)) {
                    $modules[] = [
                        'name' => $dir,
                        'path' => $fullPath,
                        'files' => $this->countPhpFiles($fullPath),
                    ];
                }
            }
        }

        return [
            'modules' => $modules,
            'total_modules' => count($modules),
        ];
    }

    #[McpTool(
        name: 'analyze_queue_initializers',
        description: 'Comprehensive analysis of Queue system initializers and their discovery status'
    )]
    public function analyzeQueueInitializers(): array
    {
        try {
            // Get discovery results
            $initializerResults = $this->discoveryRegistry->attributes->get(\App\Framework\Attributes\Initializer::class);

            // Filter Queue-related initializers
            $queueInitializers = [];
            foreach ($initializerResults as $result) {
                if (str_contains($result->className->toString(), 'Queue')) {
                    $queueInitializers[] = [
                        'class' => $result->className->toString(),
                        'method' => $result->methodName ? $result->methodName->toString() : '__invoke',
                        'file' => $result->filePath,
                        'additional_data' => $result->additionalData,
                        'return_type' => $result->additionalData['return'] ?? 'unknown',
                        'contexts' => $result->additionalData['contexts'] ?? null,
                    ];
                }
            }

            // Check expected Queue services
            $expectedServices = [
                'App\\Framework\\Queue\\Interfaces\\DistributedLockInterface',
                'App\\Framework\\Queue\\Services\\JobMetricsManagerInterface',
                'App\\Framework\\Queue\\Contracts\\JobProgressTrackerInterface',
                'App\\Framework\\Queue\\Contracts\\DeadLetterQueueInterface',
                'App\\Framework\\Queue\\Contracts\\JobDependencyManagerInterface',
                'App\\Framework\\Queue\\Services\\WorkerRegistry',
                'App\\Framework\\Queue\\Services\\JobDistributionService',
                'App\\Framework\\Queue\\Services\\WorkerHealthCheckService',
                'App\\Framework\\Queue\\Services\\FailoverRecoveryService',
            ];

            $serviceStatus = [];
            foreach ($expectedServices as $service) {
                try {
                    $hasService = $this->container->has($service);
                    $serviceStatus[$service] = $hasService ? 'REGISTERED' : 'NOT_REGISTERED';
                } catch (\Throwable $e) {
                    $serviceStatus[$service] = 'ERROR: ' . $e->getMessage();
                }
            }

            // Identify expected initializers and their status
            $expectedInitializers = [
                'App\\Framework\\Queue\\Initializers\\DistributedLockInitializer',
                'App\\Framework\\Queue\\Initializers\\JobMetricsInitializer',
                'App\\Framework\\Queue\\Initializers\\ProgressTrackingInitializer',
                'App\\Framework\\Queue\\Initializers\\DeadLetterQueueInitializer',
                'App\\Framework\\Queue\\QueueDependencyInitializer',
                'App\\Framework\\Queue\\Initializers\\WorkerRegistryInitializer',
                'App\\Framework\\Queue\\Initializers\\JobDistributionInitializer',
                'App\\Framework\\Queue\\Initializers\\WorkerHealthCheckInitializer',
                'App\\Framework\\Queue\\Initializers\\FailoverRecoveryInitializer',
            ];

            $initializerStatus = [];
            foreach ($expectedInitializers as $expected) {
                $found = false;
                foreach ($queueInitializers as $initializer) {
                    if ($initializer['class'] === $expected) {
                        $initializerStatus[$expected] = [
                            'status' => 'DISCOVERED',
                            'return_type' => $initializer['return_type'],
                            'file' => $initializer['file'],
                        ];
                        $found = true;

                        break;
                    }
                }
                if (! $found) {
                    $initializerStatus[$expected] = ['status' => 'NOT_DISCOVERED'];
                }
            }

            return [
                'summary' => [
                    'total_queue_initializers_discovered' => count($queueInitializers),
                    'expected_initializers' => count($expectedInitializers),
                    'expected_services' => count($expectedServices),
                    'registered_services' => count(array_filter($serviceStatus, fn ($status) => $status === 'REGISTERED')),
                ],
                'discovered_queue_initializers' => $queueInitializers,
                'expected_initializer_status' => $initializerStatus,
                'service_registration_status' => $serviceStatus,
                'analysis' => $this->generateQueueAnalysis($queueInitializers, $serviceStatus, $initializerStatus),
            ];

        } catch (\Throwable $e) {
            return [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
                'file' => $e->getFile() . ':' . $e->getLine(),
            ];
        }
    }

    private function generateQueueAnalysis(array $queueInitializers, array $serviceStatus, array $initializerStatus): array
    {
        $issues = [];
        $recommendations = [];

        // Check for missing initializers
        $notDiscovered = array_filter($initializerStatus, fn ($status) => $status['status'] === 'NOT_DISCOVERED');
        if (! empty($notDiscovered)) {
            $issues[] = count($notDiscovered) . ' expected initializers not discovered';
            $recommendations[] = 'Check file syntax and import statements in missing initializers';
        }

        // Check for unregistered services
        $notRegistered = array_filter($serviceStatus, fn ($status) => $status === 'NOT_REGISTERED');
        if (! empty($notRegistered)) {
            $issues[] = count($notRegistered) . ' expected services not registered';
            $recommendations[] = 'Verify initializers have correct return types and dependency resolution';
        }

        // Check for void return types (should be service types)
        $voidInitializers = array_filter($queueInitializers, fn ($init) => $init['return_type'] === 'void' || $init['return_type'] === null);
        if (! empty($voidInitializers)) {
            $issues[] = count($voidInitializers) . ' initializers have void return types (should return services)';
            $recommendations[] = 'Convert void initializers to return concrete service types for lazy registration';
        }

        return [
            'status' => empty($issues) ? 'HEALTHY' : 'ISSUES_DETECTED',
            'issues' => $issues,
            'recommendations' => $recommendations,
        ];
    }

    #[McpTool(
        name: 'framework_metrics',
        description: 'Get framework internal counters and metrics'
    )]
    public function frameworkMetrics(): array
    {
        try {
            $snapshot = $this->container->metrics->snapshot();

            return [
                'counters' => $snapshot->toArray(),
                'timestamp' => date('Y-m-d H:i:s'),
                'total_counters' => count($snapshot->toArray()),
            ];
        } catch (\Throwable $e) {
            return [
                'error' => $e->getMessage(),
            ];
        }
    }

    private function countPhpFiles(string $directory): int
    {
        $count = 0;
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS)
        );

        foreach ($iterator as $file) {
            if ($file->getExtension() === 'php') {
                $count++;
            }
        }

        return $count;
    }
}
