<?php

declare(strict_types=1);

namespace PpPhp\Core;

class Autoloader
{
    /**
     * Array of namespace prefix => base directory pairs.
     *
     * @var array<string, string>
     */
    private array $prefixes = [];

    /**
     * Register the autoloader with SPL.
     */
    public function register(): void
    {
        spl_autoload_register([$this, 'loadClass'], true, true);
    }

    /**
     * Add a namespace prefix to directory mapping.
     *
     * @param string $prefix The namespace prefix (e.g., "PpPhp\Core\").
     * @param string $baseDir The base directory (e.g., "/path/to/core/").
     */
    public function addNamespace(string $prefix, string $baseDir): void
    {
        // Normalize: trim trailing backslashes, ensure trailing slash on path
        $prefix = trim($prefix, '\\') . '\\';
        $baseDir = rtrim($baseDir, \DIRECTORY_SEPARATOR) . \DIRECTORY_SEPARATOR;
        $this->prefixes[$prefix] = $baseDir;
    }

    /**
     * Autoload callback.
     *
     * @param string $class Fully qualified class name.
     */
    public function loadClass(string $class): void
    {
        $prefix = $class;

        // Work backwards through the namespace to find a mapped prefix
        while (false !== $pos = strrpos($prefix, '\\')) {
            $prefix = substr($class, 0, $pos + 1);
            $relativeClass = substr($class, $pos + 1);

            if (isset($this->prefixes[$prefix])) {
                $file = $this->prefixes[$prefix]
                      . str_replace('\\', \DIRECTORY_SEPARATOR, $relativeClass)
                      . '.php';

                if ($this->requireFile($file)) {
                    return;
                }
            }

            $prefix = rtrim($prefix, '\\');
        }
    }

    /**
     * Include the file if it exists.
     */
    protected function requireFile(string $file): bool
    {
        if (file_exists($file)) {
            require_once $file;
            return true;
        }
        return false;
    }
}