import { DEMO_AUTH_ENABLED, getValidAccessToken } from "@/lib/auth"; const SUPABASE_URL = (import.meta.env["VITE_SUPABASE_URL"] ?? "").replace(/\/+$/, ""); const SUPABASE_ANON_KEY = import.meta.env["VITE_SUPABASE_ANON_KEY"] ?? ""; function parseErrorMessage(payload: unknown, fallback: string) { if (!payload || typeof payload !== "object") return fallback; const record = payload as Record; const message = record["message"]; const error = record["error"]; const details = record["details"]; if (typeof message === "string" && message.trim()) return message; if (typeof error === "string" && error.trim()) return error; if (typeof details === "string" && details.trim()) return details; return fallback; } function isExpiredJwt(status: number, payload: unknown) { if (status === 401) return true; const text = typeof payload === "string" ? payload : payload && typeof payload === "object" ? JSON.stringify(payload) : ""; return /jwt\s+expired|token\s+.*expired|expired\s+jwt/i.test(text); } async function rpcRequest(functionName: string, payload: Record, accessToken: string) { const response = await fetch(`${SUPABASE_URL}/rest/v1/rpc/${functionName}`, { method: "POST", headers: { apikey: SUPABASE_ANON_KEY, Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify(payload), }); const text = await response.text(); let parsed: unknown = null; if (text) { try { parsed = JSON.parse(text) as unknown; } catch { parsed = text; } } return { response, parsed }; } export async function hasRealSupabaseSession() { if (DEMO_AUTH_ENABLED) return false; return Boolean(await getValidAccessToken()); } export async function callAuthenticatedRpc( functionName: string, payload: Record = {}, ): Promise { if (DEMO_AUTH_ENABLED) { throw new Error("Esta operación de Supabase no está disponible en modo demostrativo."); } if (!SUPABASE_URL || !SUPABASE_ANON_KEY) { throw new Error("Faltan las variables VITE_SUPABASE_URL o VITE_SUPABASE_ANON_KEY."); } let accessToken = await getValidAccessToken(); if (!accessToken) { throw new Error("La sesión terminó. Vuelve a iniciar sesión."); } let result = await rpcRequest(functionName, payload, accessToken); // Cinturón de seguridad: si una pestaña estuvo suspendida y el JWT venció // entre comprobaciones, renovamos la sesión y repetimos la RPC una sola vez. if (!result.response.ok && isExpiredJwt(result.response.status, result.parsed)) { accessToken = await getValidAccessToken(true); if (!accessToken) { throw new Error("La sesión terminó. Vuelve a iniciar sesión."); } result = await rpcRequest(functionName, payload, accessToken); } if (!result.response.ok) { throw new Error( parseErrorMessage( result.parsed, `Supabase rechazó la operación ${functionName} (${result.response.status}).`, ), ); } return result.parsed as T; }