/**
 * JWT Token Management
 *
 * Handles JWT token generation and validation.
 */
import { createHmac, timingSafeEqual } from 'crypto';
// Base64URL encoding/decoding
function base64UrlEncode(data) {
    const base64 = Buffer.from(data).toString('base64');
    return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
function base64UrlDecode(data) {
    const base64 = data.replace(/-/g, '+').replace(/_/g, '/');
    const padding = base64.length % 4;
    const padded = padding ? base64 + '='.repeat(4 - padding) : base64;
    return Buffer.from(padded, 'base64').toString('utf8');
}
/**
 * Create HMAC signature
 */
function createSignature(data, secret) {
    return base64UrlEncode(createHmac('sha256', secret).update(data).digest());
}
/**
 * Generate a JWT token
 */
export function generateToken(payload, secret, expiresIn = '24h') {
    const now = Math.floor(Date.now() / 1000);
    const exp = now + parseExpiration(expiresIn);
    const fullPayload = {
        ...payload,
        iat: now,
        exp,
    };
    const header = { alg: 'HS256', typ: 'JWT' };
    const headerEncoded = base64UrlEncode(JSON.stringify(header));
    const payloadEncoded = base64UrlEncode(JSON.stringify(fullPayload));
    const signature = createSignature(`${headerEncoded}.${payloadEncoded}`, secret);
    return `${headerEncoded}.${payloadEncoded}.${signature}`;
}
/**
 * Verify and decode a JWT token
 */
export function verifyToken(token, secret) {
    try {
        const parts = token.split('.');
        if (parts.length !== 3) {
            return null;
        }
        const [headerEncoded, payloadEncoded, signatureProvided] = parts;
        // Verify signature
        const expectedSignature = createSignature(`${headerEncoded}.${payloadEncoded}`, secret);
        // Use timing-safe comparison
        const sig1 = Buffer.from(signatureProvided);
        const sig2 = Buffer.from(expectedSignature);
        if (sig1.length !== sig2.length || !timingSafeEqual(sig1, sig2)) {
            return null;
        }
        // Decode payload
        const payload = JSON.parse(base64UrlDecode(payloadEncoded));
        // Check expiration
        const now = Math.floor(Date.now() / 1000);
        if (payload.exp && payload.exp < now) {
            return null;
        }
        return payload;
    }
    catch {
        return null;
    }
}
/**
 * Parse expiration string to seconds
 */
function parseExpiration(exp) {
    const match = exp.match(/^(\d+)([smhd])$/);
    if (!match) {
        return 24 * 60 * 60; // Default 24 hours
    }
    const value = parseInt(match[1], 10);
    const unit = match[2];
    switch (unit) {
        case 's':
            return value;
        case 'm':
            return value * 60;
        case 'h':
            return value * 60 * 60;
        case 'd':
            return value * 24 * 60 * 60;
        default:
            return 24 * 60 * 60;
    }
}
/**
 * Decode token without verification (for debugging)
 */
export function decodeToken(token) {
    try {
        const parts = token.split('.');
        if (parts.length !== 3) {
            return null;
        }
        return JSON.parse(base64UrlDecode(parts[1]));
    }
    catch {
        return null;
    }
}
/**
 * Check if token is expired
 */
export function isTokenExpired(token) {
    const payload = decodeToken(token);
    if (!payload || !payload.exp) {
        return true;
    }
    const now = Math.floor(Date.now() / 1000);
    return payload.exp < now;
}
/**
 * Get time until token expires (in seconds)
 */
export function getTokenTTL(token) {
    const payload = decodeToken(token);
    if (!payload || !payload.exp) {
        return 0;
    }
    const now = Math.floor(Date.now() / 1000);
    return Math.max(0, payload.exp - now);
}
/**
 * Refresh a token (generate new one with same payload)
 */
export function refreshToken(token, secret, expiresIn = '24h') {
    const payload = verifyToken(token, secret);
    if (!payload) {
        return null;
    }
    return generateToken({
        sub: payload.sub,
        scopes: payload.scopes,
        type: payload.type,
    }, secret, expiresIn);
}
/**
 * Create access token from API key
 */
export function createAccessToken(keyId, scopes, secret, expiresIn = '1h') {
    return generateToken({
        sub: keyId,
        scopes,
        type: 'api_key',
    }, secret, expiresIn);
}
/**
 * Create user token
 */
export function createUserToken(userId, scopes, secret, expiresIn = '24h') {
    return generateToken({
        sub: userId,
        scopes,
        type: 'user',
    }, secret, expiresIn);
}
//# sourceMappingURL=jwt.js.map