<?php

declare(strict_types=1);

namespace PpPhp\Core\Resources;

final class ResourceRegistry
{
    private static ?array $registry = null;

    public static function all(): array
    {
        if (self::$registry === null) {
            self::$registry = self::scan();
        }

        return self::$registry;
    }

    public static function byType(string $type): array
    {
        return self::all()[strtolower($type)] ?? [];
    }

    public static function icons(): array
    {
        return self::byType('icons');
    }

    public static function reset(): void
    {
        self::$registry = null;
    }

    private static function scan(): array
    {
        $baseDir = defined('BASE_PATH')
            ? BASE_PATH . '/resources'
            : dirname(__DIR__, 2) . '/resources';

        return self::scanDirectory($baseDir);
    }

    private static function scanDirectory(string $baseDir): array
    {
        $registry = [];

        if (!is_dir($baseDir)) {
            return $registry;
        }

        foreach (self::directoryNames($baseDir) as $typeDir) {
            if (!self::validNamespaceSegment($typeDir)) {
                continue;
            }

            $typePath = $baseDir . '/' . $typeDir;
            foreach (self::directoryNames($typePath) as $packDir) {
                if (!self::validNamespaceSegment($packDir)) {
                    continue;
                }

                $packPath = $typePath . '/' . $packDir;
                $packFile = $packPath . '/Pack.php';

                if (!is_file($packFile) || !is_readable($packFile)) {
                    continue;
                }

                $className = 'PpPhp\\Resources\\' . $typeDir . '\\' . $packDir . '\\Pack';

                try {
                    require_once $packFile;

                    if (!class_exists($className)) {
                        continue;
                    }

                    $type = strtolower((string)$className::type());
                    $id = (string)$className::id();

                    if ($type === '' || $id === '') {
                        continue;
                    }

                    $registry[$type][$id] = [
                        'id' => $id,
                        'label' => $className::label(),
                        'type' => $type,
                        'version' => $className::version(),
                        'class' => $className,
                        'path' => $packPath,
                        'assets' => $className::assets(),
                        'metadata' => $className::metadata(),
                    ];
                } catch (\Throwable $exception) {
                    error_log(sprintf('Resource pack %s could not be loaded: %s', $packPath, $exception->getMessage()));
                }
            }
        }

        return $registry;
    }

    /**
     * Return only real, local child directories. Files such as .DS_Store,
     * manifest.schema.json, and preset JSON documents must never be treated as
     * pack directories. Symlinks are intentionally ignored so discovery cannot
     * escape a hosting account's open_basedir boundary.
     *
     * @return list<string>
     */
    private static function directoryNames(string $path): array
    {
        $entries = @scandir($path);
        if (!is_array($entries)) {
            return [];
        }

        $directories = [];
        foreach ($entries as $entry) {
            if ($entry === '' || $entry[0] === '.') {
                continue;
            }

            $candidate = $path . '/' . $entry;
            if (is_link($candidate) || !is_dir($candidate)) {
                continue;
            }
            $directories[] = $entry;
        }

        sort($directories, SORT_NATURAL | SORT_FLAG_CASE);
        return $directories;
    }

    private static function validNamespaceSegment(string $value): bool
    {
        return preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/D', $value) === 1;
    }
}
