<?php
/**
 * Plugin Name: AIEO Autoloader
 * Description: Class autoloader for ai-eshop-optimizer + ai-eshop-optimizer-runtime.
 *              Lets either plugin ship classes in the shared `AIEO_*` namespace without
 *              `require_once` chains. Runtime path wins for conflicts; admin path is the
 *              fallback. Loads nothing, hooks nothing else — pure autoload.
 * Version:     1.0.0
 * Author:      Oxford Metadata
 *
 * Phase 1.5a skeleton — see
 * wp-content/plugins/ai-eshop-optimizer/PHASE-1.5-RUNTIME-ADMIN-SPLIT.md
 *
 * DESIGN
 * ------
 *  - Handles any class whose name starts with `AIEO_` (underscored) OR
 *    `AIEO\\` (namespaced — reserved for future).
 *  - Maps class name → relative path under either plugin's `includes/`.
 *    Sanitize rules: lowercase, underscores stay, prefix `class-` (WP core convention).
 *    Example: `AIEO_DMM_Pricing_Engine` →
 *      runtime:   `ai-eshop-optimizer-runtime/includes/dmm/class-aieo-dmm-pricing-engine.php`
 *      admin:     `ai-eshop-optimizer/includes/dmm/class-aieo-dmm-pricing-engine.php`
 *  - Subfolder hint: classes containing `_DMM_Module_*` or `_DMM_Subtab_*` or ending in
 *    `_Admin` are admin-side. Everything else is runtime-first.
 *  - Zero side-effects during registration. No DB reads. No hooks.
 *
 * Tuning knob: define `AIEO_AUTOLOAD_DEBUG` in wp-config.php for error_log tracing.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! defined( 'AIEO_AUTOLOAD_VERSION' ) ) {
	define( 'AIEO_AUTOLOAD_VERSION', '1.0.0' );
}

spl_autoload_register( 'aieo_autoload_register' );

/**
 * SPL autoloader for AIEO_* classes.
 *
 * @param string $class_name
 * @return void
 */
function aieo_autoload_register( $class_name ) {
	if ( strpos( $class_name, 'AIEO_' ) !== 0 ) {
		return;
	}

	// Turn `AIEO_DMM_Pricing_Engine` → `class-aieo-dmm-pricing-engine.php`.
	$file_name = 'class-' . strtolower( str_replace( '_', '-', $class_name ) ) . '.php';

	// Decide subfolder under `includes/`. Order matters — first match wins.
	$subdirs = array();

	if ( strpos( $class_name, 'AIEO_DMM_Module_' ) === 0 ) {
		$subdirs[] = 'dmm/modules/';
	} elseif ( strpos( $class_name, 'AIEO_DMM_' ) === 0 ) {
		// Modules classes live under modules/ but the base class and engines
		// live under the dmm/ root. Try modules/ first so any AIEO_DMM_*
		// class the admin registers (or the runtime pairs with) is found
		// without maintaining an allowlist; root is the fallback.
		$subdirs[] = 'dmm/modules/';
		$subdirs[] = 'dmm/';
	} elseif ( strpos( $class_name, 'AIEO_Carts' ) === 0 ) {
		// Saved & Abandoned Carts subsystem (AIEO_Carts, AIEO_Carts_Store,
		// AIEO_Carts_Frontend, AIEO_Carts_Capture) lives under includes/carts/.
		// Mapped here so the storefront save/share/resume works runtime-only.
		$subdirs[] = 'carts/';
	} elseif ( strpos( $class_name, 'AIEO_SEO_' ) === 0 ) {
		// SEO engine (AIEO_SEO_Store, AIEO_SEO_Head, AIEO_SEO_Head_Builder,
		// AIEO_SEO_Primary_Term, AIEO_SEO_Sitemap, AIEO_SEO_Importer) lives
		// under includes/seo/. AIEO_SEO_Head is bundled in BOTH plugins so the
		// runtime serves the head with the core blocked (runtime-first wins);
		// the rest are core-only. NB: AIEO_DMM_SEO (the admin module) is matched
		// by the AIEO_DMM_ branch above, not here.
		$subdirs[] = 'seo/';
	} else {
		$subdirs[] = '';
	}

	// Search order: runtime plugin first (its copies are canonical WHEN
	// ACTIVE), core plugin second. CRITICAL: skip the runtime path when the
	// runtime is NOT active. A deactivated-but-not-deleted runtime keeps its
	// files on disk, so without this guard the autoloader would still load the
	// runtime's classes — which reference AIEO_RUNTIME_* constants the
	// deactivated runtime never defined — and shadow the core's fully-working
	// copies, breaking core-only (free-version) operation.
	$plugins = array();
	if ( aieo_autoload_runtime_active() ) {
		$plugins[] = 'ai-eshop-optimizer-runtime';
	}
	$plugins[] = 'ai-eshop-optimizer';

	foreach ( $plugins as $plugin ) {
		foreach ( $subdirs as $sub ) {
			$path = WP_PLUGIN_DIR . '/' . $plugin . '/includes/' . $sub . $file_name;
			if ( file_exists( $path ) ) {
				require_once $path;
				if ( defined( 'AIEO_AUTOLOAD_DEBUG' ) && AIEO_AUTOLOAD_DEBUG ) {
					if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( '[aieo-autoload] ' . $class_name . ' ← ' . $path ); }
				}
				return;
			}
		}
	}

	if ( defined( 'AIEO_AUTOLOAD_DEBUG' ) && AIEO_AUTOLOAD_DEBUG ) {
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( '[aieo-autoload] MISS: ' . $class_name . ' (tried ' . $file_name . ')' ); }
	}
}

/**
 * Is the runtime plugin ACTIVE (not merely present on disk)?
 *
 * Short-circuits on AIEO_RUNTIME_DIR — the constant the runtime defines the
 * instant its main file loads (fast, no DB). Falls back to the `active_plugins`
 * option so the answer is correct even before the runtime's main file has run,
 * which keeps the runtime-first search order load-order-independent. Cached for
 * the request. Returns false WITHOUT caching if called too early to read
 * options, so a later call re-evaluates.
 *
 * @return bool
 */
function aieo_autoload_runtime_active() {
	static $active = null;
	if ( null !== $active ) {
		return $active;
	}
	if ( defined( 'AIEO_RUNTIME_DIR' ) ) {
		return ( $active = true );
	}
	if ( ! function_exists( 'get_option' ) ) {
		return false; // too early to decide; re-evaluate on the next call
	}
	$list   = (array) get_option( 'active_plugins', array() );
	$active = (bool) preg_grep( '#^ai-eshop-optimizer-runtime/#', $list );
	return $active;
}
