/**
 * Runtime adapter for AWS Lambda
 */
import { Env } from '../../config/env.js';
import { Logger } from '../../config/logger.js';
import { IncomingMessage, ServerResponse, } from '../../node-singletons/http.js';
import { ErrorResponse, HttpResponse } from '../RuntimeAdapter.js';
/**
 * AWS Lambda adapter for API Gateway and ALB events
 * Converts Lambda events to standard HTTP format for ZinTrust framework
 * Sealed namespace for immutability
 */
export const LambdaAdapter = Object.freeze({
    /**
     * Create a new Lambda adapter instance
     */
    create(config) {
        const configuredLoggerProvided = config.logger !== undefined;
        const defaultLogger = configuredLoggerProvided ? undefined : createDefaultLogger();
        const adapter = {
            platform: 'lambda',
            logger: config.logger,
            createMockHttpObjects,
            async handle(event, context) {
                return handleLambdaRequest(adapter, config, event, context);
            },
            parseRequest(event) {
                return parseLambdaRequest(event);
            },
            formatResponse(response) {
                return formatLambdaResponse(response);
            },
            getLogger() {
                if (adapter.logger !== undefined)
                    return adapter.logger;
                if (configuredLoggerProvided === true)
                    return createFallbackLogger();
                return defaultLogger ?? createDefaultLogger();
            },
            supportsPersistentConnections() {
                // Lambda containers persist for warm invocations
                return true;
            },
            getEnvironment() {
                return {
                    nodeEnv: Env.NODE_ENV,
                    runtime: 'lambda',
                    dbConnection: Env.get('DB_CONNECTION', 'sqlite'),
                    dbHost: Env.DB_HOST,
                    dbPort: Env.DB_PORT,
                };
            },
        };
        return adapter;
    },
});
/**
 * Handle Lambda request
 */
async function handleLambdaRequest(adapter, config, event, _context) {
    try {
        const lambdaEvent = event;
        const request = adapter.parseRequest(event);
        // Create mock Node.js request/response objects
        const { req, res, responseData } = (adapter.createMockHttpObjects ?? createMockHttpObjects)(request);
        // Parse body
        const body = parseBody(lambdaEvent);
        // Set request timeout
        const timeout = config.timeout ?? 30000;
        const timeoutHandle = setTimeout(() => {
            res.writeHead(504, { 'Content-Type': 'application/json' });
            res.end(JSON.stringify({
                error: 'Gateway Timeout',
                statusCode: 504,
            }));
        }, timeout);
        try {
            // Process request through ZinTrust handler
            await config.handler(req, res, body);
        }
        finally {
            clearTimeout(timeoutHandle);
        }
        // Extract response
        const response = HttpResponse.create();
        response.setStatus(responseData.statusCode);
        response.setHeaders(responseData.headers);
        response.setBody(responseData.body);
        adapter.getLogger().debug('Lambda request processed', {
            statusCode: response.statusCode,
            path: request.path,
            method: request.method,
        });
        return response.toResponse();
    }
    catch (error) {
        Logger.error('Lambda handler error', error);
        const nodeEnv = typeof Env.NODE_ENV === 'string' && String(Env.NODE_ENV) !== ''
            ? Env.NODE_ENV
            : 'development';
        const includeDetails = nodeEnv === 'development' || String(nodeEnv) === 'dev';
        const errorResponse = ErrorResponse.create(500, 'Internal Server Error', includeDetails ? { message: error.message } : undefined);
        return errorResponse.toResponse();
    }
}
/**
 * Parse Lambda request
 */
function parseLambdaRequest(event) {
    const lambdaEvent = event;
    // Support both API Gateway v1 and v2 formats, plus ALB
    const isV2 = 'requestContext' in lambdaEvent && 'http' in lambdaEvent.requestContext;
    const isAlb = 'requestContext' in lambdaEvent && 'elb' in lambdaEvent.requestContext;
    let requestData;
    if (isV2 === true) {
        requestData = parseV2Request(lambdaEvent);
    }
    else if (isAlb === true) {
        requestData = parseAlbRequest(lambdaEvent);
    }
    else {
        requestData = parseV1Request(lambdaEvent);
    }
    return {
        method: requestData.method.toUpperCase(),
        path: requestData.path,
        headers: normalizeHeaders(requestData.headers),
        body: requestData.body === null ? null : Buffer.from(requestData.body),
        query: requestData.query,
        remoteAddr: requestData.remoteAddr,
    };
}
/**
 * Format Lambda response
 */
function formatLambdaResponse(response) {
    // Lambda expects specific format
    return {
        statusCode: response.statusCode,
        headers: response.headers,
        body: typeof response.body === 'string' ? response.body : response.body?.toString('utf-8') ?? '',
        isBase64Encoded: response.isBase64Encoded ?? false,
    };
}
/**
 * Parse body from Lambda event
 */
