<?php

declare(strict_types=1);

namespace SeoScope\Database;

use PDO;

final class MigrationRunner
{
    public function __construct(
        private PDO $pdo,
        private string $tablePrefix
    ) {
    }

    public function ensureMigrationsTable(): void
    {
        $table = $this->escapeIdentifier($this->tablePrefix . 'migrations');
        $this->pdo->exec(
            "CREATE TABLE IF NOT EXISTS {$table} (
                `id` int unsigned NOT NULL AUTO_INCREMENT,
                `migration` varchar(255) NOT NULL,
                `applied_at` datetime NOT NULL,
                PRIMARY KEY (`id`),
                UNIQUE KEY `migration_unique` (`migration`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
        );
    }

    /**
     * @throws \RuntimeException
     */
    public function runAll(string $migrationsDir): void
    {
        if (!is_dir($migrationsDir)) {
            return;
        }
        $this->ensureMigrationsTable();

        $files = glob($migrationsDir . '/*.php') ?: [];
        sort($files, SORT_STRING);

        foreach ($files as $file) {
            $name = basename($file);
            if ($this->isApplied($name)) {
                continue;
            }
            $callback = require $file;
            if (!is_callable($callback)) {
                throw new \RuntimeException('Migration must return a callable: ' . $name);
            }
            $callback($this->pdo, $this->tablePrefix);
            $this->recordApplied($name);
        }
    }

    private function isApplied(string $name): bool
    {
        $table = $this->escapeIdentifier($this->tablePrefix . 'migrations');
        $stmt = $this->pdo->prepare("SELECT 1 FROM {$table} WHERE migration = ? LIMIT 1");
        $stmt->execute([$name]);
        return (bool) $stmt->fetchColumn();
    }

    private function recordApplied(string $name): void
    {
        $table = $this->escapeIdentifier($this->tablePrefix . 'migrations');
        $stmt = $this->pdo->prepare(
            "INSERT INTO {$table} (migration, applied_at) VALUES (?, ?)"
        );
        $stmt->execute([$name, date('Y-m-d H:i:s')]);
    }

    /**
     * Prefix is trusted (config); still only allow alphanumerics + underscore for safety.
     */
    private function escapeIdentifier(string $ident): string
    {
        if (!preg_match('/^[a-zA-Z0-9_]+$/', $ident)) {
            throw new \InvalidArgumentException('Invalid table identifier prefix');
        }
        return '`' . str_replace('`', '``', $ident) . '`';
    }
}
