<?php
declare(strict_types=1);

namespace Celena\Core\Database;

use Celena\Core\Logger;

/**
 * Запускает миграции из указанной директории по алфавиту.
 * Каждая миграция — PHP-файл, возвращающий замыкание function (Schema $s, Connection $c): void.
 * Применённые миграции трекаются в таблице celena_migrations.
 */
final class MigrationRunner
{
    public function __construct(
        private readonly Connection $conn,
        private readonly Logger $logger,
    ) {
    }

    public function run(string $dir): array
    {
        $schema = new Schema($this->conn);
        $this->ensureTable($schema);
        $applied = $this->loadApplied();
        $files = glob($dir . '/*.php') ?: [];
        sort($files);
        $ran = [];
        foreach ($files as $file) {
            $name = basename($file, '.php');
            if (in_array($name, $applied, true)) {
                continue;
            }
            $migration = require $file;
            if (!$migration instanceof \Closure) {
                throw new \RuntimeException("Migration $name must return a Closure(Schema, Connection).");
            }
            $this->conn->transaction(function () use ($migration, $schema, $name) {
                $migration($schema, $this->conn);
                $this->conn->builder('celena_migrations')->insert([
                    'name' => $name,
                    'applied_at' => date('Y-m-d H:i:s'),
                ]);
            });
            $this->logger->info("Migration applied: $name");
            $ran[] = $name;
        }
        return $ran;
    }

    private function ensureTable(Schema $schema): void
    {
        if ($schema->hasTable('celena_migrations')) {
            return;
        }
        $schema->create('celena_migrations', function (Blueprint $t) {
            $t->id();
            $t->string('name', 191);
            $t->datetime('applied_at');
            $t->unique('name');
        });
    }

    /** @return string[] */
    private function loadApplied(): array
    {
        $rows = $this->conn->builder('celena_migrations')->select('name')->get();
        return array_map(fn ($r) => (string) $r['name'], $rows);
    }
}
