<?php

declare(strict_types=1);

namespace App\Services\Install;

use Illuminate\Support\Facades\DB;
use RuntimeException;

/**
 * 在「重新安装 / 重置」模式下，按依赖安全顺序移除本宿主安装相关的数据库对象（仅限当前连接库）。
 */
final class InstallerHostedSchemaResetService
{
    public function resetMysqlHostedInstallArtifacts(?string $connectionName = null): void
    {
        $connectionName ??= (string) config('database.default', 'mysql');
        $connection = DB::connection($connectionName);
        $driver = $connection->getDriverName();

        if (! in_array($driver, ['mysql', 'mariadb'], true)) {
            throw new RuntimeException('当前数据库类型不支持通过安装向导执行重新安装清理，请使用 MySQL 或 MariaDB 专用空库。');
        }

        $databaseName = (string) ($connection->getDatabaseName() ?: config('database.connections.'.$connectionName.'.database', ''));

        if ($databaseName === '') {
            throw new RuntimeException('无法确定当前数据库名称，已中止重新安装清理。');
        }

        $grammar = $connection->getQueryGrammar();
        $views = $this->fetchNamesByTypes($connectionName, $databaseName, ['VIEW']);
        $views = array_values(array_filter(
            $views,
            static fn (string $name): bool => InstallerHostedSchemaCatalog::isHostOwnedView($name)
        ));
        rsort($views, SORT_STRING);

        foreach ($views as $view) {
            $connection->statement('DROP VIEW IF EXISTS '.$grammar->wrapTable($view));
        }

        $baseTables = $this->fetchNamesByTypes($connectionName, $databaseName, ['BASE TABLE']);
        $tablesToDrop = array_values(array_filter(
            $baseTables,
            static fn (string $name): bool => InstallerHostedSchemaCatalog::isHostOwnedBaseTable($name)
        ));
        rsort($tablesToDrop, SORT_STRING);

        $connection->statement('SET FOREIGN_KEY_CHECKS=0');

        try {
            foreach ($tablesToDrop as $table) {
                $connection->statement('DROP TABLE IF EXISTS '.$grammar->wrapTable($table));
            }
        } finally {
            $connection->statement('SET FOREIGN_KEY_CHECKS=1');
        }
    }

    /**
     * @param  list<string>  $types
     * @return list<string>
     */
    private function fetchNamesByTypes(string $connectionName, string $databaseName, array $types): array
    {
        if ($types === []) {
            return [];
        }

        $connection = DB::connection($connectionName);
        $placeholders = implode(',', array_fill(0, count($types), '?'));
        $sql = "SELECT table_name AS table_name, table_type AS table_type
            FROM information_schema.tables
            WHERE table_schema = ?
              AND table_type IN ({$placeholders})";

        $bindings = array_merge([$databaseName], $types);
        $rows = $connection->select($sql, $bindings);
        $names = [];

        foreach ($rows as $row) {
            $name = (string) ($row->table_name ?? $row->TABLE_NAME ?? '');

            if ($name !== '') {
                $names[] = $name;
            }
        }

        $names = array_values(array_unique($names));
        sort($names, SORT_STRING);

        return $names;
    }
}
