import { Env } from '../config/env.js';
import { Logger } from '../config/logger.js';
import * as crypto from '../node-singletons/crypto.js';
/**
 * API Key Authentication
 */
export const ApiKeyAuth = Object.freeze({
    /**
     * Create a new API key auth instance
     */
    create(apiKey) {
        const generateSecureApiKeyDefault = () => {
            return crypto.randomBytes(32).toString('hex');
        };
        const getApiKey = () => {
            const envKey = Env.get('SERVICE_API_KEY');
            const secureDefault = generateSecureApiKeyDefault();
            if (envKey === undefined || envKey === secureDefault) {
                Logger.warn('⚠️  WARNING: Using generated default API key. Set SERVICE_API_KEY environment variable in production!');
                return secureDefault;
            }
            return envKey;
        };
        const key = apiKey ?? getApiKey();
        return {
            verify(token) {
                return token === key;
            },
            generate() {
                return crypto.randomBytes(32).toString('hex');
            },
        };
    },
});
/**
 * JWT Authentication
 */
export const JwtAuth = Object.freeze({
    /**
     * Create a new JWT auth instance
     */
    create(secret) {
        const generateSecureJwtDefault = () => {
            return crypto.randomBytes(32).toString('hex');
        };
        const getJwtSecret = () => {
            const envSecret = Env.get('SERVICE_JWT_SECRET');
            const secureDefault = generateSecureJwtDefault();
            if (envSecret === undefined || envSecret === secureDefault) {
                Logger.warn('⚠️  WARNING: Using generated default JWT secret. Set SERVICE_JWT_SECRET environment variable in production!');
                return secureDefault;
            }
            return envSecret;
        };
        const jwtSecret = secret ?? getJwtSecret();
        return {
            sign(payload, _expiresIn = '1h') {
                // Simplified JWT (in production, use jsonwebtoken library)
                const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64');
                const body = Buffer.from(JSON.stringify({ ...payload, iat: Date.now() })).toString('base64');
                const signature = crypto
                    .createHmac('sha256', jwtSecret)
                    .update(`${header}.${body}`)
                    .digest('base64');
                return `${header}.${body}.${signature}`;
            },
            verify(token) {
                try {
                    const parts = token.split('.');
                    if (parts.length !== 3)
                        return null;
                    const [header, body, signature] = parts;
                    const expectedSignature = crypto
                        .createHmac('sha256', jwtSecret)
                        .update(`${header}.${body}`)
                        .digest('base64');
                    if (signature !== expectedSignature) {
                        return null;
                    }
                    return JSON.parse(Buffer.from(body, 'base64').toString());
                }
                catch (error) {
                    Logger.error('JWT verification failed', error);
                    return null;
                }
            },
        };
    },
});
/**
 * Custom Authentication (developer-defined)
 */
export const CustomAuth = Object.freeze({
    /**
     * Create a new custom auth instance
     */
    create(validator) {
        return {
            verify(token) {
                return validator(token);
            },
        };
    },
});
/**
 * Create initial authentication context
 */
const createAuthContext = (strategy) => {
    return {
        isServiceCall: false,
        strategy,
        authenticated: strategy === 'none',
    };
};
/**
 * Attach context to request and proceed to next middleware
 */
const attachContextAndNext = (req, context, next) => {
    req.context ??= {};
    req.context['serviceAuth'] = context;
    return next();
};
/**
 * Finalize successful service authentication
 */
const finalizeServiceAuth = (req, context, next) => {
    context.authenticated = true;
    context.isServiceCall = true;
    return attachContextAndNext(req, context, next);
};
/**
 * Parse authorization header
 */
const parseAuthHeader = (req) => {
    const authHeader = req.getHeader('authorization');
    if (authHeader === undefined || typeof authHeader !== 'string') {
        return null;
    }
    const parts = authHeader.split(' ');
    if (parts.length !== 2) {
        return null;
    }
    const scheme = parts[0];
    const token = parts[1];
    if (!scheme || !token) {
        return null;
    }
    return { scheme, token };
};
/**
 * Verify API Key strategy
 */
const verifyApiKeyStrategy = (apiKeyAuth, scheme, token) => {
    if (scheme !== 'Bearer' || apiKeyAuth.verify(token) === false) {
        return { authenticated: false, status: 403, error: 'Invalid API key' };
    }
    return { authenticated: true };
};
/**
 * Verify JWT strategy
 */
const verifyJwtStrategy = (jwtAuth, scheme, token, context) => {
    if (scheme !== 'Bearer') {
        return {
            authenticated: false,
            status: 401,
            error: 'Invalid authorization scheme',
        };
    }
    const payload = jwtAuth.verify(token);
    if (payload === null) {
        return { authenticated: false, status: 403, error: 'Invalid JWT token' };
    }
    const serviceName = payload['serviceName'];
    context.serviceName = typeof serviceName === 'string' ? serviceName : '';
    return { authenticated: true };
};
/**
 * Verify Custom strategy
 */
const verifyCustomStrategy = (customValidator, token) => {
    if (customValidator === null || customValidator(token) === false) {
        return { authenticated: false, status: 403, error: 'Authentication failed' };
    }
    return { authenticated: true };
};
/**
 * Verify authentication strategy
 */
const verifyStrategy = (strategy, auth, context, deps) => {
    switch (strategy) {
        case 'api-key':
            return verifyApiKeyStrategy(deps.apiKeyAuth, auth.scheme, auth.token);
        case 'jwt':
            return verifyJwtStrategy(deps.jwtAuth, auth.scheme, auth.token, context);
        case 'custom':
            return verifyCustomStrategy(deps.customValidator, auth.token);
        default:
            return { authenticated: false, status: 401, error: 'Unsupported strategy' };
    }
};
const createServiceAuthMiddleware = () => {
    let defaultApiKeyAuth = null;
    let defaultJwtAuth = null;
    let customValidator = null;
    const getApiKeyAuth = () => {
        defaultApiKeyAuth ??= ApiKeyAuth.create();
        return defaultApiKeyAuth;
    };
    const getJwtAuth = () => {
        defaultJwtAuth ??= JwtAuth.create();
        return defaultJwtAuth;
    };
    return {
        /**
         * Register custom auth validator
         */
        registerCustomAuth(validator) {
            customValidator = validator;
        },
        /**
         * Middleware to authenticate service-to-service calls
         */
        middleware(strategy) {
            return async (req, res, next) => {
                const context = createAuthContext(strategy);
                if (strategy === 'none') {
                    return attachContextAndNext(req, context, next);
                }
                const auth = parseAuthHeader(req);
                if (auth === null) {
                    return res.setStatus(401).json({ error: 'Missing or invalid authorization header' });
                }
                const result = verifyStrategy(strategy, auth, context, {
                    apiKeyAuth: getApiKeyAuth(),
                    jwtAuth: getJwtAuth(),
                    customValidator,
                });
                if (!result.authenticated) {
                    return res.setStatus(result.status ?? 401).json({ error: result.error });
                }
                return finalizeServiceAuth(req, context, next);
            };
        },
    };
};
export const ServiceAuthMiddleware = Object.freeze(createServiceAuthMiddleware());
export default ServiceAuthMiddleware;
