<?php

declare(strict_types=1);

namespace App\Framework\Core;

use App\Framework\Discovery\FileScanner;
use Exception;
use ReflectionClass;

class Discovery
{
    private AttributeProcessor $attributeProcessor;
    private FileScanner $fileScanner;
    private ClassParser $classParser;
    private DiscoveryCacheManager $cacheManager;

    /**
     * @param AttributeMapper[] $mappers
     */
    public function __construct(
        ?string $srcDir = null,
        AttributeMapper ...$mappers
    ) {
        // Automatisches Auffinden der Mapper, wenn ein Quellverzeichnis angegeben ist
        if ($srcDir !== null) {
            $locator = new AttributeMapperLocator();
            $autoMappers = $locator->locateMappers($srcDir);
            $mappers = array_merge($mappers, $autoMappers);
        }

        // Initialisierung der Komponenten
        $this->attributeProcessor = new AttributeProcessor($mappers);
        $this->fileScanner = new FileScanner();
        $this->classParser = new ClassParser();
        $this->cacheManager = new DiscoveryCacheManager();
    }

    /**
     * Entdeckt und verarbeitet alle Attribute im angegebenen Verzeichnis
     */
    public function discover(string $directory): array
    {
        $cacheFile = $this->cacheManager->getCachePathForDirectory($directory);
        $latestMTime = $this->fileScanner->getLatestMTime($directory);

        // Versuchen, aus dem Cache zu laden
        $data = $this->cacheManager->loadCache($cacheFile, $latestMTime);
        if ($data !== null) {
            return $data;
        }

        // Keine Cache-Daten verfügbar, führe vollständige Entdeckung durch
        $results = $this->performDiscovery($directory);

        // Speichere Ergebnisse im Cache
        $results['__discovery_mtime'] = $latestMTime;
        $this->cacheManager->storeCache($cacheFile, $results);

        // Entferne Metadaten für die Rückgabe
        unset($results['__discovery_mtime']);

        return $results;
    }

    /**
     * Führt die eigentliche Entdeckung durch
     */
    private function performDiscovery(string $directory): array
    {
        $results = [];

        foreach ($this->fileScanner->scanDirectory($directory) as $file) {
            try {
                $className = $this->classParser->getClassNameFromFile($file->getPathname());

                if (!$className || !class_exists($className)) {
                    continue;
                }

                $refClass = new ReflectionClass($className);
                $this->attributeProcessor->processClass($refClass, $results);

            } catch (Exception $e) {
                error_log("Discovery Warnung: Fehler in Datei {$file->getPathname()}: " . $e->getMessage());
            }
        }

        return $results;
    }

}
