"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HostedDemoAuthSessionService = void 0;
const crypto_1 = require("crypto");
const logger_1 = require("../utils/logger");
const HostedDemoAuthSessionStore_1 = require("./HostedDemoAuthSessionStore");
const AUTH_SESSION_COOKIE_NAME = 'rivet_demo_auth_session';
const AUTH_SESSION_TTL_MS = 12 * 60 * 60 * 1000;
const AUTH_SESSION_CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
const log = (0, logger_1.createLogger)('HostedDemoAuthSessionService');
/**
 * Stores browser-scoped auth state for hosted demo mode.
 */
class HostedDemoAuthSessionService {
    authBySessionId = new Map();
    cleanupTimer;
    store;
    now;
    constructor(options) {
        this.store = options?.store ?? new HostedDemoAuthSessionStore_1.InMemoryHostedDemoAuthSessionStore();
        this.now = options?.now ?? (() => Date.now());
        this.cleanupTimer = setInterval(() => {
            this.cleanupExpiredSessions();
        }, AUTH_SESSION_CLEANUP_INTERVAL_MS);
        this.cleanupTimer.unref();
    }
    async initialize() {
        await this.store.initialize();
        const records = await this.store.loadAll();
        for (const { sessionId, value } of records) {
            if (this.now() - value.updatedAt > AUTH_SESSION_TTL_MS) {
                void this.store.delete(sessionId);
                continue;
            }
            this.authBySessionId.set(sessionId, value);
        }
    }
    getCookieSessionId(req) {
        const cookieHeader = req.headers.cookie;
        if (!cookieHeader) {
            return null;
        }
        const pairs = cookieHeader.split(';');
        for (const pair of pairs) {
            const [rawName, rawValue] = pair.split('=');
            if (!rawName || !rawValue) {
                continue;
            }
            if (rawName.trim() !== AUTH_SESSION_COOKIE_NAME) {
                continue;
            }
            return decodeURIComponent(rawValue.trim());
        }
        return null;
    }
    setCookieSessionId(res, sessionId) {
        res.cookie(AUTH_SESSION_COOKIE_NAME, sessionId, {
            httpOnly: true,
            sameSite: 'lax',
            secure: process.env.NODE_ENV === 'production',
            maxAge: AUTH_SESSION_TTL_MS,
            path: '/',
        });
    }
    getOrCreateSessionId(req, res) {
        const existingSessionId = this.getCookieSessionId(req);
        if (existingSessionId) {
            this.setCookieSessionId(res, existingSessionId);
            return existingSessionId;
        }
        const sessionId = (0, crypto_1.randomUUID)();
        this.setCookieSessionId(res, sessionId);
        return sessionId;
    }
    getAuthForRequest(req) {
        const sessionId = this.getCookieSessionId(req);
        if (!sessionId) {
            return null;
        }
        return this.getAuthForSessionId(sessionId);
    }
    getAuthForSessionId(sessionId) {
        if (!sessionId) {
            return null;
        }
        const existingAuth = this.authBySessionId.get(sessionId);
        if (!existingAuth) {
            return null;
        }
        if (this.now() - existingAuth.updatedAt > AUTH_SESSION_TTL_MS) {
            this.authBySessionId.delete(sessionId);
            void this.store.delete(sessionId);
            return null;
        }
        return existingAuth;
    }
    storeAuthForRequest(req, res, auth) {
        const sessionId = this.getOrCreateSessionId(req, res);
        return this.storeAuthForSessionId(sessionId, res, auth);
    }
    storeAuthForSessionId(sessionId, res, auth) {
        const existingAuth = this.getAuthForSessionId(sessionId);
        this.setCookieSessionId(res, sessionId);
        const nextValue = {
            activeDemoSessionId: existingAuth?.activeDemoSessionId,
            ...auth,
            updatedAt: this.now(),
        };
        this.authBySessionId.set(sessionId, nextValue);
        this.persist(sessionId, nextValue);
        return sessionId;
    }
    getActiveDemoSessionIdForRequest(req) {
        const auth = this.getAuthForRequest(req);
        return auth?.activeDemoSessionId ?? null;
    }
    storeActiveDemoSessionIdForRequest(req, res, demoSessionId) {
        const sessionId = this.getOrCreateSessionId(req, res);
        this.storeActiveDemoSessionIdForSessionId(sessionId, res, demoSessionId);
    }
    storeActiveDemoSessionIdForSessionId(sessionId, res, demoSessionId) {
        const existingAuth = this.getAuthForSessionId(sessionId);
        if (!existingAuth) {
            return;
        }
        this.setCookieSessionId(res, sessionId);
        const nextValue = {
            ...existingAuth,
            activeDemoSessionId: demoSessionId,
            updatedAt: this.now(),
        };
        this.authBySessionId.set(sessionId, nextValue);
        this.persist(sessionId, nextValue);
    }
    clearActiveDemoSessionIdForRequest(req, res) {
        const sessionId = this.getCookieSessionId(req);
        if (!sessionId) {
            return;
        }
        this.clearActiveDemoSessionIdForSessionId(sessionId, res);
    }
    clearActiveDemoSessionIdForSessionId(sessionId, res) {
        const existingAuth = this.getAuthForSessionId(sessionId);
        if (!existingAuth) {
            return;
        }
        this.setCookieSessionId(res, sessionId);
        const nextValue = {
            ...existingAuth,
            updatedAt: this.now(),
        };
        delete nextValue.activeDemoSessionId;
        this.authBySessionId.set(sessionId, nextValue);
        this.persist(sessionId, nextValue);
    }
    /**
     * Fully clears the browser-scoped auth for this request (logout): drops the
     * in-memory + persisted record and expires the session cookie so the next
     * /api/config read sees an unauthenticated session.
     */
    clearAuthForRequest(req, res) {
        const sessionId = this.getCookieSessionId(req);
        if (sessionId) {
            this.authBySessionId.delete(sessionId);
            void this.store.delete(sessionId);
        }
        res.clearCookie(AUTH_SESSION_COOKIE_NAME, { path: '/' });
    }
    shutdown() {
        clearInterval(this.cleanupTimer);
        this.authBySessionId.clear();
        void this.store.shutdown();
    }
    cleanupExpiredSessions() {
        const now = this.now();
        for (const [sessionId, value] of this.authBySessionId.entries()) {
            if (now - value.updatedAt > AUTH_SESSION_TTL_MS) {
                this.authBySessionId.delete(sessionId);
                void this.store.delete(sessionId);
            }
        }
    }
    persist(sessionId, value) {
        void this.store
            .set(sessionId, value, AUTH_SESSION_TTL_MS)
            .catch((error) => {
            log.warn('Failed to persist hosted demo auth session:', error);
        });
    }
}
exports.HostedDemoAuthSessionService = HostedDemoAuthSessionService;
//# sourceMappingURL=HostedDemoAuthSessionService.js.map