<?php

namespace RtmBusiness\PostSync;

use Exception;
use WordpressModels\Traits\SingletonTrait;

class Migrator
{
    use SingletonTrait;

    public function __construct()
    {
        if (!get_site_option('postsync_migration_list')) {
            update_site_option('postsync_migration_list', []);
        }
        if (!get_site_option('postsync_plugin_version')) {
            update_site_option('postsync_plugin_version', '');
        }
    }

    /**
     * Gets all the available migration classes sorted by priority
     * @param $onlyNonMigrated bool show only classes that have not been migrated yet
     * @param int|null $priority show only classes with given priority, null for all
     * @return array list of migration classes
     */
    public function getMigrations(bool $onlyNonMigrated = false, ?int $priority = null): array
    {
        // Get all the files in the Migrations folder
        $migrationFiles = array_diff(scandir(__DIR__ . '/Migrations/'), ['..', '.']);
        $classes = [];

        foreach ($migrationFiles as $file) {
            $file = str_replace('.php', '', $file);
            // Check if the class exists, if so add it to the migration array
            if (class_exists('RtmBusiness\PostSync\Migrations\\' . $file)) {
                $classes[] = 'RtmBusiness\PostSync\Migrations\\' . $file;
            }
        }

        $migrations = [];
        foreach ($classes as $migrationClass) {
            // Check if the migration class implements the migration interface
            if (class_implements($migrationClass, 'RtmBusiness\PostSync\Migrations\MigrationInterface')) {
                $migrations[] = new $migrationClass();
            }
        }

        // Sort array by priority
        usort($migrations, function ($a, $b) {
            return ($a->getPriority() < $b->getPriority()) ? -1 : 1;
        });

        if ($onlyNonMigrated) {
            $migrations = array_filter($migrations, function ($migration) {
                return !$migration->isMigrated();
            });
        }

        if ($priority !== null) {
            $migrations = array_filter($migrations, function ($migration) use ($priority) {
                return $migration->getPriority() == $priority;
            });
        }

        // Return migration classes that are not migrated yet
        return $migrations;
    }

    /**
     * Runs migration method inside every or given migration class
     * @param $migrations array of migration classes
     * @return void
     * @throws Exception
     */
    public function run(array $migrations = []): void
    {
        $migrations = (empty($migrations)) ? $this->getMigrations() : $migrations;
        $migrationList = [];
        if (!empty($migrations)) {
            foreach ($migrations as $migration) {
                // Check if the migration class is already migrated, if not migrate
                if (!$migration->isMigrated()) {
                    try {
                        $migration->migrate();
                        $migrationList[get_class($migration)] = [
                            "name" => get_class($migration),
                            "success" => true,
                            "priority" => $migration->getPriority(),
                            "timestamp" => time(),
                        ];
                        Logger::debug(
                            'Migration successful',
                            "Successful migration of class " . get_class($migration),
                            'Migrator::run',
                        );
                    } catch (Exception $e) {
                        $migrationList[get_class($migration)] = [
                            "name" => get_class($migration),
                            "success" => false,
                            "priority" => $migration->getPriority(),
                            "timestamp" => time(),
                            "error" => $e->getMessage()
                        ];
                        Logger::error(
                            'Migration error: An exception has been triggered',
                            "An error occurred when migrating " . get_class($migration) . ": {$e->getMessage()}",
                            'Migrator::run',
                            'Plugin',
                            [
                                "class" => get_class($migration),
                                "exception" => $e->getMessage(),
                                "priority" => $migration->getPriority()
                            ]
                        );
                    }
                }
            }
        }
        $currentMigrations = array_merge(get_site_option('postsync_migration_list'), $migrationList);
        // Update plugin version in options
        update_site_option('postsync_plugin_version', Core::PLUGIN_VERSION);
        update_site_option('postsync_migration_list', $currentMigrations);
    }

    /**
     * Runs rollback method inside every or given migration class
     * @param $migrations array of migration classes
     * @return void
     * @throws Exception
     */
    public function rollback(array $migrations = []): void
    {
        $migrations = (empty($migrations)) ? $this->getMigrations() : $migrations;
        $migrationList = [];
        if (!empty($migrations)) {
            foreach ($migrations as $migration) {
                // Check if the migration class is already migrated, if so you can roll back
                if ($migration->isMigrated()) {
                    try {
                        $migration->rollback();
                        $migrationList[get_class($migration)] = [
                            "name" => get_class($migration),
                            "success" => false,
                            "priority" => $migration->getPriority(),
                            "timestamp" => time(),
                        ];
                        Logger::debug(
                            'Rollback successful',
                            "Successful rollback of class " . get_class($migration),
                            'Migrator::rollback',
                        );
                    } catch (Exception $e) {
                        $migrationList[get_class($migration)] = [
                            "name" => get_class($migration),
                            "success" => false,
                            "priority" => $migration->getPriority(),
                            "timestamp" => time(),
                            "error" => $e->getMessage()
                        ];
                        Logger::error(
                            'Migration rollback error: An exception has been triggered',
                            "An error occurred when rolling back " . get_class($migration) . ": {$e->getMessage()}",
                            'Migrator::rollback',
                            'Plugin',
                            [
                                "class" => get_class($migration),
                                "exception" => $e->getMessage(),
                                "priority" => $migration->getPriority()
                            ]
                        );
                    }
                }
            }
        }

        $currentMigrations = array_merge(get_site_option('postsync_migration_list'), $migrationList);
        update_site_option('postsync_migration_list', $currentMigrations);
    }
}
