<?php
declare(strict_types=1);

namespace App\Framework\View;

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

/**
 * Visitor zum Auffinden von Templates
 */
final class TemplateDiscoveryVisitor implements FileVisitor
{
    private array $templates = [];
    private array $scannedDirectories = [];
    private string $templateExtension;
    private ?array $templateLookupIndex = null;

    public function __construct(string $templateExtension = '.view.php')
    {
        $this->templateExtension = $templateExtension;
    }

    public function onScanStart(): void
    {
        $this->templates = [];
        $this->scannedDirectories = [];
        $this->templateLookupIndex = null;
    }

    public function visitClass(string $className, string $filePath): void
    {
        // Hier prüfen wir nicht auf Klassen, sondern auf Template-Dateien
        $directory = dirname($filePath);

        // Vermeide mehrfaches Scannen desselben Verzeichnisses
        if (isset($this->scannedDirectories[$directory])) {
            return;
        }

        $this->scannedDirectories[$directory] = true;

        // Suche Templates im selben Verzeichnis wie die Klasse
        $templateFiles = glob($directory . '/*' . $this->templateExtension);

        foreach ($templateFiles as $templateFile) {
            $filename = basename($templateFile);
            $templateName = substr($filename, 0, -strlen($this->templateExtension));

            if (!isset($this->templates[$templateName])) {
                $this->templates[$templateName] = [];
            }

            // Ordne das Template der Klasse zu, wenn sie im selben Verzeichnis ist
            $this->templates[$templateName][$className] = $templateFile;

            // Füge auch einen Standard-Eintrag ohne Klasse hinzu
            $this->templates[$templateName][''] = $templateFile;
        }
    }

    public function onScanComplete(): void
    {
        // Erstelle einen Index für schnelleren Zugriff
        $this->createTemplateLookupIndex();
    }

    /**
     * Wird aufgerufen, wenn ein inkrementeller Scan beginnt
     */
    public function onIncrementalScanStart(): void
    {
        // Behalte vorhandene Templates bei, setze nur scannedDirectories zurück
        $this->scannedDirectories = [];
        $this->templateLookupIndex = null;
    }

    /**
     * Wird aufgerufen, wenn ein inkrementeller Scan abgeschlossen ist
     */
    public function onIncrementalScanComplete(): void
    {
        // Aktualisiere den Index nach inkrementellem Scan
        $this->createTemplateLookupIndex();
    }

    /**
     * Erstellt einen Index für schnelleren Template-Zugriff
     */
    private function createTemplateLookupIndex(): void
    {
        $this->templateLookupIndex = [];

        foreach ($this->templates as $templateName => $variants) {
            foreach ($variants as $className => $path) {
                $key = $this->buildLookupKey($templateName, $className);
                $this->templateLookupIndex[$key] = $path;
            }
        }
    }

    /**
     * Erstellt einen eindeutigen Lookup-Schlüssel für ein Template
     */
    private function buildLookupKey(string $templateName, string $className = ''): string
    {
        return $templateName . '|' . $className;
    }

    /**
     * Lädt Daten aus dem Cache
     */
    public function loadFromCache(Cache $cache): void
    {
        $cacheItem = $cache->get($this->getCacheKey());
        if ($cacheItem->isHit) {
            $this->templates = $cacheItem->value;
            $this->createTemplateLookupIndex();
        }
    }

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

    /**
     * Liefert die zu cachenden Daten des Visitors
     */
    public function getCacheableData(): array
    {
        return $this->templates;
    }

    /**
     * Gibt den Pfad eines Templates zurück
     */
    public function getTemplatePath(string $templateName, ?string $className = null): ?string
    {
        // Verwende den Index für schnelleren Zugriff, wenn verfügbar
        if ($this->templateLookupIndex !== null) {
            $key = $this->buildLookupKey($templateName, $className ?? '');
            if (isset($this->templateLookupIndex[$key])) {
                return $this->templateLookupIndex[$key];
            }

            // Fallback auf Standard-Template
            $fallbackKey = $this->buildLookupKey($templateName, '');
            return $this->templateLookupIndex[$fallbackKey] ?? null;
        }

        // Klassische Suche als Fallback
        if (isset($this->templates[$templateName][$className ?? ''])) {
            return $this->templates[$templateName][$className ?? ''];
        }

        return $this->templates[$templateName][''] ?? null;
    }

    /**
     * Gibt alle gefundenen Templates zurück
     */
    public function getAllTemplates(): array
    {
        return $this->templates;
    }

    /**
     * Gibt alle Templates in einem bestimmten Verzeichnis zurück
     */
    public function getTemplatesInDirectory(string $directory): array
    {
        $result = [];
        $normalizedDirectory = rtrim($directory, '/');

        foreach ($this->templates as $templateName => $variants) {
            foreach ($variants as $className => $path) {
                if (dirname($path) === $normalizedDirectory) {
                    if (!isset($result[$templateName])) {
                        $result[$templateName] = [];
                    }
                    $result[$templateName][$className] = $path;
                }
            }
        }

        return $result;
    }
}