function parseBody(event) {
    const body = event.body;
    if (body === null || body === undefined || body === '')
        return null;
    if (event.isBase64Encoded === true && typeof body === 'string') {
        return Buffer.from(body, 'base64');
    }
    return typeof body === 'string' ? Buffer.from(body, 'utf-8') : Buffer.from(body);
}
/**
 * Create mock Node.js request/response objects
 */
function createMockHttpObjects(request) {
    const responseData = {
        statusCode: 200,
        headers: { 'Content-Type': 'application/json' },
        body: null,
    };
    // Create mock request object
    const req = new IncomingMessage(null);
    req.method = request.method;
    req.url = request.path;
    req.headers = request.headers;
    req.remoteAddress =
        request.remoteAddr !== undefined && request.remoteAddr !== '' ? request.remoteAddr : '0.0.0.0';
    // Create mock response object
    const res = new ServerResponse(req);
    res.writeHead = function (statusCode, statusMessageOrHeaders, headers) {
        responseData.statusCode = statusCode;
        const headersObj = typeof statusMessageOrHeaders === 'object' ? statusMessageOrHeaders : headers;
        if (headersObj) {
            Object.assign(responseData.headers, headersObj);
        }
        return res;
    };
    res.end = function (chunk, _encodingOrCb, _cb) {
        if (typeof chunk === 'function') {
            chunk();
        }
        else if (chunk !== null && chunk !== undefined) {
            responseData.body = chunk;
        }
        return res;
    };
    res.write = function (chunk) {
        responseData.body = chunk;
        return true;
    };
    return { req, res, responseData };
}
/**
 * Get remote address for API Gateway v2
 */
function getRemoteAddrV2(event, headers) {
    if (event.requestContext.http.sourceIp !== '') {
        return event.requestContext.http.sourceIp;
    }
    const forwarded = headers['x-forwarded-for']?.toString().split(',')[0];
    if (forwarded !== undefined && forwarded !== '') {
        return forwarded;
    }
    return '0.0.0.0';
}
/**
 * Get remote address for ALB
 */
function getRemoteAddrAlb(headers) {
    const forwarded = headers['x-forwarded-for']?.toString().split(',')[0];
    if (forwarded !== undefined && forwarded !== '') {
        return forwarded;
    }
    const realIp = headers['x-real-ip']?.toString();
    if (realIp !== undefined && realIp !== '') {
        return realIp;
    }
    return '0.0.0.0';
}
/**
 * Get remote address for API Gateway v1
 */
function getRemoteAddrV1(headers) {
    const forwardedCap = headers['X-Forwarded-For']?.toString().split(',')[0];
    if (forwardedCap !== undefined && forwardedCap !== '') {
        return forwardedCap;
    }
    const forwarded = headers['x-forwarded-for']?.toString().split(',')[0];
    if (forwarded !== undefined && forwarded !== '') {
        return forwarded;
    }
    return '0.0.0.0';
}
function parseHttpStyleRequest(event, resolveRemoteAddr) {
    const headers = event.headers ?? {};
    return createParsedLambdaRequest({
        method: event.httpMethod,
        path: event.path,
        headers,
        query: event.queryStringParameters ?? {},
        body: event.body ?? null,
        remoteAddr: resolveRemoteAddr(headers),
    });
}
/**
 * Parse API Gateway v2 request
 */
function parseV2Request(event) {
    const headers = event.headers ?? {};
    return {
        method: event.requestContext.http.method,
        path: event.rawPath,
        headers,
        query: event.queryStringParameters ?? {},
        body: event.body ?? null,
        remoteAddr: getRemoteAddrV2(event, headers),
    };
}
/**
 * Parse ALB request
 */
function parseAlbRequest(event) {
    return parseHttpStyleRequest(event, getRemoteAddrAlb);
}
/**
 * Parse API Gateway v1 request
 */
function parseV1Request(event) {
    return parseHttpStyleRequest(event, getRemoteAddrV1);
}
function createParsedLambdaRequest(request) {
    return request;
}
/**
 * Normalize headers to lowercase
 */
function normalizeHeaders(headers) {
    const normalized = {};
    for (const [key, value] of Object.entries(headers)) {
        normalized[key.toLowerCase()] = value;
    }
    return normalized;
}
function createDefaultLogger() {
    return {
        debug: (msg, data) => Logger.debug(`[Lambda] ${msg}`, data === undefined ? '' : JSON.stringify(data)),
        info: (msg, data) => Logger.info(`[Lambda] ${msg}`, data === undefined ? '' : JSON.stringify(data)),
        warn: (msg, data) => Logger.warn(`[Lambda] ${msg}`, data === undefined ? '' : JSON.stringify(data)),
        error: (msg, err) => Logger.error(`[Lambda] ${msg}`, err?.message),
    };
}
function createFallbackLogger() {
    return {
        debug: (msg) => Logger.debug(`[Lambda] ${msg}`),
        info: (msg) => Logger.info(`[Lambda] ${msg}`),
        warn: (msg) => Logger.warn(`[Lambda] ${msg}`),
        error: (msg, err) => Logger.error(`[Lambda] ${msg}`, err?.message),
    };
}
