<?php
declare(strict_types=1);

namespace App\Framework\Core;

/**
 * Optimierter PathProvider mit Caching
 */
final class PathProvider
{
    private string $basePath;
    private array $resolvedPaths = [];
    private ?array $namespacePaths = null {
        get {
            if ($this->namespacePaths !== null) {
                return $this->namespacePaths;
            }

            // Aus composer.json auslesen
            $composerJsonPath = $this->basePath . '/composer.json';
            if (!file_exists($composerJsonPath)) {
                return $this->namespacePaths = [];
            }

            $composerJson = json_decode(file_get_contents($composerJsonPath), true);
            $this->namespacePaths = [];

            if (isset($composerJson['autoload']['psr-4'])) {
                foreach ($composerJson['autoload']['psr-4'] as $namespace => $path) {
                    $this->namespacePaths[$namespace] = $this->resolvePath($path);
                }
            }

            return $this->namespacePaths;
        }
    }

    public function __construct(string $basePath)
    {
        $this->basePath = rtrim($basePath, '/');
    }

    /**
     * Gibt den Basispfad des Projekts zurück
     */
    public function getBasePath(): string
    {
        return $this->basePath;
    }

    /**
     * Löst einen relativen Pfad zum Basispfad auf
     */
    public function resolvePath(string $relativePath): string
    {
        if (isset($this->resolvedPaths[$relativePath])) {
            return $this->resolvedPaths[$relativePath];
        }

        $path = $this->basePath . '/' . ltrim($relativePath, '/');
        $this->resolvedPaths[$relativePath] = $path;

        return $path;
    }

    /**
     * Prüft, ob ein Pfad im Projektverzeichnis existiert
     */
    public function pathExists(string $relativePath): bool
    {
        return file_exists($this->resolvePath($relativePath));
    }

    /**
     * Konvertiert einen Namespace in einen relativen Pfad
     */
    public function namespaceToPath(string $namespace): ?string
    {
        $namespacePaths = $this->namespacePaths;

        // Finde den passenden Namespace-Präfix
        foreach ($namespacePaths as $prefix => $path) {
            if (str_starts_with($namespace, $prefix)) {
                $relativeNamespace = substr($namespace, strlen($prefix));
                $relativePath = str_replace('\\', '/', $relativeNamespace);
                return rtrim($path, '/') . '/' . $relativePath . '.php';
            }
        }

        return null;
    }

    /**
     * Konvertiert einen Pfad in einen Namespace
     */
    public function pathToNamespace(string $path): ?string
    {
        $namespacePaths = $this->namespacePaths;
        $absolutePath = realpath($path);

        if (!$absolutePath) {
            return null;
        }

        // Finde den passenden Pfad-Präfix
        foreach ($namespacePaths as $namespace => $nsPath) {
            $realNsPath = realpath($nsPath);

            if ($realNsPath && str_starts_with($absolutePath, $realNsPath)) {
                $relativePath = substr($absolutePath, strlen($realNsPath));
                $relativePath = rtrim(str_replace('.php', '', $relativePath), '/');
                $relativeNamespace = str_replace('/', '\\', $relativePath);
                return $namespace . ltrim($relativeNamespace, '\\');
            }
        }

        return null;
    }

    /**
     * Gibt den Pfad zum Cache-Verzeichnis zurück
     */
    public function getCachePath(string $path = ''): string
    {
        return $this->basePath . '/cache/' . ltrim($path, '/');
    }

    /**
     * Gibt den Pfad zum Quellverzeichnis zurück
     */
    public function getSourcePath(string $path = ''): string
    {
        return $this->basePath . '/src/' . ltrim($path, '/');
    }
}
