<?php

/** Database driver helpers (SQLite legacy + MySQL). */

function qdb_driver(): string {
    if (isset($GLOBALS['qdb_driver_cache'])) {
        return (string)$GLOBALS['qdb_driver_cache'];
    }
    $GLOBALS['qdb_driver_cache'] = qdb_driver_resolve();
    return (string)$GLOBALS['qdb_driver_cache'];
}

function qdb_reset_driver_cache(): void {
    unset($GLOBALS['qdb_driver_cache']);
}

function qdb_driver_resolve(): string {
    $configured = strtolower(trim((string)(qdb_env_get('QDB_DRIVER') ?? '')));
    if ($configured === 'mysql' || $configured === 'sqlite') {
        $driver = $configured;
        return $driver;
    }
    if (qdb_env_get('QDB_MYSQL_HOST') || qdb_env_get('QDB_MYSQL_DATABASE')) {
        $driver = 'mysql';
        return $driver;
    }
    $driver = 'sqlite';
    return $driver;
}

function qdb_is_mysql(): bool {
    return qdb_driver() === 'mysql';
}

function qdb_is_sqlite(): bool {
    return qdb_driver() === 'sqlite';
}

function qdb_schema_file(): string {
    return qdb_is_mysql()
        ? __DIR__ . '/../schema.mysql.sql'
        : QDB_SCHEMA;
}

function qdb_mysql_dsn(): string {
    $host = qdb_env_get('QDB_MYSQL_HOST') ?: '127.0.0.1';
    $port = qdb_env_get('QDB_MYSQL_PORT') ?: '3306';
    $db = qdb_env_get('QDB_MYSQL_DATABASE') ?: 'nat-as-db';
    return "mysql:host={$host};port={$port};dbname={$db};charset=utf8mb4";
}

function qdb_mysql_credentials(): array {
    return [
        qdb_env_get('QDB_MYSQL_USER') ?: 'nat_as_db',
        qdb_env_get('QDB_MYSQL_PASSWORD') ?: '',
    ];
}

