<?php
declare(strict_types=1);

namespace WPDesk\Migrations;

use Psr\Log\LoggerInterface;
use WPDesk\Migrations\Finder\GlobFinder;
use WPDesk\Migrations\Version\AlphabeticalComparator;
use WPDesk\Migrations\Version\Comparator;
use WPDesk\Migrations\Version\Version;
use WPDesk\Migrations\Version\WpdbMigrationFactory;

class WpdbMigrator implements Migrator {
	private const DB_VERSION = 'omnibus_db_version';

	/** @var \wpdb */
	private $wpdb;

	/** @var MigrationsRepository */
	private $migrations_repository;

	/** @var Comparator */
	private $comparator;

	/** @var LoggerInterface */
	private $logger;

	/** @param string[] $migration_directories */
	public static function from_directories( array $migration_directories ): self {
		global $wpdb;
		$logger = new WpdbLogger();

		return new self(
			$wpdb,
			new FilesystemMigrationsRepository(
				$migration_directories,
				new GlobFinder(),
				new WpdbMigrationFactory(
					$wpdb,
					$logger
				),
				new AlphabeticalComparator()
			),
			new AlphabeticalComparator(),
			$logger
		);
	}

	/** @param class-string<AbstractMigration>[] $migration_class_names */
	public static function from_classes( array $migration_class_names ): self {
		global $wpdb;
		$logger = new WpdbLogger();

		return new self(
			$wpdb,
			new ArrayMigrationsRepository(
				$migration_class_names,
				new WpdbMigrationFactory(
					$wpdb,
					$logger
				),
				new AlphabeticalComparator()
			),
			new AlphabeticalComparator(),
			$logger
		);
	}

	public function __construct(
		\wpdb $wpdb,
		MigrationsRepository $migrations_repository,
		Comparator $comparator,
		LoggerInterface $logger
	) {
		$this->wpdb                  = $wpdb;
		$this->migrations_repository = $migrations_repository;
		$this->comparator            = $comparator;
		$this->logger                = $logger;
	}

	private function get_current_version(): Version {
		return new Version( get_option( self::DB_VERSION, '' ) );
	}

	public function migrate(): void {
		require_once ABSPATH . 'wp-admin/includes/upgrade.php';

		$this->logger->info( 'DB update start' );
		$current_version = $this->get_current_version();

		foreach ( $this->migrations_repository->get_migrations() as $migration ) {
			if ( $this->comparator->compare( $migration->get_version(), $this->get_current_version() ) ) {
				$this->logger->info( sprintf( 'DB update %s:%s', $current_version, $migration->get_version() ) );
				try {
					$migration->get_migration()->up();
					$this->logger->info( sprintf( 'DB update %s:%s -> ', $current_version, $migration->get_version() ) . 'OK' );
					update_option( self::DB_VERSION, (string) $migration->get_version(), true );
				} catch ( \Throwable $e ) {
					$error_msg = sprintf( 'Error while upgrading a database: %s', $this->wpdb->last_error );
					$this->logger->error( $error_msg );
					trigger_error( $error_msg, E_USER_WARNING );
				}
			}
		}

		$this->logger->info( 'DB update finished' );
	}

}