/**
 * Runtime adapter for Cloudflare Workers
 */
import { appConfig } from '../../config/index.js';
import { isUndefinedOrNull } from '../../helper/index.js';
import { Cloudflare } from '../../config/cloudflare.js';
import { Env, getProcessLike } from '../../config/env.js';
import { Logger } from '../../config/logger.js';
import { createMockHttpObjects, ErrorResponse, HttpResponse } from '../RuntimeAdapter.js';
/**
 * Cloudflare Workers adapter for Cloudflare's edge compute platform
 * Uses fetch API and handles D1 database, KV storage bindings
 * Sealed namespace for immutability
 */
export const CloudflareAdapter = Object.freeze({
    /**
     * Create a new Cloudflare adapter instance
     */
    create(config) {
        const workersEnv = Cloudflare.getWorkersEnv();
        if (workersEnv !== null) {
            const processEnv = getProcessLike()?.env;
            const resolveEnvSource = () => {
                if (processEnv === undefined)
                    return workersEnv;
                return { ...processEnv, ...workersEnv };
            };
            Env.setSource(resolveEnvSource);
        }
        const logger = config.logger ?? createDefaultLogger();
        return {
            platform: 'cloudflare',
            async handle(event) {
                return handleCloudflareRequest(this, config, logger, event);
            },
            parseRequest(event) {
                return parseCloudflareRequest(event);
            },
            formatResponse(response) {
                return formatCloudflareResponse(response);
            },
            getLogger() {
                return (logger ?? {
                    debug: (msg) => Logger.debug(`[Cloudflare] ${msg}`),
                    info: (msg) => Logger.info(`[Cloudflare] ${msg}`),
                    warn: (msg) => Logger.warn(`[Cloudflare] ${msg}`),
                    error: (msg, err) => Logger.error(`[Cloudflare] ${msg}`, err?.message),
                });
            },
            supportsPersistentConnections() {
                // Cloudflare Workers isolates don't support persistent connections
                return false;
            },
            getEnvironment() {
                return getCloudflareEnvironment();
            },
        };
    },
    /**
     * Get D1 database binding
     * Usage: adapter.getD1Database()
     */
    getD1Database() {
        // @ts-ignore - Cloudflare Workers environment
        return globalThis.env?.DB ?? null;
    },
    /**
     * Get KV namespace binding for secrets/configuration
     * Usage: adapter.getKV('NAMESPACE_NAME')
     */
    getKV(namespace) {
        // @ts-ignore - Cloudflare Workers environment
        return globalThis.env?.[namespace] ?? null;
    },
});
/**
 * Handle Cloudflare request
 */
async function handleCloudflareRequest(adapter, config, logger, event) {
    try {
        const request = event;
        // Parse incoming request
        const platformRequest = adapter.parseRequest(request);
        // Read request body
        const body = request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : null;
        platformRequest.body = body;
        // Create mock Node.js request/response objects
        const { req, res, responseData } = createMockHttpObjects(platformRequest);
        // Set request timeout
        const timeout = config.timeout ?? 30000;
        const timeoutHandle = setTimeout(() => {
            responseData.statusCode = 504;
            responseData.body = JSON.stringify({
                error: 'Gateway Timeout',
                statusCode: 504,
            });
        }, timeout);
        try {
            // Process request through ZinTrust handler
            await config.handler(req, res, body);
        }
        finally {
            clearTimeout(timeoutHandle);
        }
        // Format response for Cloudflare
        const response = HttpResponse.create();
        response.setStatus(responseData.statusCode);
        response.setHeaders(responseData.headers);
        response.setBody(responseData.body);
        logger?.debug('Cloudflare request processed', {
            statusCode: response.statusCode,
            path: platformRequest.path,
        });
        return response.toResponse();
    }
    catch (error) {
        const err = error;
        Logger.error('Cloudflare handler error', err);
        if (typeof err?.stack === 'string' && err.stack.trim() !== '') {
            Logger.error('Cloudflare handler stack', err.stack);
        }
        const errorResponse = ErrorResponse.create(500, 'Internal Server Error', appConfig.isDevelopment() ? { message: error.message } : undefined);
        return errorResponse.toResponse();
    }
}
function parseCloudflareRequest(event) {
    const url = new URL(event.url);
    const headers = {};
    // Convert Headers object to Record
    event.headers.forEach((value, key) => {
        headers[key.toLowerCase()] = value;
    });
    // Build query object manually to ensure it works
    const query = {};
    for (const [key, value] of url.searchParams.entries()) {
        if (isUndefinedOrNull(query[key])) {
            query[key] = value;
        }
        else if (Array.isArray(query[key])) {
            query[key].push(value);
        }
        else {
            query[key] = [query[key], value];
        }
    }
    return {
        method: event.method.toUpperCase(),
        path: url.pathname,
        headers,
        query,
        remoteAddr: headers['cf-connecting-ip']?.toString() || '0.0.0.0',
        signal: event.signal,
    };
}
/**
 * Format Cloudflare response
 */
function formatCloudflareResponse(response) {
    // Convert to Cloudflare Response format
    const headers = new Headers();
    for (const [key, value] of Object.entries(response.headers)) {
        if (Array.isArray(value)) {
            value.forEach((v) => headers.append(key, v));
        }
        else {
            headers.set(key, value);
        }
    }
    let body = null;
    if (response.body !== null && response.body !== undefined) {
        if (typeof response.body === 'string') {
            body = response.body;
        }
        else if (typeof ReadableStream !== 'undefined' &&
            (response.body instanceof ReadableStream ||
                response.body?.constructor?.name === 'ReadableStream')) {
            body = response.body;
        }
        else {
            body = response.body.toString();
        }
    }
    return new Response(body, {
        status: response.statusCode,
        headers,
    });
}
/**
 * Get Cloudflare environment
 */
function getCloudflareEnvironment() {
    // @ts-ignore - Cloudflare Workers environment
    const env = globalThis.env ?? {};
    return {
        nodeEnv: env.ENVIRONMENT ?? 'production',
        runtime: 'cloudflare',
        dbConnection: env.DB_CONNECTION ?? 'd1', // D1 is Cloudflare's database
        dbHost: undefined, // D1 uses bindings, not host
        dbPort: undefined,
    };
}
function createDefaultLogger() {
    return {
        debug: (msg, data) => Logger.debug(`[Cloudflare] ${msg}`, data !== undefined && data !== null ? JSON.stringify(data) : ''),
        info: (msg, data) => Logger.info(`[Cloudflare] ${msg}`, data !== undefined && data !== null ? JSON.stringify(data) : ''),
        warn: (msg, data) => Logger.warn(`[Cloudflare] ${msg}`, data !== undefined && data !== null ? JSON.stringify(data) : ''),
        error: (msg, err) => Logger.error(`[Cloudflare] ${msg}`, err?.message),
    };
}
