<?php

namespace Tests\Support;

use Config\Database;
use Throwable;

class DBReset
{
    private const DB_GROUP = 'tests';

    protected static array $excludeTables = ['migrations', 'school_years'];
    private static bool $migrated = false;

    public static function resetDatabase(): void
    {
        $config = config('Database');
        $dbConfig = $config->{self::DB_GROUP} ?? [];
        self::assertSafeTestDatabase($config->default ?? [], $dbConfig);

        try {
            $db = Database::connect(self::DB_GROUP);
            $db->initialize();
        } catch (Throwable $exception) {
            throw new \RuntimeException(
                sprintf(
                    'Test database connection failed: group=%s host=%s port=%s database=%s',
                    self::DB_GROUP,
                    $dbConfig['hostname'] ?? '',
                    $dbConfig['port'] ?? '',
                    $dbConfig['database'] ?? ''
                ),
                0,
                $exception
            );
        }

        self::migrateTestDatabase($db);

        $db->disableForeignKeyChecks();

        $tables = $db->resetDataCache()->listTables();

        foreach ($tables as $table) {
            if (!in_array($table, self::$excludeTables, true) && $db->tableExists($table, false)) {
                $db->query('TRUNCATE TABLE ' . $db->escapeIdentifiers($table));
            }
        }

        $db->enableForeignKeyChecks();
    }

    private static function migrateTestDatabase($db): void
    {
        if (self::$migrated) {
            return;
        }

        $runner = \Config\Services::migrations(config('Migrations'), $db, false);
        $runner->setNamespace('App')->latest();

        self::$migrated = true;
    }

    private static function assertSafeTestDatabase(array $defaultConfig, array $testConfig): void
    {
        $defaultDatabase = (string) ($defaultConfig['database'] ?? '');
        $testDatabase = (string) ($testConfig['database'] ?? '');

        if ($testDatabase === '') {
            throw new \RuntimeException('Refusing to reset database: tests database name is empty.');
        }

        if ($defaultDatabase !== '' && $testDatabase === $defaultDatabase) {
            throw new \RuntimeException(sprintf(
                'Refusing to reset database "%s": tests database matches the default application database.',
                $testDatabase
            ));
        }

        if (! preg_match('/(^test_|_test$|^tests$|^alrahma_test$)/i', $testDatabase)) {
            throw new \RuntimeException(sprintf(
                'Refusing to reset database "%s": test database names must clearly identify a test database.',
                $testDatabase
            ));
        }
    }
}
