<?php
/*
 * Copyright 2026 Alessandro Chiapparini <sprint_planer_web@chiapparini.org>
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * See the LICENSE file in the project root for the full license text.
 */

declare(strict_types=1);

namespace App\Db;

use PDO;
use PDOException;
use RuntimeException;

/**
 * Process-wide PDO/SQLite connection.
 *
 * Intentionally a small singleton: the app runs one request per PHP process
 * under Apache's prefork MPM, so a per-process cache is safe and cheap.
 */
final class Connection
{
    private static ?PDO $pdo = null;
    private static ?string $path = null;

    public static function pdo(): PDO
    {
        if (self::$pdo instanceof PDO) {
            return self::$pdo;
        }

        $path = self::resolvePath();
        $dir = dirname($path);
        if (!is_dir($dir)) {
            if (!mkdir($dir, 0o775, true) && !is_dir($dir)) {
                throw new RuntimeException("Cannot create sqlite dir: {$dir}");
            }
        }

        try {
            $pdo = new PDO('sqlite:' . $path, null, null, [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES => false,
            ]);
        } catch (PDOException $e) {
            throw new RuntimeException("Cannot open sqlite database at {$path}: " . $e->getMessage(), 0, $e);
        }

        // Sane pragmas. foreign_keys MUST be on for every connection.
        $pdo->exec('PRAGMA foreign_keys = ON');
        $pdo->exec('PRAGMA journal_mode = WAL');
        $pdo->exec('PRAGMA synchronous = NORMAL');
        $pdo->exec('PRAGMA busy_timeout = 5000');

        self::$pdo = $pdo;
        self::$path = $path;
        return $pdo;
    }

    public static function path(): string
    {
        if (self::$path === null) {
            self::pdo();
        }
        return (string) self::$path;
    }

    /** Reset — only for tests. */
    public static function reset(): void
    {
        self::$pdo = null;
        self::$path = null;
    }

    private static function resolvePath(): string
    {
        $path = getenv('DB_PATH');
        if (is_string($path) && $path !== '') {
            return $path;
        }
        // Fallback suitable for local dev outside docker.
        return dirname(__DIR__, 2) . '/data/app.sqlite';
    }
}
