/**
 * Client Session Management
 */
import { randomBytes, randomUUID } from 'node:crypto';
import jwt from 'jsonwebtoken';
import { log } from '@mondaydotcomorg/atp-runtime';
const DEFAULT_JWT_SECRET = 'atp-default-development-secret';
/**
 * Client session manager with JWT-based authentication
 */
export class ClientSessionManager {
    cache;
    inMemorySessions = new Map();
    cleanupTimers = new Map();
    tokenTTL;
    jwtSecret;
    constructor(options) {
        this.cache = options.cache;
        this.tokenTTL = options.tokenTTL;
        const secret = process.env.ATP_JWT_SECRET;
        if (!secret) {
            log.warn('ATP_JWT_SECRET not set - using default secret. This is insecure for production! ' +
                'Generate a secure secret with: openssl rand -base64 32');
            this.jwtSecret = DEFAULT_JWT_SECRET;
        }
        else {
            this.jwtSecret = secret;
        }
    }
    /**
     * Initialize a new client session
     */
    async initClient(request) {
        const clientId = this.generateClientId();
        const now = Date.now();
        const expiresAt = now + this.tokenTTL;
        const tokenRotateAt = now + this.tokenTTL / 2;
        const token = this.generateToken(clientId);
        const session = {
            clientId,
            createdAt: now,
            expiresAt,
            clientInfo: request.clientInfo,
            guidance: request.guidance,
            tools: request.tools || [],
            services: request.services,
        };
        if (this.cache) {
            const ttlSeconds = Math.floor(this.tokenTTL / 1000);
            await this.cache.set(`session:${clientId}`, session, ttlSeconds);
        }
        else {
            this.inMemorySessions.set(clientId, session);
            this.scheduleCleanup(clientId, this.tokenTTL);
        }
        return {
            clientId,
            token,
            expiresAt,
            tokenRotateAt,
        };
    }
    /**
     * Verify client token (JWT-based, stateless)
     */
    async verifyClient(clientId, token) {
        try {
            // Prevent algorithm confusion attacks - only allow HS256
            const decoded = jwt.verify(token, this.jwtSecret, {
                algorithms: ['HS256'],
            });
            if (decoded.clientId !== clientId || decoded.type !== 'client') {
                return false;
            }
            const session = await this.getSession(clientId);
            return session !== null;
        }
        catch (error) {
            return false;
        }
    }
    /**
     * Get client session
     */
    async getSession(clientId) {
        let session = null;
        if (this.cache) {
            session = await this.cache.get(`session:${clientId}`);
        }
        else {
            session = this.inMemorySessions.get(clientId) || null;
        }
        if (!session) {
            return null;
        }
        if (Date.now() > session.expiresAt) {
            if (this.cache) {
                await this.cache.delete(`session:${clientId}`);
            }
            else {
                this.inMemorySessions.delete(clientId);
            }
            return null;
        }
        return session;
    }
    /**
     * Revoke client session
     */
    async revokeClient(clientId) {
        if (this.cache) {
            await this.cache.delete(`session:${clientId}`);
        }
        else {
            this.inMemorySessions.delete(clientId);
        }
    }
    /**
     * Generate cryptographically secure client ID
     */
    generateClientId() {
        const random = randomBytes(16).toString('hex');
        return `cli_${random}`;
    }
    /**
     * Generate JWT token for client
     */
    generateToken(clientId) {
        return jwt.sign({
            clientId,
            type: 'client',
            jti: randomUUID(),
        }, this.jwtSecret, {
            expiresIn: 3600,
        });
    }
    /**
     * Schedule cleanup of expired in-memory session
     */
    scheduleCleanup(clientId, ttl) {
        const existingTimer = this.cleanupTimers.get(clientId);
        if (existingTimer) {
            clearTimeout(existingTimer);
        }
        const timer = setTimeout(() => {
            this.inMemorySessions.delete(clientId);
            this.cleanupTimers.delete(clientId);
        }, ttl);
        this.cleanupTimers.set(clientId, timer);
    }
    /**
     * Manually cleanup a session (useful for tests and explicit cleanup)
     */
    async cleanup(clientId) {
        const timer = this.cleanupTimers.get(clientId);
        if (timer) {
            clearTimeout(timer);
            this.cleanupTimers.delete(clientId);
        }
        if (this.cache) {
            await this.cache.delete(`session:${clientId}`);
        }
        else {
            this.inMemorySessions.delete(clientId);
        }
    }
    /**
     * Cleanup all sessions (useful for shutdown)
     */
    async cleanupAll() {
        for (const timer of this.cleanupTimers.values()) {
            clearTimeout(timer);
        }
        this.cleanupTimers.clear();
        if (!this.cache) {
            this.inMemorySessions.clear();
        }
    }
}
//# sourceMappingURL=client-sessions.js.map