/**
 * Session Service
 * Handles session management for the auth module
 */
import crypto from "crypto";
export function createSessionService(adapter, config = {}) {
    const expiresIn = config.expiresIn ?? 604800; // 7 days default
    const updateAge = config.updateAge ?? 86400; // 1 day default
    return {
        generateToken() {
            return crypto.randomBytes(32).toString("hex");
        },
        async createSession({ user, tenantId, ipAddress, userAgent, type = "default" }) {
            const token = this.generateToken();
            const expiresAt = new Date(Date.now() + expiresIn * 1000);
            const session = await adapter.createSession({
                token,
                user_Id: user.id,
                tenant_Id: tenantId || null,
                expiresAt,
                ipAddress: ipAddress || null,
                userAgent: userAgent || null,
                type,
            });
            return session;
        },
        async validateSession(token) {
            const result = await adapter.findSessionByToken(token);
            if (!result) {
                return null;
            }
            const { session, user } = result;
            // Check if session has expired
            if (new Date() > new Date(session.expiresAt)) {
                await adapter.deleteSessionByToken(token);
                return null;
            }
            // Check if session needs to be updated (extend expiration)
            const sessionAge = Date.now() - new Date(session.updatedAt || session.createdAt).getTime();
            if (sessionAge > updateAge * 1000) {
                const newExpiresAt = new Date(Date.now() + expiresIn * 1000);
                await adapter.updateSession(session.id, { expiresAt: newExpiresAt });
                session.expiresAt = newExpiresAt;
            }
            return { session, user };
        },
        async invalidateSession(token) {
            await adapter.deleteSessionByToken(token);
            // Clear session cache so revocation takes effect immediately
            try {
                const { getCache } = await import('../../utils/cache.js');
                const cache = getCache();
                await cache.delete(`auth:session:${token}`);
            }
            catch { }
        },
        async invalidateAllSessions(userId) {
            // Capture active tokens first so we can evict session cache entries after delete.
            const sessions = await adapter.findSessionsByUserId(userId);
            await adapter.deleteSessionsByUserId(userId);
            try {
                const { getCache } = await import('../../utils/cache.js');
                const cache = getCache();
                await Promise.all(sessions.map((session) => cache.delete(`auth:session:${session.token}`)));
            }
            catch { }
        },
        async listSessions(userId) {
            const sessions = await adapter.findSessionsByUserId(userId);
            // Filter out expired sessions
            const now = new Date();
            return sessions.filter((s) => new Date(s.expiresAt) > now);
        },
        async updateSession(sessionId) {
            const newExpiresAt = new Date(Date.now() + expiresIn * 1000);
            return adapter.updateSession(sessionId, { expiresAt: newExpiresAt });
        },
        async cleanupExpiredSessions(userId) {
            const sessions = await adapter.findSessionsByUserId(userId);
            const now = new Date();
            for (const session of sessions) {
                if (new Date(session.expiresAt) <= now) {
                    await adapter.deleteSession(session.id);
                    // Best-effort cache cleanup for expired session entries.
                    try {
                        const { getCache } = await import('../../utils/cache.js');
                        const cache = getCache();
                        await cache.delete(`auth:session:${session.token}`);
                    }
                    catch { }
                }
            }
        },
        async countSessionsByType(userId, type, tenantId = null) {
            const sessions = await adapter.findSessionsByUserId(userId);
            const now = new Date();
            return sessions.filter((s) => {
                if (new Date(s.expiresAt) <= now)
                    return false;
                if (s.type !== type)
                    return false;
                if (tenantId !== null && s.tenant_Id !== tenantId)
                    return false;
                return true;
            }).length;
        },
    };
}
// Lazy getter for SettingsService to avoid circular dependency
let _SettingsService = null;
async function getSettingsService() {
    if (!_SettingsService) {
        const module = await import('../../services/SettingsService.js');
        _SettingsService = module.default;
    }
    return _SettingsService;
}
// Lazy getter for ItemsService
let _ItemsService = null;
async function getItemsService() {
    if (!_ItemsService) {
        const module = await import('../../services/ItemsService.js');
        _ItemsService = module.default || module.ItemsService;
    }
    return _ItemsService;
}
export async function validateSessionLimits(sessionService, userId, sessionType, tenantId = null, role = null, config) {
    // Skip validation for 'default' session types
    if (sessionType === "default") {
        return { isValid: true };
    }
    // Validate session type
    if (!["mobile", "web"].includes(sessionType)) {
        return { isValid: false, error: "Invalid session type. Must be 'mobile' or 'web'" };
    }
    // Always skip session limit validation for administrator role
    if (role?.name === "administrator") {
        return { isValid: true };
    }
    try {
        // If no config provided, fetch from SettingsService
        let settings = config;
        if (!settings) {
            const settingsService = await getSettingsService();
            const isMultiTenantEnabled = process.env.MULTI_TENANT === "true";
            if (isMultiTenantEnabled && tenantId) {
                settings = await settingsService.getTenantSettings(tenantId);
            }
            else {
                settings = settingsService.getGlobalSettings();
            }
        }
        if (!settings) {
            return { isValid: true };
        }
        // Get session limit for this type
        const limitKey = `${sessionType}_session_limit`;
        const sessionLimit = settings[limitKey];
        // If no limit is set, allow
        if (sessionLimit === undefined || sessionLimit === null || sessionLimit === -1) {
            return { isValid: true };
        }
        // Check if role is in the session_limit_roles array (if specified)
        const sessionLimitRoles = settings.session_limit_roles;
        if (sessionLimitRoles && Array.isArray(sessionLimitRoles) && sessionLimitRoles.length > 0) {
            if (!role?.id || !sessionLimitRoles.includes(role.id)) {
                return { isValid: true };
            }
        }
        // If limit is 0, don't allow any sessions of this type
        if (sessionLimit === 0) {
            return {
                isValid: false,
                error: `${sessionType.charAt(0).toUpperCase() + sessionType.slice(1)} sessions are not allowed`,
            };
        }
        // Count existing sessions using ItemsService directly for accurate counting
        const ItemsService = await getItemsService();
        const sessionsService = new ItemsService('baasix_Sessions', { accountability: undefined });
        const filter = {
            user_Id: { eq: userId },
            type: { eq: sessionType },
            expiresAt: { gt: new Date().toISOString() }
        };
        const isMultiTenantEnabled = process.env.MULTI_TENANT === "true";
        if (isMultiTenantEnabled && tenantId) {
            filter.tenant_Id = { eq: tenantId };
        }
        const activeSessions = await sessionsService.readByQuery({
            filter,
            limit: -1
        }, true);
        const count = activeSessions.data?.length || 0;
        if (count >= sessionLimit) {
            return {
                isValid: false,
                error: `Maximum ${sessionType} session limit (${sessionLimit}) reached. Please logout from another device.`,
            };
        }
        return { isValid: true };
    }
    catch (error) {
        console.error("Error validating session limits:", error);
        return { isValid: true };
    }
}
export default createSessionService;
//# sourceMappingURL=session.js.map