<?php

namespace CliqThemes\ORM\Schema;

use CliqThemes\ORM\Database\Connection;

/**
 * Runs ordered, idempotent schema migrations and tracks the applied version in
 * a WordPress option so tables can evolve across plugin releases without data
 * loss. This replaces ad-hoc "drop and recreate" upgrade logic.
 */
class Migrator
{
    protected SchemaManager $schema;

    protected Connection $connection;

    protected string $optionKey;

    /**
     * @param string $optionKey WordPress option name used to persist the version.
     */
    public function __construct(string $optionKey, ?SchemaManager $schema = null, ?Connection $connection = null)
    {
        $this->optionKey = $optionKey;
        $this->connection = $connection ?? new Connection();
        $this->schema = $schema ?? new SchemaManager($this->connection);
    }

    /**
     * The schema builder migrations operate against.
     */
    public function schema(): SchemaManager
    {
        return $this->schema;
    }

    /**
     * The currently applied schema version (0 when nothing has run yet).
     */
    public function currentVersion(): int
    {
        if (!function_exists('get_option')) {
            return 0;
        }

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

    /**
     * Persist the applied schema version.
     */
    public function setVersion(int $version): void
    {
        if (function_exists('update_option')) {
            update_option($this->optionKey, $version);
        }
    }

    /**
     * Apply every migration whose version is greater than the stored version,
     * in ascending order, recording the version after each step so an
     * interrupted run resumes from where it stopped.
     *
     * Each callback receives the {@see SchemaManager} instance.
     *
     * @param array<int, callable(SchemaManager): void> $migrations
     *
     * @return int The version after running (unchanged when already current).
     */
    public function migrate(array $migrations): int
    {
        ksort($migrations, SORT_NUMERIC);

        $current = $this->currentVersion();

        foreach ($migrations as $version => $callback) {
            $version = (int) $version;

            if ($version <= $current) {
                continue;
            }

            $callback($this->schema);
            $this->setVersion($version);
            $current = $version;
        }

        return $current;
    }

    /**
     * Determine if a column exists on the given (un-prefixed) table.
     */
    public function hasColumn(string $table, string $column): bool
    {
        $qualified = $this->connection->table($table);

        $result = $this->connection->select(
            sprintf('SHOW COLUMNS FROM `%s` LIKE %%s', $qualified),
            [$column]
        );

        return !empty($result);
    }

    /**
     * Determine if a table exists.
     */
    public function hasTable(string $table): bool
    {
        return $this->schema->tableExists($table);
    }
}
