<?php
// config.php
// Conexión a BD + configuración de seguridad de sesión.
// Se incluye al inicio de CADA archivo que necesite sesión o BD.

// ── Configuración de sesión segura (debe ir ANTES de session_start) ──
ini_set('session.use_strict_mode', 1);      // rechaza IDs de sesión inventados por el cliente
ini_set('session.cookie_httponly', 1);      // la cookie no es accesible desde JS
ini_set('session.cookie_samesite', 'Lax');  // mitiga CSRF básico manteniendo enlaces externos
ini_set('session.use_only_cookies', 1);     // nunca aceptar session ID por URL

// Si el sitio corre bajo HTTPS (recomendado siempre en producción),
// esto fuerza que la cookie de sesión solo viaje cifrada.
if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
    ini_set('session.cookie_secure', 1);
}

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

// ── Conexión PDO ──────────────────────────────────────────────
function db(): PDO {
    static $pdo = null;
    if ($pdo === null) {
        $host = 'localhost';
        $dbname = 'sgc';   // TODO: ajustar al desplegar
        $user = 'root';               // TODO: ajustar al desplegar
        $pass = '';                 // TODO: ajustar al desplegar

        $pdo = new PDO(
            "mysql:host=$host;dbname=$dbname;charset=utf8mb4",
            $user,
            $pass,
            [
                PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE  => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES    => false, // prepares reales, mejor protección contra inyección
            ]
        );
    }
    return $pdo;
}

// ── Helpers de sesión / autenticación ─────────────────────────

// Retorna el usuario en sesión o null si no hay sesión activa.
function usuarioActual(): ?array {
    return $_SESSION['usuario'] ?? null;
}

// Corta la ejecución si no hay sesión activa, redirigiendo a login.
// También verifica que el usuario de la sesión SIGA existiendo y activo
// en BD — si alguien borró/recreó la cuenta después de iniciar sesión
// (o quedó una cookie "recordarme" vieja), esto evita que cualquier
// INSERT que use ese id explote con un error de llave foránea, y en
// cambio fuerza un re-login limpio con un mensaje claro.
function requerirLogin(): array {
    $u = usuarioActual();
    if (!$u) {
        header('Location: login.php');
        exit;
    }

    static $yaVerificado = false;
    if (!$yaVerificado) {
        $yaVerificado = true;
        $stmt = db()->prepare("SELECT activo FROM usuarios WHERE id = ?");
        $stmt->execute([$u['id']]);
        $fila = $stmt->fetch();
        if (!$fila || !$fila['activo']) {
            $_SESSION = [];
            session_destroy();
            header('Location: login.php?motivo=sesion_invalida');
            exit;
        }
    }

    return $u;
}

// Corta la ejecución si el usuario en sesión no tiene uno de los
// roles permitidos (403 simple, sin revelar detalles).
function requerirRol(array $rolesPermitidos): array {
    $u = requerirLogin();
    if (!in_array($u['rol'], $rolesPermitidos, true)) {
        http_response_code(403);
        echo 'No tienes permiso para acceder a esta sección.';
        exit;
    }
    return $u;
}

// ── Correo del sistema ─────────────────────────────────────────
// Envoltorio simple sobre mail(). Igual que en recuperar_password.php:
// depende de que el servidor tenga un MTA configurado (sendmail/Postfix)
// o un relay SMTP en php.ini. Si los correos no llegan en producción,
// reemplazar por PHPMailer + SMTP con credenciales reales.
function enviarCorreoSistema(string $para, string $asunto, string $cuerpo): void {
    if (!$para || !filter_var($para, FILTER_VALIDATE_EMAIL)) return;
    @mail($para, $asunto, $cuerpo, "From: no-responder@fondodocumental.local\r\n");
}

// ── CSRF ───────────────────────────────────────────────────────
function csrfToken(): string {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function csrfCampoOculto(): string {
    return '<input type="hidden" name="csrf_token" value="' . htmlspecialchars(csrfToken()) . '">';
}

// Valida el token recibido por POST contra el de la sesión.
// hash_equals evita timing attacks al comparar strings.
function csrfValidarOMorir(): void {
    $recibido = $_POST['csrf_token'] ?? '';
    if (!$recibido || !hash_equals($_SESSION['csrf_token'] ?? '', $recibido)) {
        http_response_code(400);
        die('Token de seguridad inválido o expirado. Recarga la página e intenta de nuevo.');
    }
}