/** @return array{0: PDO, 1: string, 2: null} */
function qdb_mysql_open(): array {
    static $pdo = null;
    if ($pdo === null) {
        [$user, $pass] = qdb_mysql_credentials();
        $pdo = new PDO(qdb_mysql_dsn(), $user, $pass, [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    }
    return [$pdo, '', null];
}

function qdb_set_foreign_keys(PDO $pdo, bool $enabled): void {
    if (qdb_is_mysql()) {
        $pdo->exec('SET FOREIGN_KEY_CHECKS = ' . ($enabled ? '1' : '0'));
        return;
    }
    $pdo->exec('PRAGMA foreign_keys = ' . ($enabled ? 'ON' : 'OFF') . ';');
}

function qdb_table_exists(PDO $pdo, string $table): bool {
    if (qdb_is_mysql()) {
        $stmt = $pdo->prepare(
            'SELECT 1 FROM information_schema.tables
             WHERE table_schema = DATABASE() AND table_name = :t LIMIT 1'
        );
        $stmt->execute([':t' => $table]);
        return (bool)$stmt->fetchColumn();
    }
    $stmt = $pdo->prepare(
        "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = :t LIMIT 1"
    );
    $stmt->execute([':t' => $table]);
    return (bool)$stmt->fetchColumn();
}

function qdb_column_exists(PDO $pdo, string $table, string $column): bool {
    if (qdb_is_mysql()) {
        $stmt = $pdo->prepare(
            'SELECT 1 FROM information_schema.columns
             WHERE table_schema = DATABASE() AND table_name = :t AND column_name = :c LIMIT 1'
        );
        $stmt->execute([':t' => $table, ':c' => $column]);
        return (bool)$stmt->fetchColumn();
    }
    $stmt = $pdo->query('PRAGMA table_info(' . $table . ')');
    foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $col) {
        if (($col['name'] ?? '') === $column) {
            return true;
        }
    }
    return false;
}

function qdb_schema_version(PDO $pdo): int {
    if (qdb_is_mysql()) {
        if (!qdb_table_exists($pdo, 'schema_version')) {
            return 0;
        }
        return (int)$pdo->query('SELECT version FROM schema_version WHERE id = 1')->fetchColumn();
    }
    return (int)$pdo->query('PRAGMA user_version')->fetchColumn();
}

function qdb_set_schema_version(PDO $pdo, int $version): void {
    if (qdb_is_mysql()) {
        if (!qdb_table_exists($pdo, 'schema_version')) {
            $pdo->exec(
                'CREATE TABLE IF NOT EXISTS schema_version (
                    id INT NOT NULL PRIMARY KEY,
                    version INT NOT NULL
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
            );
        }
        $pdo->prepare(
            'INSERT INTO schema_version (id, version) VALUES (1, :v)
             ON DUPLICATE KEY UPDATE version = VALUES(version)'
        )->execute([':v' => $version]);
        return;
    }
    $pdo->exec('PRAGMA user_version = ' . (int)$version . ';');
}

function qdb_stmt_affected_rows(PDO $pdo, PDOStatement $stmt): int {
    if (qdb_is_mysql()) {
        return $stmt->rowCount();
    }
    return (int)$pdo->query('SELECT changes()')->fetchColumn();
}

function qdb_create_index_if_not_exists(PDO $pdo, string $name, string $table, string $columnsSql): void {
    if (qdb_is_mysql()) {
        if (!qdb_table_exists($pdo, $table)) {
            return;
        }
        $stmt = $pdo->prepare(
            'SELECT 1 FROM information_schema.statistics
             WHERE table_schema = DATABASE() AND table_name = :t AND index_name = :i LIMIT 1'
        );
        $stmt->execute([':t' => $table, ':i' => $name]);
        if ($stmt->fetchColumn()) {
            return;
        }
        $pdo->exec("CREATE INDEX `{$name}` ON `{$table}` ({$columnsSql})");
        return;
    }
    $pdo->exec("CREATE INDEX IF NOT EXISTS {$name} ON {$table}({$columnsSql})");
}

/**
 * @param list<string> $conflictColumns
 */
function qdb_upsert_do_nothing(array $conflictColumns): string {
    if (qdb_is_mysql()) {
        $first = $conflictColumns[0];
        return "ON DUPLICATE KEY UPDATE `{$first}` = `{$first}`";
    }
    return 'ON CONFLICT(' . implode(', ', $conflictColumns) . ') DO NOTHING';
}

/**
 * @param list<string> $conflictColumns
 * @param array<string, string> $assignments column => expression (use excluded.col on SQLite)
 */
function qdb_upsert_update(array $conflictColumns, array $assignments): string {
    if (qdb_is_mysql()) {
        $parts = [];
        foreach ($assignments as $column => $expression) {
            $mysqlExpr = preg_replace('/\bexcluded\.(\w+)\b/', 'VALUES($1)', $expression) ?? $expression;
            $parts[] = "`{$column}` = {$mysqlExpr}";
        }
        return 'ON DUPLICATE KEY UPDATE ' . implode(', ', $parts);
    }
    $parts = [];
    foreach ($assignments as $column => $expression) {
        $parts[] = "{$column} = {$expression}";
    }
    return 'ON CONFLICT(' . implode(', ', $conflictColumns) . ') DO UPDATE SET ' . implode(', ', $parts);
}

function qdb_exec_schema_file(PDO $pdo, string $schemaPath): void {
    $sql = file_get_contents($schemaPath);
    if ($sql === false) {
        throw new RuntimeException("Could not read schema: {$schemaPath}");
    }
    qdb_exec_schema_sql($pdo, $sql);
}

function qdb_exec_schema_sql(PDO $pdo, string $sql): void {
    if (qdb_is_mysql()) {
        foreach (qdb_split_sql_statements($sql) as $statement) {
            $trimmed = trim($statement);
            if ($trimmed === '' || str_starts_with($trimmed, '--')) {
                continue;
            }
            $pdo->exec($trimmed);
        }
        return;
    }
    $pdo->exec($sql);
}

/** @return list<string> */
function qdb_split_sql_statements(string $sql): array {
    $parts = preg_split('/;\s*\n/', $sql) ?: [];
    return array_values(array_filter(array_map('trim', $parts), fn($s) => $s !== ''));
}
