<?php
/**
 * Sentient Forms Autoloader.
 * Dynamically loads class files using a pre-generated class map.
 * This approach is efficient and reliable, as the map is generated
 * during a build step, ensuring all class paths are accurate.
 *
 * @package SentientForms
 * @since   0.1.0
 */

if ( !defined( 'ABSPATH' ) )
{
    exit; // Exit if accessed directly.
}

/**
 * Sentient_Forms_Autoloader Class.
 * Responsible for autoloading plugin classes via a class map.
 * The class map is expected to be generated by a build script
 * (e.g., build/generate-classmap.php) and located at 'includes/class-map.php'.
 *
 * @since 0.1.0
 */
final class Sentient_Forms_Autoloader
{

    /**
     * Stores the loaded class map.
     * Loaded once and stored statically to avoid redundant file operations.
     *
     * @var array<string, string>|null
     */
    private static ?array $class_map = null;

    /**
     * Flag to indicate if the class map has been attempted to load.
     * Prevents multiple attempts if the map file is missing.
     *
     * @var bool
     */
    private static bool $map_load_attempted = false;

    /**
     * Path to the class map file.
     * The paths within class-map.php are expected to be resolvable from the location
     * where class-map.php is included (e.g., using __DIR__ within class-map.php itself).
     *
     * @var string
     */
    private string $class_map_file_path;

    /**
     * Constructor.
     * Registers the autoloader and triggers loading of the class map.
     *
     * @since 0.1.0
     */
    public function __construct()
    {
        $this->class_map_file_path = __DIR__ . '/class-map.php';

        // Load the class map if it hasn't been loaded yet.
        if ( !self::$map_load_attempted )
        {
            $this->load_class_map();
        }

        spl_autoload_register( [ $this, 'autoload' ] );
    }

    /**
     * Loads the class map from the generated file.
     * This method is called once.
     *
     * @since 0.1.0
     */
    private function load_class_map(): void
    {
        self::$map_load_attempted = true; // Mark that loading has been attempted.

        if ( file_exists( $this->class_map_file_path ) )
        {
            $map_data = require $this->class_map_file_path;
            if ( is_array( $map_data ) )
            {
                self::$class_map = $map_data;

                sentient_forms_debug_log(
                    'Sentient Forms autoloader class map loaded.',
                    [
                        'class_count' => count( self::$class_map ),
                    ]
                );
            }
            else
            {
                self::$class_map = []; // Initialize as empty array if map is invalid.
                sentient_forms_debug_log(
                    'Sentient Forms autoloader class map did not return an array.',
                    [
                        'class_map_path' => $this->class_map_file_path,
                    ]
                );
            }
        }
        else
        {
            self::$class_map = []; // Initialize as empty array if map file doesn't exist.
            sentient_forms_debug_log(
                'Sentient Forms autoloader class map file was not found.',
                [
                    'class_map_path' => $this->class_map_file_path,
                ]
            );
        }
    }

    /**
     * Autoload callback.
     * Includes the class file if it's found in the class map.
     *
     * @param string $class_name The fully qualified name of the class, interface, enum, or trait to load.
     *
     * @since 0.1.0
     */
    public function autoload( string $class_name ): void
    {
        if ( 'Sentient_Forms_Abstract_Base_Controller' === $class_name )
        {
            require_once __DIR__ . '/rest-api/controllers/abstract-class-base-controller.php';
            return;
        }

        if ( 'Sentient_Forms_Permission_Utils_Trait' === $class_name )
        {
            require_once __DIR__ . '/rest-api/permissions/trait-permission-utils.php';
            return;
        }

        // Only attempt to autoload plugin symbols.
        $supported_prefixes = [ 'Sentient_Forms_' ];
        $has_supported_prefix = false;
        foreach ( $supported_prefixes as $prefix )
        {
            if ( str_starts_with( $class_name, $prefix ) )
            {
                $has_supported_prefix = true;
                break;
            }
        }

        if ( !$has_supported_prefix )
        {
            return;
        }

        // If the class map is empty (e.g., file not found or invalid), do nothing further.
        // An error would have been logged during load_class_map().
        if ( empty( self::$class_map ) && self::$map_load_attempted )
        {
            return;
        }

        if ( !isset( self::$class_map[ $class_name ] ) )
        {
            return;
        }

        $file_path = self::$class_map[ $class_name ];

        // The paths in class-map.php are generated using __DIR__ (of class-map.php)
        // so they should be absolute or correctly relative.
        if ( !file_exists( $file_path ) )
        {
            sentient_forms_debug_log(
                'Sentient Forms autoloader class map references a missing file.',
                [
                    'class_name' => $class_name,
                    'file_path'  => $file_path,
                ]
            );
            return;
        }

        require_once $file_path;
    }
}

new Sentient_Forms_Autoloader(); // this should be done once when the plugin loads.
