<?php
/**
 * @package Hafenstudios\Core
 */

namespace Hafenstudios\Core\Db;

defined( 'ABSPATH' ) || exit;

/**
 * Migrations-Framework fuer Custom Tables.
 *
 * Jedes Plugin registriert versionierte Migrationen. Der Migrator fuehrt die noch
 * ausstehenden in Reihenfolge aus und merkt sich die erreichte Schemaversion in
 * wp_options (`hs_{namespace}_db_version`). So bleiben Tabellen ueber Updates
 * hinweg konsistent (BAUPLAN Abschnitt 25, technisches Risiko Custom-Table-Migration).
 */
final class Migrator {

	private string $namespace;

	private string $target_version;

	private string $version_option;

	/**
	 * Registrierte Migrationen: version => callable.
	 *
	 * @var array<string, callable>
	 */
	private array $migrations = array();

	/**
	 * @param string $namespace      Plugin-Slug.
	 * @param string $target_version Ziel-Schemaversion.
	 */
	public function __construct( string $namespace, string $target_version ) {
		$this->namespace      = sanitize_key( $namespace );
		$this->target_version = $target_version;
		$this->version_option = 'hs_' . $this->namespace . '_db_version';
	}

	/**
	 * Registriert eine Migration.
	 *
	 * @param string   $version  Schemaversion, ab der diese Migration gilt.
	 * @param callable $callback Erhaelt ($wpdb, $charset_collate).
	 * @return Migrator
	 */
	public function register( string $version, callable $callback ): Migrator {
		$this->migrations[ $version ] = $callback;
		return $this;
	}

	/**
	 * Fuehrt alle ausstehenden Migrationen aus.
	 *
	 * @return void
	 */
	public function run(): void {
		global $wpdb;

		$current = (string) get_option( $this->version_option, '0' );
		if ( version_compare( $current, $this->target_version, '>=' ) ) {
			return;
		}

		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
		$charset_collate = $wpdb->get_charset_collate();

		uksort( $this->migrations, 'version_compare' );

		foreach ( $this->migrations as $version => $callback ) {
			if ( version_compare( $current, $version, '<' ) ) {
				call_user_func( $callback, $wpdb, $charset_collate );
				update_option( $this->version_option, $version );
			}
		}

		update_option( $this->version_option, $this->target_version );
	}

	/**
	 * Setzt die gespeicherte Schemaversion zurueck (fuer saubere Deinstallation).
	 *
	 * @return void
	 */
	public function reset(): void {
		delete_option( $this->version_option );
	}
}
