<?php
declare(strict_types=1);

/** PDO to MariaDB `url_shortener` (separate from crashes DB). */
final class UrlShortenerDatabase {
    private static ?PDO $pdo = null;
    /** @var array<string,mixed>|null|false */
    private static $resolvedConfig = null;

    /** @return array<string,mixed>|null */
    private static function resolveConfig(): ?array {
        if (self::$resolvedConfig !== null) {
            return self::$resolvedConfig === false ? null : self::$resolvedConfig;
        }
        $block = cfg('url_shortener', null);
        if (is_array($block)) {
            if (!empty($block['enabled']) && is_array($block['db']['mysql'] ?? null)) {
                self::$resolvedConfig = $block;
                return $block;
            }
            if (is_array($block['db']['mysql'] ?? null) && ($block['db']['mysql']['database'] ?? '') !== '') {
                $block['enabled'] = true;
                self::$resolvedConfig = $block;
                return $block;
            }
        }
        $crashDb = cfg('db.mysql', []);
        if (!is_array($crashDb) || ($crashDb['password'] ?? '') === '') {
            self::$resolvedConfig = false;
            return null;
        }
        $m = $crashDb;
        $m['database'] = 'url_shortener';
        $candidate = [
            'enabled' => true,
            'public_base' => 'https://s.f0xx.org',
            'db' => ['mysql' => $m],
        ];
        if (!self::pingMysql($m)) {
            self::$resolvedConfig = false;
            return null;
        }
        self::$resolvedConfig = $candidate;
        return $candidate;
    }

    /** @param array<string,mixed> $m */
    private static function pingMysql(array $m): bool {
        try {
            $db = (string) ($m['database'] ?? '');
            if ($db === '') {
                return false;
            }
            $charset = (string) ($m['charset'] ?? 'utf8mb4');
            $socket = trim((string) ($m['socket'] ?? ''));
            if ($socket !== '') {
                $dsn = sprintf('mysql:unix_socket=%s;dbname=%s;charset=%s', $socket, $db, $charset);
            } else {
                $dsn = sprintf(
                    'mysql:host=%s;port=%d;dbname=%s;charset=%s',
                    $m['host'] ?? '127.0.0.1',
                    (int) ($m['port'] ?? 3306),
                    $db,
                    $charset
                );
            }
            $pdo = new PDO($dsn, (string) ($m['username'] ?? ''), (string) ($m['password'] ?? ''), [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            ]);
            $pdo->query('SELECT 1');
            return true;
        } catch (Throwable) {
            return false;
        }
    }

    public static function enabled(): bool {
        return self::resolveConfig() !== null;
    }

    public static function publicBase(): string {
        $block = self::resolveConfig();
        if ($block !== null) {
            return rtrim((string) ($block['public_base'] ?? 'https://s.f0xx.org'), '/');
        }
        return rtrim((string) cfg('url_shortener.public_base', 'https://s.f0xx.org'), '/');
    }

    public static function pdo(): PDO {
        if (!self::enabled()) {
            throw new RuntimeException('url_shortener not configured');
        }
        if (self::$pdo !== null) {
            return self::$pdo;
        }
        $block = self::resolveConfig();
        $m = is_array($block) ? ($block['db']['mysql'] ?? []) : [];
        if (!is_array($m) || ($m['database'] ?? '') === '') {
            throw new RuntimeException('url_shortener.db.mysql not configured');
        }
        $db = (string) $m['database'];
        $charset = (string) ($m['charset'] ?? 'utf8mb4');
        $socket = trim((string) ($m['socket'] ?? ''));
        if ($socket !== '') {
            $dsn = sprintf('mysql:unix_socket=%s;dbname=%s;charset=%s', $socket, $db, $charset);
        } else {
            $dsn = sprintf(
                'mysql:host=%s;port=%d;dbname=%s;charset=%s',
                $m['host'] ?? '127.0.0.1',
                (int) ($m['port'] ?? 3306),
                $db,
                $charset
            );
        }
        self::$pdo = new PDO($dsn, (string) ($m['username'] ?? ''), (string) ($m['password'] ?? ''), [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        return self::$pdo;
    }

    public static function ping(): bool {
        if (!self::enabled()) {
            return false;
        }
        try {
            self::pdo()->query('SELECT 1');
            return true;
        } catch (Throwable) {
            return false;
        }
    }
}
