<?php
declare(strict_types=1);

namespace App\Framework\Filesystem;

use App\Framework\Logging\DefaultLogger;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LoggerFactory;
use ReflectionClass;

/**
 * Factory für Filesystem-Objekte mit Lazy-Loading-Unterstützung.
 */
final class FilesystemFactory
{
    /**
     * Erstellt ein File-Objekt mit Lazy-Loading für schwere Eigenschaften.
     *
     * @param string $path Pfad zur Datei
     * @param Storage $storage Storage-Implementierung
     * @param int|null $cacheTimeoutSeconds Optional, Zeit in Sekunden, nach der der Cache ungültig wird
     * @param bool $lazyLoad Optional, ob Lazy-Loading verwendet werden soll
     * @param DefaultLogger|null $logger Optional, Logger für Debug-Informationen
     */
    public static function createFile(
        string $path,
        Storage $storage,
        ?int $cacheTimeoutSeconds = null,
        bool $lazyLoad = true,
        ?DefaultLogger $logger = null
    ): File {
        $logger ??= LoggerFactory::getDefaultLogger();

        // Direkte Instanziierung ohne Lazy-Loading
        if (!$lazyLoad) {
            $logger->debug("Erstelle File-Objekt ohne Lazy-Loading: {$path}");

            // Nur laden wenn die Datei existiert
            if (!$storage->exists($path)) {
                return new File($path, $storage);
            }

            return new File(
                path: $path,
                storage: $storage,
                contents: $storage->get($path),
                size: $storage->size($path),
                lastModified: $storage->lastModified($path)
            );
        }

        $reflection = new ReflectionClass(File::class);
        $loadTime = time();

        // LazyProxy verwenden für individuelle Property-Callbacks
        return $reflection->newLazyProxy([
            // Dateiinhalt wird erst beim ersten Zugriff geladen
            'contents' => function(File $file) use ($loadTime, $cacheTimeoutSeconds, $logger) {
                $logger->debug("Lazy-Loading contents für {$file->path}");

                // Cache-Invalidierung basierend auf Zeit
                if ($cacheTimeoutSeconds !== null && time() - $loadTime > $cacheTimeoutSeconds) {
                    $logger->debug("Cache-Timeout erreicht für {$file->path}, lade neu");
                }

                if (!$file->exists()) {
                    $logger->debug("Datei existiert nicht: {$file->path}");
                    return '';
                }

                return $file->storage->get($file->path);
            },

            // Dateigröße wird erst beim ersten Zugriff ermittelt
            'size' => function(File $file) use ($loadTime, $cacheTimeoutSeconds, $logger) {
                $logger->debug("Lazy-Loading size für {$file->path}");

                // Cache-Invalidierung basierend auf Zeit
                if ($cacheTimeoutSeconds !== null && time() - $loadTime > $cacheTimeoutSeconds) {
                    $logger->debug("Cache-Timeout erreicht für {$file->path}, lade neu");
                }

                if (!$file->exists()) {
                    $logger->debug("Datei existiert nicht: {$file->path}");
                    return 0;
                }

                return $file->storage->size($file->path);
            },

            // Zeitstempel wird erst beim ersten Zugriff ermittelt
            'lastModified' => function(File $file) use ($loadTime, $cacheTimeoutSeconds, $logger) {
                $logger->debug("Lazy-Loading lastModified für {$file->path}");

                // Cache-Invalidierung basierend auf Zeit
                if ($cacheTimeoutSeconds !== null && time() - $loadTime > $cacheTimeoutSeconds) {
                    $logger->debug("Cache-Timeout erreicht für {$file->path}, lade neu");
                }

                if (!$file->exists()) {
                    $logger->debug("Datei existiert nicht: {$file->path}");
                    return 0;
                }

                return $file->storage->lastModified($file->path);
            },
        ]);
    }

    /**
     * Erstellt ein Directory-Objekt mit Lazy-Loading für schwere Eigenschaften.
     *
     * @param string $path Pfad zum Verzeichnis
     * @param Storage $storage Storage-Implementierung
     * @param bool $lazyLoad Optional, ob Lazy-Loading verwendet werden soll
     * @param DefaultLogger|null $logger Optional, Logger für Debug-Informationen
     */
    public static function createDirectory(
        string $path,
        Storage $storage,
        bool $lazyLoad = true,
        ?DefaultLogger $logger = null
    ): Directory {
        $logger ??= LoggerFactory::getDefaultLogger();

        // Direkte Instanziierung ohne Lazy-Loading
        if (!$lazyLoad) {
            $logger->debug("Erstelle Directory-Objekt ohne Lazy-Loading: {$path}");

            $contents = [];
            if (is_dir($path)) {
                $contents = $storage->listDirectory($path);
            }

            return new Directory($path, $storage, $contents);
        }

        $reflection = new ReflectionClass(Directory::class);

        // LazyGhost verwenden - alle Eigenschaften werden beim ersten Zugriff initialisiert
        $lazyDir = $reflection->newLazyGhost(
            // Initializer-Callback
            function(Directory $directory) use ($logger): void {
                $logger->debug("Lazy-Loading Directory-Inhalt für {$directory->path}");

                // Verzeichnisinhalt wird erst beim ersten Zugriff auf eine Eigenschaft geladen
                if ($directory->exists()) {
                    $directory->contents = $directory->storage->listDirectory($directory->path);
                } else {
                    $directory->contents = [];
                }
            }
        );
        /** @var Directory $lazyDir */
        return $lazyDir;
    }
}
