import crypto from 'crypto';

/**
 * CSRF Token Manager
 *
 * Manages CSRF token generation, storage, and validation.
 * Tokens are stored in memory with expiration timestamps.
 *
 * In production, consider migrating to Redis for:
 * - Multi-server deployments
 * - Persistence across restarts
 * - Better scalability
 */

interface TokenData {
    token: string;
    expiresAt: number;
}

// In-memory storage: userId -> TokenData
const tokenStore = new Map<string, TokenData>();

// Token expiration time (1 hour)
const TOKEN_EXPIRY_MS = 60 * 60 * 1000;

/**
 * Generate a cryptographically secure random token
 */
export const generateCsrfToken = (): string => {
    return crypto.randomBytes(32).toString('base64url');
};

/**
 * Store a CSRF token for a user
 * @param userId - The user ID to associate with the token
 * @param token - The CSRF token to store
 */
export const storeToken = (userId: string, token: string): void => {
    const expiresAt = Date.now() + TOKEN_EXPIRY_MS;
    tokenStore.set(userId, { token, expiresAt });
};

/**
 * Generate and store a new CSRF token for a user
 * @param userId - The user ID
 * @returns The generated token
 */
export const generateAndStoreToken = (userId: string): string => {
    const token = generateCsrfToken();
    storeToken(userId, token);
    return token;
};

/**
 * Validate a CSRF token for a user
 * @param userId - The user ID
 * @param token - The token to validate
 * @returns true if valid, false otherwise
 */
export const validateToken = (userId: string, token: string): boolean => {
    const storedData = tokenStore.get(userId);

    if (!storedData) {
        return false;
    }

    // Check if token has expired
    if (Date.now() > storedData.expiresAt) {
        tokenStore.delete(userId);
        return false;
    }

    // Constant-time comparison to prevent timing attacks
    return crypto.timingSafeEqual(
        Buffer.from(storedData.token),
        Buffer.from(token)
    );
};

/**
 * Invalidate a user's CSRF token (on logout, etc.)
 * @param userId - The user ID
 */
export const invalidateToken = (userId: string): void => {
    tokenStore.delete(userId);
};

/**
 * Clean up expired tokens (should be called periodically)
 */
export const cleanupExpiredTokens = (): void => {
    const now = Date.now();
    for (const [userId, tokenData] of tokenStore.entries()) {
        if (now > tokenData.expiresAt) {
            tokenStore.delete(userId);
        }
    }
};

/**
 * Get token expiry time in milliseconds
 */
export const getTokenExpiryMs = (): number => {
    return TOKEN_EXPIRY_MS;
};

// Cleanup expired tokens every 15 minutes
setInterval(cleanupExpiredTokens, 15 * 60 * 1000);
