<?php
/**
 * Abstract Migration Runner
 *
 * Provides the shared boilerplate for all module migration runners.
 * Modules extend this class and declare their VERSION_OPTION constant
 * and get_migrations() method — nothing else is needed.
 *
 * @package AmbassadorHub\Migrations
 */

namespace AmbassadorHub\Migrations;

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

/**
 * Base class for module-specific migration runners.
 *
 * ### Subclass usage
 *
 * ```php
 * final class MigrationRunner extends AbstractMigrationRunner {
 *
 *     protected const VERSION_OPTION = 'ambh_my_module_db_version';
 *
 *     protected static function get_migrations(): array {
 *         return [
 *             1 => Migrations\CreateTables::class,
 *             2 => Migrations\AddSomeColumn::class,
 *         ];
 *     }
 * }
 * ```
 *
 * Call `MigrationRunner::init()` once from `MyModule::init()`.
 */
abstract class AbstractMigrationRunner {

	/**
	 * WordPress option key for this module's current DB schema version.
	 *
	 * Subclasses MUST override this constant.
	 * Example: 'ambh_donations_db_version'
	 */
	protected const VERSION_OPTION = '';

	/**
	 * Return the ordered map of integer version → migration class name.
	 *
	 * Keys must be sequential integers starting at 1. Each value must be
	 * a fully-qualified class name with a `public static function up(): void` method.
	 *
	 * @return array<int, class-string>
	 */
	abstract protected static function get_migrations(): array;

	/**
	 * Register this runner with the platform migration hook.
	 *
	 * Call once from YourModule::init().
	 *
	 * @return void
	 */
	final public static function init(): void {
		add_action( 'ambh_run_migrations', array( static::class, 'run' ) );
	}

	/**
	 * Run all pending migrations in order.
	 *
	 * Safe to call multiple times — already-applied versions are skipped.
	 * Advances the stored version after each individual step so a partial
	 * run does not re-apply completed migrations on the next attempt.
	 *
	 * @return void
	 */
	final public static function run(): void {
		$current = (int) get_option( static::VERSION_OPTION, 0 );

		foreach ( static::get_migrations() as $version => $migration_class ) {
			if ( $version > $current ) {
				if ( ! class_exists( $migration_class ) ) {
					// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
					error_log( "AmbassadorHub: migration class {$migration_class} not found, skipping." );
					continue;
				}
				$migration_class::up();
				update_option( static::VERSION_OPTION, $version );

				/**
				 * Fires after a single migration step is applied.
				 *
				 * @since 0.1.0
				 *
				 * @param string $version_option  The module's version option key.
				 * @param int    $version         The migration version just applied.
				 * @param string $migration_class Fully-qualified migration class name.
				 */
				do_action( 'ambh_migration_applied', static::VERSION_OPTION, $version, $migration_class );
			}
		}
	}
}
