/**
 * Auth exchange via Gateway.
 *
 * The daemon sends OAuth codes and refresh tokens to the Gateway (never directly
 * to Supabase). The Gateway handles the Supabase interaction and returns tokens.
 *
 * See PRD.md: "auth_exchange Message" and "token_refresh Message" sections.
 */
import WebSocket from "ws";
import { randomUUID } from "node:crypto";
import { getEnv } from "../env.js";
/** Default timeout for auth operations (30 seconds). */
const AUTH_TIMEOUT_MS = 30_000;
/**
 * Exchange an OAuth authorization code for session tokens via the Gateway.
 *
 * Opens a temporary WebSocket to the Gateway, sends an auth_exchange request,
 * and waits for the response with tokens.
 */
export async function exchangeCodeViaGateway(code) {
    const { gatewayUrl } = getEnv();
    const requestId = `auth_${randomUUID()}`;
    const request = {
        type: "auth_exchange",
        requestId,
        payload: { code },
    };
    const response = await sendAndWait(gatewayUrl, request, requestId, "auth_exchange_response");
    if (response.error) {
        throw new Error(`Auth exchange failed: ${response.error.message}`);
    }
    if (!response.payload) {
        throw new Error("Auth exchange returned empty payload");
    }
    return {
        access_token: response.payload.access_token,
        refresh_token: response.payload.refresh_token,
        expires_at: response.payload.expires_at,
        user: response.payload.user,
    };
}
/**
 * Refresh tokens via the Gateway.
 *
 * Opens a temporary WebSocket to the Gateway, sends a token_refresh request,
 * and waits for the response with new tokens.
 */
export async function refreshTokenViaGateway(refreshToken) {
    const { gatewayUrl } = getEnv();
    const requestId = `refresh_${randomUUID()}`;
    const request = {
        type: "token_refresh",
        requestId,
        payload: { refresh_token: refreshToken },
    };
    const response = await sendAndWait(gatewayUrl, request, requestId, "token_refresh_response");
    if (response.error) {
        throw new Error(`Token refresh failed: ${response.error.message}`);
    }
    if (!response.payload) {
        throw new Error("Token refresh returned empty payload");
    }
    return {
        access_token: response.payload.access_token,
        refresh_token: response.payload.refresh_token,
        expires_at: response.payload.expires_at,
    };
}
/**
 * Open a temporary WebSocket, send a message, and wait for a response
 * matching the given requestId and type. Closes the connection afterward.
 */
function sendAndWait(url, message, requestId, expectedType) {
    return new Promise((resolve, reject) => {
        let settled = false;
        const ws = new WebSocket(url);
        const timer = setTimeout(() => {
            if (!settled) {
                settled = true;
                ws.close();
                reject(new Error(`Auth request timed out after ${AUTH_TIMEOUT_MS}ms`));
            }
        }, AUTH_TIMEOUT_MS);
        ws.on("open", () => {
            ws.send(JSON.stringify(message));
        });
        ws.on("message", (data) => {
            try {
                const parsed = JSON.parse(data.toString());
                if (parsed.type === expectedType && parsed.requestId === requestId) {
                    settled = true;
                    clearTimeout(timer);
                    ws.close();
                    resolve(parsed);
                }
            }
            catch {
                // Ignore unparseable messages
            }
        });
        ws.on("error", (err) => {
            if (!settled) {
                settled = true;
                clearTimeout(timer);
                reject(new Error(`WebSocket error during auth: ${err.message}`));
            }
        });
        ws.on("close", (code) => {
            if (!settled) {
                settled = true;
                clearTimeout(timer);
                reject(new Error(`WebSocket closed unexpectedly (code: ${code})`));
            }
        });
    });
}
//# sourceMappingURL=gateway-auth.js.map