import { Logger } from '../config/logger.js';
import { securityConfig } from '../config/security.js';
import { RequestContext } from '../http/RequestContext.js';
import { respondWithMiddlewareFailure, } from './MiddlewareFailureResponder.js';
import { JwtManager } from '../security/JwtManager.js';
import { JwtSessions } from '../security/JwtSessions.js';
const getJwtFailureReason = (error) => {
    if (typeof error !== 'object' || error === null)
        return 'invalid_token';
    const candidate = error;
    if (candidate.name === 'TokenExpiredError' || candidate.code === 'TOKEN_EXPIRED') {
        return 'expired_token';
    }
    return 'invalid_token';
};
const getHeaderValue = (value) => {
    if (Array.isArray(value))
        return typeof value[0] === 'string' ? value[0] : '';
    return typeof value === 'string' ? value : '';
};
const getBearerToken = (authorizationHeader) => {
    const trimmed = authorizationHeader.trim();
    if (trimmed === '')
        return null;
    const [scheme, ...rest] = trimmed.split(/\s+/);
    if (typeof scheme !== 'string' || scheme.toLowerCase() !== 'bearer')
        return null;
    const token = rest.join(' ').trim();
    if (token === '')
        return null;
    return token;
};
const getOptionalStringOrNumberClaim = (payload, key) => {
    const value = payload[key];
    if (typeof value === 'string')
        return value;
    if (typeof value === 'number')
        return String(value);
    return undefined;
};
const createJwtFailureBody = (reason, message) => ({
    error: {
        code: reason,
        message,
    },
});
const respondJwtUnauthorized = async (req, res, onUnauthorized, reason, message, error) => {
    await respondWithMiddlewareFailure(req, res, onUnauthorized, {
        middleware: 'jwt',
        reason,
        statusCode: 401,
        message,
        body: createJwtFailureBody(reason, message),
        error,
    });
};
const hydrateJwtRequestContext = (req, payload) => {
    req.user = payload;
    const subject = payload['sub'];
    if (typeof subject === 'string' && subject.trim() !== '') {
        RequestContext.setUserId(req, subject);
    }
    const tenantId = getOptionalStringOrNumberClaim(payload, 'tenantId') ??
        getOptionalStringOrNumberClaim(payload, 'tenant_id');
    if (tenantId !== undefined && tenantId.trim() !== '') {
        RequestContext.setTenantId(req, tenantId);
    }
};
const createJwtMiddlewareHandler = (jwt, algorithm, onUnauthorized) => {
    return async (req, res, next) => {
        if (req.context?.['authStrategy'] === 'bulletproof' && req.user !== undefined) {
            await next();
            return;
        }
        const authorizationHeader = getHeaderValue(req.getHeader('authorization'));
        if (authorizationHeader === '') {
            await respondJwtUnauthorized(req, res, onUnauthorized, 'missing_authorization_header', 'Missing authorization header');
            return;
        }
        const token = getBearerToken(authorizationHeader);
        if (token === null) {
            await respondJwtUnauthorized(req, res, onUnauthorized, 'invalid_authorization_header_format', 'Invalid authorization header format');
            return;
        }
        try {
            const payload = jwt.verify(token, algorithm);
            if (!(await JwtSessions.isActive(token))) {
                await respondJwtUnauthorized(req, res, onUnauthorized, 'inactive_session', 'Invalid or expired token');
                return;
            }
            hydrateJwtRequestContext(req, payload);
            await next();
        }
        catch (error) {
            Logger.debug('JWT verification failed', {
                algorithm,
                error: error instanceof Error ? error.message : String(error),
            });
            await respondJwtUnauthorized(req, res, onUnauthorized, getJwtFailureReason(error), 'Invalid or expired token', error);
        }
    };
};
export const JwtAuthMiddleware = Object.freeze({
    create(options = {}) {
        const algorithm = options.algorithm ?? securityConfig.jwt.algorithm;
        const secret = options.secret ?? securityConfig.jwt.secret;
        const jwt = JwtManager.create();
        if (algorithm === 'HS256' || algorithm === 'HS512') {
            jwt.setHmacSecret(secret);
        }
        return createJwtMiddlewareHandler(jwt, algorithm, options.onUnauthorized);
    },
});
export default JwtAuthMiddleware;
