<?php
declare(strict_types=1);

namespace App\Framework\Core;

use App\Framework\Cache\Cache;
use App\Framework\Discovery\FileVisitor;

/**
 * Visitor zum Auffinden von Interface-Implementierungen
 */
final class InterfaceImplementationVisitor implements FileVisitor
{
    private array $interfaces = [];
    private array $implementations = [];
    private array $implementationsByClass = [];

    /**
     * @param array $targetInterfaces Die zu suchenden Interfaces
     */
    public function __construct(private readonly array $targetInterfaces = [])
    {
    }

    public function onScanStart(): void
    {
        $this->implementations = [];
        $this->implementationsByClass = [];
    }

    public function onIncrementalScanStart(): void
    {
        // Bei inkrementellem Scan behalten wir vorhandene Implementierungen
        // und aktualisieren sie mit neuen Daten
    }

    public function onIncrementalScanComplete(): void
    {
        // Sortiere nach inkrementellem Scan
        $this->sortImplementations();
    }

    public function visitClass(string $className, string $filePath): void
    {
        if (!class_exists($className)) {
            return;
        }

        try {
            $reflection = new \ReflectionClass($className);

            // Abstrakte Klassen und Interfaces überspringen
            if ($reflection->isAbstract() || $reflection->isInterface()) {
                return;
            }

            // Alte Einträge für diese Klasse entfernen (wichtig für inkrementelle Scans)
            $this->removeImplementationsForClass($className);

            // Prüfe, ob die Klasse eines der Ziel-Interfaces implementiert
            foreach ($this->targetInterfaces as $interface) {
                if ($reflection->implementsInterface($interface)) {
                    if (!isset($this->implementations[$interface])) {
                        $this->implementations[$interface] = [];
                    }

                    // Prüfe auf Duplikate
                    if (!in_array($className, $this->implementations[$interface])) {
                        $this->implementations[$interface][] = $className;

                        // Umgekehrten Index für schnelleren Zugriff aufbauen
                        if (!isset($this->implementationsByClass[$className])) {
                            $this->implementationsByClass[$className] = [];
                        }
                        $this->implementationsByClass[$className][] = $interface;
                    }
                }
            }
        } catch (\Throwable $e) {
            error_log("Fehler beim Prüfen der Interface-Implementierung für {$className}: " . $e->getMessage());
        }
    }

    /**
     * Entfernt alle Interface-Implementierungen für eine bestimmte Klasse
     */
    private function removeImplementationsForClass(string $className): void
    {
        if (!isset($this->implementationsByClass[$className])) {
            return;
        }

        foreach ($this->implementationsByClass[$className] as $interface) {
            if (isset($this->implementations[$interface])) {
                $key = array_search($className, $this->implementations[$interface]);
                if ($key !== false) {
                    unset($this->implementations[$interface][$key]);
                    // Array-Indizes neu numerieren
                    $this->implementations[$interface] = array_values($this->implementations[$interface]);
                }
            }
        }

        unset($this->implementationsByClass[$className]);
    }

    public function onScanComplete(): void
    {
        // Sortiere Implementierungen für konsistente Reihenfolge
        $this->sortImplementations();
    }

    /**
     * Sortiert alle Implementierungen für konsistente Ergebnisse
     */
    private function sortImplementations(): void
    {
        foreach ($this->implementations as &$classes) {
            sort($classes);
        }
    }

    public function loadFromCache(Cache $cache): void
    {
        $cacheItem = $cache->get($this->getCacheKey());
        if ($cacheItem->isHit) {
            if (is_array($cacheItem->value) && isset($cacheItem->value['implementations'], $cacheItem->value['byClass'])) {
                $this->implementations = $cacheItem->value['implementations'];
                $this->implementationsByClass = $cacheItem->value['byClass'];
            }
        }
    }

    public function getCacheKey(): string
    {
        return 'interface_implementations';
    }

    public function getCacheableData(): mixed
    {
        return [
            'implementations' => $this->implementations,
            'byClass' => $this->implementationsByClass
        ];
    }

    /**
     * Gibt alle Implementierungen eines Interfaces zurück
     */
    public function getImplementations(string $interface): array
    {
        return $this->implementations[$interface] ?? [];
    }

    /**
     * Prüft, ob eine Klasse ein bestimmtes Interface implementiert
     */
    public function doesImplement(string $className, string $interface): bool
    {
        return isset($this->implementationsByClass[$className]) &&
               in_array($interface, $this->implementationsByClass[$className]);
    }

    /**
     * Gibt alle Interfaces zurück, die eine Klasse implementiert
     */
    public function getClassInterfaces(string $className): array
    {
        return $this->implementationsByClass[$className] ?? [];
    }

    /**
     * Gibt alle gefundenen Implementierungen zurück
     */
    public function getAllImplementations(): array
    {
        return $this->implementations;
    }
}
