<?php

declare(strict_types=1);

namespace ShieldX;

defined('ABSPATH') || exit;

final class Autoloader
{
    /** @var string */
    private static $baseDir = '';

    /** @var string */
    private static $baseReal = '';

    /** @var array<string,string> */
    private static $resolved = [];

    public static function register(string $baseDir): void
    {
        self::$baseDir = rtrim(str_replace('\\', '/', $baseDir), '/') . '/';
        $baseReal = realpath(self::$baseDir);
        self::$baseReal = is_string($baseReal) ? rtrim(str_replace('\\', '/', $baseReal), '/') . '/' : '';
        // A later registration may point at a different plugin base directory.
        // Never reuse paths that were containment-checked against the old root.
        self::$resolved = [];
        spl_autoload_register([self::class, 'load']);
    }

    public static function load(string $class): void
    {
        $prefix = __NAMESPACE__ . '\\';
        $prefixLength = strlen($prefix);

        if (strncmp($prefix, $class, $prefixLength) !== 0) {
            return;
        }

        $relative = substr($class, $prefixLength);
        $relative = str_replace('\\', '/', $relative);
        if ($relative === '' || strpos($relative, "\0") !== false) {
            return;
        }

        if (isset(self::$resolved[$class])) {
            require_once self::$resolved[$class];

            return;
        }

        $autoloadPath = self::$baseDir . $relative . '.php';
        $autoloadReal = realpath($autoloadPath);
        if (self::$baseReal === '' || !is_string($autoloadReal) || !is_readable($autoloadReal)) {
            return;
        }

        $target = str_replace('\\', '/', $autoloadReal);
        // realpath() resolves traversal and links; containment is therefore the
        // authoritative path validation rather than a class-name heuristic.
        if (strpos($target, self::$baseReal) !== 0) {
            return;
        }

        self::$resolved[$class] = $autoloadReal;
        require_once $autoloadReal;
    }
}
