<?php

declare(strict_types=1);

namespace CliqThemes\StoreLocator\Extensions;

defined('ABSPATH') || exit;

/**
 * Finds and registers the extensions this build contains.
 *
 * Discovery is by directory listing, so the loader names no extension. A build
 * that omits one omits its file, and this finds one fewer.
 */
final class ExtensionLoader
{
    private static bool $booted = false;

    /**
     * Register every extension found beside this loader.
     *
     * Safe to call more than once: activation needs the extensions registered
     * before the schema runs, and a normal request needs them on `init`, so both
     * paths call this and only the first does anything.
     */
    public static function boot(): void
    {
        if (self::$booted) {
            return;
        }
        self::$booted = true;

        foreach (self::discover() as $extension) {
            $extension->register();
        }
    }

    /**
     * @return array<int, Extension>
     */
    private static function discover(): array
    {
        $files = glob(__DIR__ . '/*.php');
        if ($files === false) {
            return [];
        }

        $extensions = [];
        foreach ($files as $file) {
            $class = __NAMESPACE__ . '\\' . basename($file, '.php');
            // Skip this loader and the interface itself, and anything that turns
            // out not to be an extension — a stray file must not break boot.
            if (! class_exists($class) || ! is_subclass_of($class, Extension::class)) {
                continue;
            }
            $extensions[] = new $class();
        }

        // Sorted by class name so registration order is the same on every host,
        // rather than whatever order the filesystem returns.
        usort($extensions, static fn (Extension $a, Extension $b): int => strcmp($a::class, $b::class));

        return $extensions;
    }
}
