<?php
declare(strict_types=1);

namespace App\Framework\Discovery;

use FilesystemIterator;
use Generator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RegexIterator;
use SplFileInfo;

readonly class FileScanner implements FileScannerInterface
{
    /**
     * Scannt ein Verzeichnis nach PHP-Dateien
     *
     * @param string $directory Zu scannendes Verzeichnis
     * @return Generator<SplFileInfo> Generator, der PHP-Dateien zurückgibt
     */
    public function scanDirectory(string $directory): Generator
    {
        $rii = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS)
        );

        foreach ($rii as $file) {
            if (!$file->isFile() || $file->getExtension() !== 'php') {
                continue;
            }

            yield $file;
        }
    }

    /**
     * Findet alle Dateien mit bestimmtem Muster in einem Verzeichnis
     * @return array<SplFileInfo>
     */
    public function findFiles(string $directory, string $pattern = '*.php'): array
    {
        // Konvertiere das Glob-Muster in einen regulären Ausdruck
        $regexPattern = $this->globToRegex($pattern);

        return iterator_to_array(
            new RegexIterator(
                new RecursiveIteratorIterator(
                    new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS)
                ),
                $regexPattern
            ),
            false
        );
    }

    /**
     * Ermittelt den letzten Änderungszeitpunkt aller Dateien eines bestimmten Musters
     */
    public function getLatestMTime(string $directory, string $pattern = '*.php'): int
    {
        $maxMTime = 0;
        $files = $this->findFiles($directory, $pattern);

        foreach ($files as $file) {
            $mtime = $file->getMTime();
            if ($mtime > $maxMTime) {
                $maxMTime = $mtime;
            }
        }

        return $maxMTime;
    }

    /**
     * Findet Dateien, die seit einem bestimmten Zeitpunkt geändert wurden
     * @return array<SplFileInfo>
     */
    public function findChangedFiles(string $directory, int $timestamp, string $pattern = '*.php'): array
    {
        $files = $this->findFiles($directory, $pattern);
        $changedFiles = [];

        foreach ($files as $file) {
            if ($file->getMTime() > $timestamp) {
                $changedFiles[] = $file;
            }
        }

        return $changedFiles;
    }

    /**
     * Konvertiert ein Glob-Muster in einen regulären Ausdruck
     */
    private function globToRegex(string $globPattern): string
    {
        $regex = preg_quote($globPattern, '/');

        // Ersetze Glob-Platzhalter durch Regex-Äquivalente
        $regex = str_replace(
            ['\*', '\?', '\[', '\]'],
            ['.*', '.', '[', ']'],
            $regex
        );

        return '/^' . $regex . '$/i';
    }
}
