<?php

declare(strict_types=1);

namespace App\Framework\Discovery\Plugins;

use App\Framework\Discovery\Analysis\DependencyAnalysisResult;
use App\Framework\Discovery\Analysis\DependencyAnalyzer;
use App\Framework\Discovery\DiscoveryDataCollector;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Discovery\ValueObjects\DiscoveredAttribute;
use App\Framework\Discovery\ValueObjects\FileContext;
use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Reflection\ReflectionProvider;

final readonly class DependencyAnalysisPlugin implements DiscoveryPlugin
{
    private DependencyAnalyzer $analyzer;

    public function __construct(
        private ReflectionProvider $reflectionProvider,
        private ?Logger $logger = null
    ) {
        $this->analyzer = new DependencyAnalyzer($this->reflectionProvider, $this->logger);
    }

    /**
     * Get the name of this plugin
     */
    public function getName(): string
    {
        return 'dependency_analysis';
    }

    /**
     * Check if this plugin can process the given file
     */
    public function canProcess(FilePath $file, FileContext $context): bool
    {
        // Process all PHP files that contain classes
        return $file->hasExtension('php') && ! empty($context->getClassNames());
    }

    /**
     * Process a file and collect dependency information
     */
    public function processFile(FilePath $file, FileContext $context, DiscoveryDataCollector $collector): void
    {
        $classNames = $context->getClassNames();

        foreach ($classNames as $className) {
            // Create a discovered attribute for each class to track it
            $attribute = DiscoveredAttribute::create(
                className: $className,
                attributeType: 'DependencyNode',
                instance: null, // Will be filled during analysis
                metadata: [
                    'file' => $file->toString(),
                    'discovered_at' => date('Y-m-d H:i:s'),
                ]
            );

            $collector->addAttribute($attribute);
        }

        $this->logger?->debug('Processed file for dependency analysis', LogContext::withData([
            'file' => $file->toString(),
            'classes_found' => count($classNames),
        ]));
    }

    /**
     * Post-process the discovery results to perform dependency analysis
     */
    public function postProcess(DiscoveryRegistry $registry): DiscoveryRegistry
    {
        $this->logger?->info('Starting post-processing dependency analysis');

        // Extract all class names from the discovered attributes
        $classNames = [];
        foreach ($registry->getByType('DependencyNode') as $attribute) {
            $classNames[] = $attribute->className;
        }

        if (empty($classNames)) {
            $this->logger?->warning('No classes found for dependency analysis');

            return $registry;
        }

        // Perform dependency analysis
        $analysisResult = $this->analyzer->analyzeWithCircularDetection($classNames);

        // Add analysis result to the registry
        $analysisAttribute = DiscoveredAttribute::create(
            className: 'DependencyAnalysisResult',
            attributeType: 'DependencyAnalysis',
            instance: $analysisResult,
            metadata: [
                'analysis_timestamp' => date('Y-m-d H:i:s'),
                'classes_analyzed' => count($classNames),
                'circular_dependencies' => $analysisResult->getCircularDependencyCount(),
                'statistics' => $analysisResult->getStatistics(),
            ]
        );

        $updatedRegistry = $registry->add($analysisAttribute);

        // Update individual class attributes with dependency information
        $graph = $analysisResult->getGraph();
        foreach ($registry->getByType('DependencyNode') as $attribute) {
            $node = $graph->getNode(\App\Framework\Core\ValueObjects\ClassName::create($attribute->className));
            if ($node !== null) {
                $updatedAttribute = DiscoveredAttribute::create(
                    className: $attribute->className,
                    attributeType: 'DependencyNode',
                    instance: $node,
                    metadata: array_merge($attribute->metadata, [
                        'dependency_count' => $node->getDependencyCount(),
                        'dependent_count' => $node->getDependentCount(),
                        'complexity_score' => $node->getComplexityScore(),
                        'type' => $node->getType()->value,
                    ])
                );

                $updatedRegistry = $updatedRegistry->replace($attribute, $updatedAttribute);
            }
        }

        $this->logger?->info('Dependency analysis completed', LogContext::withData([
            'classes_analyzed' => count($classNames),
            'nodes_created' => $graph->getNodeCount(),
            'edges_created' => $graph->getEdgeCount(),
            'circular_dependencies' => $analysisResult->getCircularDependencyCount(),
        ]));

        return $updatedRegistry;
    }

    /**
     * Get the analysis result from the registry
     */
    public function getAnalysisResult(DiscoveryRegistry $registry): ?DependencyAnalysisResult
    {
        $analysisAttributes = $registry->getByType('DependencyAnalysis');
        if (empty($analysisAttributes)) {
            return null;
        }

        $attribute = reset($analysisAttributes);

        return $attribute->instance instanceof DependencyAnalysisResult ? $attribute->instance : null;
    }

    /**
     * Get recommendations based on the analysis
     *
     * @return array<string, mixed>
     */
    public function getRecommendations(DiscoveryRegistry $registry): array
    {
        $analysisResult = $this->getAnalysisResult($registry);
        if ($analysisResult === null) {
            return ['error' => 'No dependency analysis results found'];
        }

        return $this->analyzer->getRecommendations($analysisResult->getGraph());
    }
}
