<?php

declare(strict_types=1);

namespace App\Framework\Filesystem;

use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Logging\DefaultLogger;
use App\Framework\Logging\LoggerFactory;
use ReflectionClass;

/**
 * Factory für Filesystem-Objekte mit Lazy-Loading-Unterstützung.
 */
final readonly class FilesystemFactory
{
    /**
     * Erstellt ein File-Objekt mit Lazy-Loading für schwere Eigenschaften.
     *
     * @param FilePath|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(
        FilePath|string $path,
        Storage $storage,
        ?int $cacheTimeoutSeconds = null,
        bool $lazyLoad = true,
        ?DefaultLogger $logger = null
    ): File {
        $logger ??= LoggerFactory::getDefaultLogger();
        $pathString = $path instanceof FilePath ? $path->toString() : $path;

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

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

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

        $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) {
                $pathStr = $file->getPathString();
                $logger->debug("Lazy-Loading contents für {$pathStr}");

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

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

                    return '';
                }

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

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

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

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

                    return 0;
                }

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

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

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

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

                    return 0;
                }

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

    /**
     * Erstellt ein Directory-Objekt mit Lazy-Loading für schwere Eigenschaften.
     *
     * @param FilePath|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(
        FilePath|string $path,
        Storage $storage,
        bool $lazyLoad = true,
        ?DefaultLogger $logger = null
    ): Directory {
        $logger ??= LoggerFactory::getDefaultLogger();
        $pathString = $path instanceof FilePath ? $path->toString() : $path;

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

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

            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 {
                $pathStr = $directory->getPathString();
                $logger->debug("Lazy-Loading Directory-Inhalt für {$pathStr}");

                // Verzeichnisinhalt wird erst beim ersten Zugriff auf eine Eigenschaft geladen
                if ($directory->exists()) {
                    $directory->contents = $directory->storage->listDirectory($pathStr);
                } else {
                    $directory->contents = [];
                }
            }
        );

        /** @var Directory $lazyDir */
        return $lazyDir;
    }
}
