import { ProtocolConversionPipeline } from '../../index.js';
import { buildAdapterContextFromPipeline } from '../../hooks/adapter-context.js';
import { runStandardChatRequestFilters } from '../../../index.js';
import { chatEnvelopeToStandardizedWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js';
import { parseReqInboundFormatEnvelopeWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-edge-stage-semantics.js';
import { mapOpenaiChatToChatWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-semantic-mappers.js';
import { canonicalizeOpenAIChatResponse, convertStandardizedToOpenAIChat as convertCanonicalToOpenAIChat, DEFAULT_OPENAI_ENDPOINT, OPENAI_PROTOCOL } from './shared/openai-chat-helpers.js';
function assertJsonObject(value, stage) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        throw new Error(`OpenAI pipeline codec requires JSON payload at ${stage}`);
    }
    return value;
}
function restoreToolCallIndexes(targetMessages, sourceMessages) {
    if (!Array.isArray(targetMessages) || !Array.isArray(sourceMessages)) {
        return;
    }
    const sourceIndexMap = new Map();
    for (const message of sourceMessages) {
        if (!message || typeof message !== 'object')
            continue;
        const toolCalls = message.tool_calls;
        if (!Array.isArray(toolCalls))
            continue;
        toolCalls.forEach((toolCall) => {
            if (!toolCall || typeof toolCall !== 'object')
                return;
            const id = typeof toolCall.id === 'string'
                ? String(toolCall.id)
                : undefined;
            if (!id || sourceIndexMap.has(id))
                return;
            const idxValue = toolCall.index;
            if (typeof idxValue !== 'number')
                return;
            sourceIndexMap.set(id, idxValue);
        });
    }
    for (const message of targetMessages) {
        if (!message || typeof message !== 'object')
            continue;
        const toolCalls = message.tool_calls;
        if (!Array.isArray(toolCalls))
            continue;
        toolCalls.forEach((toolCall) => {
            if (!toolCall || typeof toolCall !== 'object')
                return;
            const id = typeof toolCall.id === 'string'
                ? String(toolCall.id)
                : undefined;
            if (!id)
                return;
            const sourceIndex = sourceIndexMap.get(id);
            if (sourceIndex === undefined)
                return;
            toolCall.index = sourceIndex;
        });
    }
}
function createOpenAIHooks() {
    return {
        id: 'openai-openai-v2',
        protocol: OPENAI_PROTOCOL,
        inbound: {
            parse: async ({ wire, context }) => {
                const adapterContext = buildAdapterContextFromPipeline(context, {
                    defaultEntryEndpoint: DEFAULT_OPENAI_ENDPOINT,
                    overrideProtocol: OPENAI_PROTOCOL
                });
                const formatEnvelope = parseReqInboundFormatEnvelopeWithNative({
                    rawRequest: wire,
                    protocol: OPENAI_PROTOCOL
                });
                const chatEnvelope = mapOpenaiChatToChatWithNative((formatEnvelope.payload ?? {}), adapterContext);
                const canonical = chatEnvelopeToStandardizedWithNative({
                    chatEnvelope: chatEnvelope,
                    adapterContext: adapterContext,
                    endpoint: adapterContext.entryEndpoint ?? DEFAULT_OPENAI_ENDPOINT,
                    requestId: adapterContext.requestId
                });
                return { canonical: canonical };
            }
        },
        outbound: {
            serialize: async ({ canonical }) => {
                return { payload: assertJsonObject(canonical, 'openai_outbound_serialize') };
            }
        }
    };
}
function buildPipelineContext(profile, context) {
    return {
        requestId: context.requestId,
        entryEndpoint: context.entryEndpoint ?? context.endpoint ?? DEFAULT_OPENAI_ENDPOINT,
        providerProtocol: profile.incomingProtocol ?? context.targetProtocol ?? OPENAI_PROTOCOL,
        targetProtocol: profile.outgoingProtocol ?? context.targetProtocol ?? OPENAI_PROTOCOL,
        profileId: profile.id,
        stream: context.stream,
        metadata: context.metadata
    };
}
export class OpenAIOpenAIPipelineCodec {
    id = 'openai-openai-v2';
    pipeline;
    requestMetaStore = new Map();
    initialized = false;
    constructor() {
        this.pipeline = new ProtocolConversionPipeline(createOpenAIHooks());
    }
    async initialize() {
        this.initialized = true;
    }
    ensureInitialized() {
        if (!this.initialized) {
            throw new Error('OpenAIOpenAIPipelineCodec must be initialized before use');
        }
    }
    stashMeta(requestId, bag) {
        this.requestMetaStore.set(requestId, bag.snapshot());
    }
    consumeMeta(requestId) {
        const stored = this.requestMetaStore.get(requestId);
        if (stored) {
            this.requestMetaStore.delete(requestId);
            return stored;
        }
        return undefined;
    }
    async convertRequest(payload, profile, context) {
        this.ensureInitialized();
        const inboundContext = buildPipelineContext(profile, context);
        const requestId = context.requestId ?? inboundContext.requestId ?? `req_${Date.now()}`;
        inboundContext.requestId = requestId;
        const inboundPayload = assertJsonObject(payload, 'openai_inbound_request');
        const inboundOptions = {
            payload: inboundPayload,
            context: inboundContext
        };
        const inbound = await this.pipeline.convertInbound(inboundOptions);
        this.stashMeta(requestId, inbound.meta);
        const openaiPayload = await convertCanonicalToOpenAIChat(inbound.canonical, inbound.context);
        if (!Array.isArray(openaiPayload.tools) && Array.isArray(inboundPayload.tools)) {
            openaiPayload.tools = inboundPayload.tools;
        }
        if (openaiPayload.tool_choice === undefined &&
            inboundPayload.tool_choice !== undefined) {
            openaiPayload.tool_choice = inboundPayload.tool_choice;
        }
        const filterContext = {
            ...context,
            requestId,
            entryEndpoint: context.entryEndpoint ?? DEFAULT_OPENAI_ENDPOINT,
            endpoint: context.endpoint ?? DEFAULT_OPENAI_ENDPOINT
        };
        const filtered = await runStandardChatRequestFilters(openaiPayload, profile, filterContext);
        if (filtered && typeof filtered === 'object') {
            restoreToolCallIndexes(filtered.messages, inboundPayload.messages);
        }
        return filtered;
    }
    async convertResponse(payload, profile, context) {
        this.ensureInitialized();
        const pipelineContext = buildPipelineContext(profile, context);
        const requestId = context.requestId ?? pipelineContext.requestId ?? `req_${Date.now()}`;
        pipelineContext.requestId = requestId;
        const storedMeta = this.consumeMeta(requestId);
        const sanitized = await canonicalizeOpenAIChatResponse(assertJsonObject(payload, 'openai_chat_response'), context);
        const outboundOptions = {
            canonical: sanitized,
            context: pipelineContext,
            meta: storedMeta
        };
        const outbound = await this.pipeline.convertOutbound(outboundOptions);
        return outbound.payload;
    }
}
