const DEFAULT_ENDPOINT = '/v1/chat/completions';
function isJsonObject(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function asJsonObject(value) {
    return isJsonObject(value) ? value : undefined;
}
function readString(value) {
    return typeof value === 'string' ? value : undefined;
}
function generateRequestId() {
    return `req_${Date.now()}_${Math.floor(Math.random() * 1_000_000)}`;
}
function normalizeEndpoint(candidate) {
    if (candidate && candidate.trim()) {
        return candidate.trim();
    }
    return DEFAULT_ENDPOINT;
}
function inferProviderType(protocol) {
    const value = (protocol ?? '').toLowerCase();
    if (value.includes('anthropic')) {
        return 'anthropic';
    }
    if (value.includes('responses')) {
        return 'responses';
    }
    if (value.includes('gemini')) {
        return 'gemini';
    }
    return 'openai';
}
function buildContext(input) {
    const envelope = input ?? {};
    const route = asJsonObject(envelope.route);
    const metadata = { ...(asJsonObject(envelope.metadata) ?? {}) };
    const rootEntry = readString(envelope.entryEndpoint);
    const routeId = readString(route?.requestId);
    const dataWrapper = asJsonObject(envelope.data);
    const dataEntry = readString(dataWrapper?.entryEndpoint);
    const dataMeta = asJsonObject(dataWrapper?.metadata);
    const metaEntry = readString(metadata.entryEndpoint);
    const metaEndpoint = readString(metadata.endpoint);
    const dataMetaEntry = readString(dataMeta?.entryEndpoint);
    const endpoint = metaEndpoint ??
        metaEntry ??
        dataMetaEntry ??
        dataEntry ??
        rootEntry ??
        DEFAULT_ENDPOINT;
    const entryEndpoint = metaEntry ??
        dataEntry ??
        dataMetaEntry ??
        rootEntry ??
        endpoint;
    const requestId = routeId ?? readString(metadata.requestId) ?? generateRequestId();
    metadata.requestId = requestId;
    metadata.endpoint = endpoint;
    metadata.entryEndpoint = entryEndpoint;
    return {
        requestId,
        endpoint: normalizeEndpoint(endpoint),
        entryEndpoint: normalizeEndpoint(entryEndpoint),
        metadata
    };
}
function extractPayload(subject) {
    if (!subject) {
        return {};
    }
    if ('data' in subject && subject.data !== undefined) {
        return subject.data;
    }
    if ('payload' in subject && subject.payload !== undefined) {
        return subject.payload;
    }
    return subject;
}
function buildConversionEnvelope(payload, ctx, options, overrides) {
    const endpoint = normalizeEndpoint(overrides?.endpoint ??
        options?.entryEndpoint ??
        ctx.entryEndpoint ??
        ctx.endpoint);
    const metadata = { ...ctx.metadata };
    metadata.entryEndpoint = ctx.entryEndpoint || endpoint;
    if (options?.processMode) {
        metadata.processMode = options.processMode;
    }
    if (overrides?.metadata) {
        Object.assign(metadata, overrides.metadata);
    }
    const providerProtocol = options?.providerProtocol ?? 'openai-chat';
    const providerType = options?.providerType ?? inferProviderType(providerProtocol);
    const streamSetting = metadata.stream;
    const streamingFormat = streamSetting === true ? 'sse' : 'json';
    return {
        id: ctx.requestId || generateRequestId(),
        timestamp: Date.now(),
        endpoint,
        payload,
        metadata,
        options: {
            providerProtocol,
            providerType,
            processMode: options?.processMode,
            streamingFormat,
            stage: overrides?.stage ?? options?.stage,
            direction: overrides?.direction ?? options?.direction
        }
    };
}
function prepareReturnPayload(envelope) {
    return {
        data: envelope.payload,
        metadata: {
            ...envelope.metadata,
            conversionId: envelope.id,
            conversionEndpoint: envelope.endpoint,
            conversionStage: envelope.options.stage
        }
    };
}
class CoreAdapter {
    options;
    lastRequests = new Map();
    constructor(opts) {
        this.options = opts;
    }
    mergeOptions(opts) {
        if (opts) {
            this.options = { ...(this.options ?? {}), ...opts };
        }
    }
    async processInboundRequest(request, opts) {
        this.mergeOptions(opts);
        const ctx = buildContext(request);
        const body = extractPayload(request);
        const entryEndpoint = opts?.entryEndpoint ?? ctx.entryEndpoint ?? ctx.endpoint;
        const result = prepareReturnPayload(buildConversionEnvelope(body, ctx, { ...this.options, stage: 'inbound', direction: 'request' }, {
            metadata: { direction: 'request' },
            endpoint: entryEndpoint,
            stage: 'inbound',
            direction: 'request'
        }));
        this.lastRequests.set(ctx.requestId, body);
        return result;
    }
    async processInboundResponse(response, opts) {
        this.mergeOptions(opts);
        const ctx = buildContext(response);
        const body = extractPayload(response);
        const entryEndpoint = opts?.entryEndpoint ?? ctx.entryEndpoint ?? ctx.endpoint;
        const targetEndpoint = entryEndpoint.includes('#response')
            ? entryEndpoint
            : `${entryEndpoint}#response`;
        return prepareReturnPayload(buildConversionEnvelope(body, ctx, { ...this.options, stage: 'inbound', direction: 'response' }, {
            metadata: { direction: 'response' },
            endpoint: targetEndpoint,
            stage: 'inbound',
            direction: 'response'
        }));
    }
    async processOutboundRequest(request, opts) {
        this.mergeOptions(opts);
        const ctx = buildContext(request);
        const body = extractPayload(request);
        const entryEndpoint = opts?.entryEndpoint ?? ctx.entryEndpoint ?? ctx.endpoint;
        return prepareReturnPayload(buildConversionEnvelope(body, ctx, { ...this.options, stage: 'outbound', direction: 'request' }, {
            metadata: { direction: 'request' },
            endpoint: entryEndpoint,
            stage: 'outbound',
            direction: 'request'
        }));
    }
    async processOutboundResponse(response, opts) {
        this.mergeOptions(opts);
        const ctx = buildContext(response);
        const body = extractPayload(response);
        const entryEndpoint = opts?.entryEndpoint ?? ctx.entryEndpoint ?? ctx.endpoint;
        const targetEndpoint = entryEndpoint.includes('#response')
            ? entryEndpoint
            : `${entryEndpoint}#response`;
        const result = prepareReturnPayload(buildConversionEnvelope(body, ctx, { ...this.options, stage: 'outbound', direction: 'response' }, {
            endpoint: targetEndpoint,
            metadata: {
                direction: 'response',
                originalRequest: this.lastRequests.get(ctx.requestId) ?? {}
            },
            stage: 'outbound',
            direction: 'response'
        }));
        return result;
    }
    async processIncoming(request, opts) {
        return this.processInboundRequest(request, opts);
    }
    async processOutgoing(response, opts) {
        const dto = await this.processOutboundResponse(response, opts);
        return dto;
    }
}
const singleton = new CoreAdapter();
export async function processIncoming(request, options) {
    return singleton.processIncoming(request, options);
}
export async function processOutgoing(response, options) {
    return singleton.processOutgoing(response, options);
}
export async function processInboundRequest(request, options) {
    return singleton.processInboundRequest(request, options);
}
export async function processInboundResponse(response, options) {
    return singleton.processInboundResponse(response, options);
}
export async function processOutboundRequest(request, options) {
    return singleton.processOutboundRequest(request, options);
}
export async function processOutboundResponse(response, options) {
    const dto = await singleton.processOutboundResponse(response, options);
    return dto;
}
export default {
    processIncoming,
    processOutgoing,
    processInboundRequest,
    processInboundResponse,
    processOutboundRequest,
    processOutboundResponse
};
