<?php
declare(strict_types=1);

require_once __DIR__ . '/config.php';

if (!isset($pdo) || !$pdo instanceof PDO) {
    try {
        if (DB_NAME === '' || DB_USER === '') {
            throw new RuntimeException('Database configuration is incomplete.');
        }
        $dsn = sprintf(
            'mysql:host=%s;port=%d;dbname=%s;charset=%s',
            DB_HOST,
            DB_PORT,
            DB_NAME,
            DB_CHARSET
        );
        $pdo = new PDO($dsn, DB_USER, DB_PASS, [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_STRINGIFY_FETCHES => false,
        ]);
        $pdo->exec("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci");
        $pdo->exec("SET time_zone = '+03:00'");
    } catch (Throwable $e) {
        error_log('[SMI DB] ' . $e->getMessage());
        if (APP_DEBUG) {
            http_response_code(500);
            die('<h2 style="font-family:Arial;padding:30px;color:#dc2626">Database connection failed: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8') . '</h2>');
        }
        http_response_code(503);
        die('The platform is temporarily unavailable.');
    }
}

if (!function_exists('db')) {
    function db(): PDO
    {
        global $pdo;
        return $pdo;
    }
}

if (!function_exists('smiIdentifier')) {
    function smiIdentifier(string $identifier): string
    {
        $clean = preg_replace('/[^a-zA-Z0-9_]/', '', $identifier);
        if ($clean === '') {
            throw new InvalidArgumentException('Invalid SQL identifier.');
        }
        return $clean;
    }
}

if (!function_exists('tableExists')) {
    function tableExists(PDO $pdo, string $table): bool
    {
        static $cache = [];
        $table = smiIdentifier($table);
        if (array_key_exists($table, $cache)) {
            return $cache[$table];
        }
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?');
        $stmt->execute([$table]);
        return $cache[$table] = (int)$stmt->fetchColumn() > 0;
    }
}

if (!function_exists('columnExists')) {
    function columnExists(PDO $pdo, string $table, string $column): bool
    {
        static $cache = [];
        $table = smiIdentifier($table);
        $column = smiIdentifier($column);
        $key = $table . '.' . $column;
        if (array_key_exists($key, $cache)) {
            return $cache[$key];
        }
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?');
        $stmt->execute([$table, $column]);
        return $cache[$key] = (int)$stmt->fetchColumn() > 0;
    }
}

if (!function_exists('indexExists')) {
    function indexExists(PDO $pdo, string $table, string $index): bool
    {
        $table = smiIdentifier($table);
        $index = smiIdentifier($index);
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND INDEX_NAME = ?');
        $stmt->execute([$table, $index]);
        return (int)$stmt->fetchColumn() > 0;
    }
}

if (!function_exists('safeCount')) {
    function safeCount(PDO $pdo, string $sql, array $params = []): int
    {
        try {
            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);
            return (int)$stmt->fetchColumn();
        } catch (Throwable $e) {
            error_log('[SMI safeCount] ' . $e->getMessage());
            return 0;
        }
    }
}

if (!function_exists('safeFetch')) {
    function safeFetch(PDO $pdo, string $sql, array $params = []): ?array
    {
        try {
            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);
            $row = $stmt->fetch(PDO::FETCH_ASSOC);
            return $row ?: null;
        } catch (Throwable $e) {
            error_log('[SMI safeFetch] ' . $e->getMessage());
            return null;
        }
    }
}

if (!function_exists('safeFetchAll')) {
    function safeFetchAll(PDO $pdo, string $sql, array $params = []): array
    {
        try {
            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);
            return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
        } catch (Throwable $e) {
            error_log('[SMI safeFetchAll] ' . $e->getMessage());
            return [];
        }
    }
}

if (!function_exists('safeExecute')) {
    function safeExecute(PDO $pdo, string $sql, array $params = []): bool
    {
        try {
            $stmt = $pdo->prepare($sql);
            return $stmt->execute($params);
        } catch (Throwable $e) {
            error_log('[SMI safeExecute] ' . $e->getMessage());
            return false;
        }
    }
}

if (!function_exists('mustExecute')) {
    /** Execute a required write query and allow PDO exceptions to propagate. */
    function mustExecute(PDO $pdo, string $sql, array $params = []): bool
    {
        $stmt = $pdo->prepare($sql);
        return $stmt->execute($params);
    }
}

// Compatibility aliases used by the advanced exam pages.
if (!function_exists('safeOne')) {
    function safeOne(PDO $pdo, string $sql, array $params = []): ?array { return safeFetch($pdo, $sql, $params); }
}
if (!function_exists('safeAll')) {
    function safeAll(PDO $pdo, string $sql, array $params = []): array { return safeFetchAll($pdo, $sql, $params); }
}
if (!function_exists('safeExec')) {
    function safeExec(PDO $pdo, string $sql, array $params = []): bool { return safeExecute($pdo, $sql, $params); }
}

if (!function_exists('beginTransaction')) {
    function beginTransaction(PDO $pdo): void { if (!$pdo->inTransaction()) $pdo->beginTransaction(); }
}
if (!function_exists('commitTransaction')) {
    function commitTransaction(PDO $pdo): void { if ($pdo->inTransaction()) $pdo->commit(); }
}
if (!function_exists('rollbackTransaction')) {
    function rollbackTransaction(PDO $pdo): void { if ($pdo->inTransaction()) $pdo->rollBack(); }
}
