<?php

declare(strict_types=1);

namespace Fennec\Reflection;

/**
 * Découvre les classes existantes sous un répertoire, à partir d'un préfixe de namespace.
 *
 * Même principe que Fennec\Autoload\Autoloader : aucune connaissance en dur du projet
 * qui l'utilise, juste un préfixe de namespace et un répertoire de base. Partagé entre
 * RouteScanner et CommandRegistry pour éviter de dupliquer la logique de scan.
 */
final class DirectoryClassFinder
{
    /**
     * @return list<class-string>
     */
    public function findClasses(string $namespacePrefix, string $baseDirectory): array
    {
        $normalizedPrefix = \rtrim($namespacePrefix, '\\') . '\\';
        $classNames = [];

        foreach ($this->findPhpFiles($baseDirectory) as $filePath) {
            $className = $this->resolveClassName($normalizedPrefix, $baseDirectory, $filePath);

            if (\class_exists($className)) {
                $classNames[] = $className;
            }
        }

        return $classNames;
    }

    /**
     * @return list<string>
     */
    private function findPhpFiles(string $baseDirectory): array
    {
        if (!\is_dir($baseDirectory)) {
            return [];
        }

        $files = [];
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($baseDirectory, \FilesystemIterator::SKIP_DOTS),
        );

        foreach ($iterator as $fileInfo) {
            if ($fileInfo->isFile() && $fileInfo->getExtension() === 'php') {
                $files[] = $fileInfo->getPathname();
            }
        }

        return $files;
    }

    private function resolveClassName(string $namespacePrefix, string $baseDirectory, string $filePath): string
    {
        $relativePath = \substr($filePath, \strlen(\rtrim($baseDirectory, '/\\')) + 1);
        $relativeClassName = \str_replace(['/', '\\', '.php'], ['\\', '\\', ''], $relativePath);

        return $namespacePrefix . $relativeClassName;
    }
}
