<?php

namespace SugarCalendarBookings\Support\Database;

use WP_Error;

/**
 * Class AbstractTableMigration.
 *
 * Helps migrate plugin DB tables.
 *
 * current version - represents the version of the database schema that is currently applied.
 * latest version - represents the version of the database schema that is defined in the code.
 *
 * Most of the time these versions should match. But, if current version is lower than latest version,
 * then migration will be triggered.
 *
 * @since 1.0.0
 */
abstract class AbstractTableMigration {

	/**
	 * Table name.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	const TABLE_NAME = '';

	/**
	 * Latest DB schema version.
	 *
	 * @since 1.0.0
	 *
	 * @var int
	 */
	const LATEST_VERSION = 1;

	/**
	 * Current DB schema version.
	 *
	 * @since 1.0.0
	 *
	 * @var int
	 */
	protected $current_version;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {

		$this->current_version = $this->get_current_version();
	}

	/**
	 * Get table name.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public function get_raw_table_name() {

		return 'scbookings_' . static::TABLE_NAME;
	}

	/**
	 * Get table name with DB prefix.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public function get_table_name() {

		global $wpdb;

		return $wpdb->prefix . $this->get_raw_table_name();
	}

	/**
	 * Get current migration version option name.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	private function get_current_version_option_name() {

		return $this->get_raw_table_name() . '_migration_version';
	}

	/**
	 * Get migration error option name.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	private function get_migration_error_option_name() {

		return $this->get_raw_table_name() . '_migration_error';
	}

	/**
	 * Whether migration is enabled.
	 *
	 * @since 1.0.0
	 *
	 * @return bool
	 */
	public function is_enabled() {

		return true;
	}

	/**
	 * Get current migration version.
	 *
	 * @since 1.0.0
	 *
	 * @return int
	 */
	private function get_current_version() {

		return (int) get_option( $this->get_current_version_option_name(), 0 );
	}

	/**
	 * Check DB version and update to the latest one.
	 *
	 * @since 1.0.0
	 */
	public function validate_db() {

		if ( $this->current_version < static::LATEST_VERSION ) {
			// Include upgrade.php to use dbDelta function.
			require_once ABSPATH . 'wp-admin/includes/upgrade.php';

			// Run migrations sequentially from current version to latest version.
			$next_version = $this->current_version + 1;

			for ( $version = $next_version; $version <= static::LATEST_VERSION; $version++ ) {
				$result = $this->run( $version );

				if ( is_wp_error( $result ) ) {
					$this->set_error(
						sprintf(
						/* translators: %1$d - migration version; %2$s - error message. */
							esc_html__( 'Failed to run migration #%1$d. %2$s.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
							$version,
							rtrim( $result->get_error_message(), '.' )
						)
					);

					break;
				}

				$this->update_db_ver( $version );
			}
		}
	}

	/**
	 * Run migration.
	 *
	 * @since 1.0.0
	 *
	 * @param int $version Migration version to run.
	 *
	 * @return WP_Error|true
	 */
	private function run( $version ) {

		$version = (int) $version;

		if ( ! method_exists( $this, 'migrate_to_' . $version ) ) {
			return new WP_Error( 'migration_not_found', esc_html__( 'Migration not found', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		return $this->{'migrate_to_' . $version}();
	}

	/**
	 * Update DB version in options table.
	 *
	 * @since 1.0.0
	 *
	 * @param int $version Version number.
	 */
	protected function update_db_ver( $version ) {

		$version = (int) $version;

		update_option( $this->get_current_version_option_name(), $version, true );

		// Clear error option after DB version update.
		$this->set_error( '' );
	}

	/**
	 * Set migration error.
	 *
	 * @since 1.0.0
	 *
	 * @param string $message Error message.
	 */
	protected function set_error( $message ) {

		if ( ! empty( $message ) ) {
			update_option( $this->get_migration_error_option_name(), $message, false );
		} else {
			delete_option( $this->get_migration_error_option_name() );
		}
	}
}
