import { ensureBridgeInstructions } from '../bridge-instructions.js';
import { evaluateResponsesHostPolicy } from './responses-host-policy.js';
import { convertBridgeInputToChatMessages } from '../bridge-message-utils.js';
import { createToolCallIdTransformer, enforceToolCallIdStyle, sanitizeResponsesFunctionName } from '../shared/responses-tool-utils.js';
import { mapChatToolsToBridge } from '../shared/tool-mapping.js';
import { ProviderProtocolError } from '../provider-protocol-error.js';
import { isJsonObject, jsonClone } from '../hub/types/json.js';
import { captureReqInboundResponsesContextSnapshotWithNative, mapReqInboundBridgeToolsToChatWithNative } from '../../router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js';
import { appendLocalImageBlockOnLatestUserInputWithNative, buildBridgeHistoryWithNative, filterBridgeInputForUpstreamWithNative, normalizeBridgeHistorySeedWithNative, prepareResponsesRequestEnvelopeWithNative, resolveResponsesRequestBridgeDecisionsWithNative, resolveResponsesBridgeToolsWithNative, runBridgeActionPipelineWithNative } from '../../router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js';
// --- Utilities (ported strictly) ---
import { resolveBridgePolicy, resolvePolicyActions } from '../bridge-policies.js';
import { logHubStageTiming } from '../hub/pipeline/hub-stage-timing.js';
function isObject(v) {
    return !!v && typeof v === 'object' && !Array.isArray(v);
}
function readCapturedToolResults(context) {
    const raw = context.__captured_tool_results;
    return Array.isArray(raw)
        ? raw.filter((entry) => Boolean(entry && typeof entry === 'object' && !Array.isArray(entry)))
        : undefined;
}
function runNativeResponsesBridgePipeline(input) {
    const output = runBridgeActionPipelineWithNative({
        stage: input.stage,
        actions: input.actions,
        protocol: input.protocol,
        moduleType: input.moduleType,
        requestId: input.requestId,
        state: {
            messages: input.messages,
            ...(Array.isArray(input.capturedToolResults) ? { capturedToolResults: input.capturedToolResults } : {}),
            ...(input.rawRequest ? { rawRequest: input.rawRequest } : {})
        }
    });
    return {
        messages: Array.isArray(output?.messages) ? output.messages : input.messages,
        metadata: output?.metadata && typeof output.metadata === 'object' && !Array.isArray(output.metadata)
            ? output.metadata
            : undefined
    };
}
function filterRedundantResponsesReasoningAction(actions) {
    return actions?.filter((action) => {
        const name = typeof action?.name === 'string' ? action.name.trim().toLowerCase() : '';
        return name !== 'reasoning.extract';
    });
}
// normalizeTools unified in ../args-mapping.ts
// NOTE: 自修复提示已移除（统一标准：不做模糊兜底）。
// --- Public bridge functions ---
export function captureResponsesContext(payload, dto) {
    const preservedInput = Array.isArray(payload.input)
        ? payload.input
        : undefined;
    ensureBridgeInstructions(payload);
    const captured = captureReqInboundResponsesContextSnapshotWithNative({
        rawRequest: payload,
        requestId: dto?.route?.requestId,
        toolCallIdStyle: payload?.toolCallIdStyle ?? payload?.metadata?.toolCallIdStyle
    });
    const instructionReasoning = payload?.__rcc_reasoning_instructions;
    if (instructionReasoning !== undefined && instructionReasoning !== null) {
        const segments = Array.isArray(instructionReasoning) ? instructionReasoning : [instructionReasoning];
        const normalized = segments
            .map((entry) => (typeof entry === 'string' ? entry.trim() : String(entry ?? '').trim()))
            .filter((entry) => entry.length > 0);
        if (normalized.length) {
            captured.__rcc_reasoning_instructions_segments = normalized;
        }
    }
    if (preservedInput && (!Array.isArray(captured.input) || captured.input.length === 0)) {
        captured.input = preservedInput;
    }
    if (!captured.systemInstruction && typeof payload.instructions === 'string' && payload.instructions.trim().length) {
        captured.systemInstruction = payload.instructions;
    }
    if (captured.metadata && isJsonObject(captured.metadata)) {
        const cloned = jsonClone(captured.metadata);
        delete cloned.extraFields;
        captured.metadata = cloned;
    }
    return captured;
}
export function buildChatRequestFromResponses(payload, context) {
    const requestId = typeof context.requestId === 'string' && context.requestId.trim().length
        ? context.requestId
        : 'unknown';
    // V3: 对 Responses 路径仅做“形状转换”，不做参数解析/修复。
    // 将顶层 { type,name,description,parameters,strict } 归一为 OpenAI Chat tools 形状：
    // { type:'function', function:{ name,description,parameters,strict? } }
    const toolsNormalized = Array.isArray(context.toolsNormalized) && context.toolsNormalized.length
        ? context.toolsNormalized
        : mapReqInboundBridgeToolsToChatWithNative(payload.tools);
    // 不在 Responses 路径进行 MCP 工具注入；统一由 Chat 后半段治理注入
    logHubStageTiming(requestId, 'req_inbound.responses.convert_input_to_messages', 'start');
    const convertStart = Date.now();
    let messages = convertBridgeInputToChatMessages({
        input: context.input,
        tools: toolsNormalized,
        normalizeFunctionName: 'responses',
        toolResultFallbackText: 'Command succeeded (no output).'
    });
    logHubStageTiming(requestId, 'req_inbound.responses.convert_input_to_messages', 'completed', {
        elapsedMs: Date.now() - convertStart,
        forceLog: true
    });
    try {
        const bridgePolicy = resolveBridgePolicy({ protocol: 'openai-responses', moduleType: 'openai-responses' });
        const policyActions = filterRedundantResponsesReasoningAction(resolvePolicyActions(bridgePolicy, 'request_inbound'));
        if (policyActions?.length) {
            logHubStageTiming(requestId, 'req_inbound.responses.inbound_policy', 'start');
            const policyStart = Date.now();
            const actionState = runNativeResponsesBridgePipeline({
                stage: 'request_inbound',
                actions: policyActions,
                protocol: (bridgePolicy?.protocol ?? 'openai-responses'),
                moduleType: (bridgePolicy?.moduleType ?? 'openai-responses'),
                requestId: context.requestId,
                messages,
                capturedToolResults: readCapturedToolResults(context),
                rawRequest: payload
            });
            messages = actionState.messages;
            logHubStageTiming(requestId, 'req_inbound.responses.inbound_policy', 'completed', {
                elapsedMs: Date.now() - policyStart,
                forceLog: true
            });
        }
    }
    catch {
        // Policy application is best-effort; fall back to raw mapping on failure.
    }
    if (Array.isArray(context.originalSystemMessages) && context.originalSystemMessages.length) {
        const preservedSystems = context.originalSystemMessages
            .map(text => ({ role: 'system', content: text }))
            .filter(message => typeof message.content === 'string');
        if (preservedSystems.length) {
            const nonSystemMessages = messages.filter((msg) => String(msg?.role).toLowerCase() !== 'system');
            messages = [...preservedSystems, ...nonSystemMessages];
        }
    }
    messages = appendLocalImageBlockOnLatestUserInputWithNative({ messages }).messages;
    // 不在 Responses 路径做工具治理；统一在 Chat 后半段处理
    // No system tips for MCP on OpenAI Responses path (avoid leaking tool names)
    if (!messages.length) {
        throw new ProviderProtocolError('Responses payload produced no chat messages', {
            code: 'MALFORMED_REQUEST',
            protocol: 'openai-responses',
            providerType: 'responses',
            details: {
                context: 'buildChatRequestFromResponses',
                inputLength: Array.isArray(context.input) ? context.input.length : undefined,
                requestId: context.requestId
            }
        });
    }
    // 如果只有 system 消息且无 user/assistant/tool，后续桥接 action 会从 instructions 注入兜底 user 消息
    const result = { model: payload.model, messages };
    if (typeof payload.stream === 'boolean') {
        result.stream = payload.stream;
    }
    for (const key of ['top_p', 'tool_choice', 'parallel_tool_calls', 'user', 'logit_bias', 'seed', 'response_format']) {
        if (payload[key] !== undefined)
            result[key] = payload[key];
    }
    if (typeof payload.max_tokens === 'number' && payload.max_output_tokens === undefined) {
        payload.max_output_tokens = payload.max_tokens;
    }
    if (payload.max_output_tokens !== undefined) {
        result.max_output_tokens = payload.max_output_tokens;
    }
    if (Array.isArray(toolsNormalized) && toolsNormalized.length)
        result.tools = toolsNormalized;
    return { request: result, toolsNormalized };
}
/**
 * Chat 请求 → Responses 请求（非流），用于 V3 process=chat 且 providerWire=responses 的请求编码。
 *
 * 设计目标：
 *  - 保留 model / tools / tool_choice / parallel_tool_calls 等字段；
 *  - 将 system 消息折叠到 instructions；
 *  - 将 user/assistant/tool 消息编码为 input[] 中的 message 块，使得 mapResponsesInputToChat 能够还原为等价 Chat 请求。
 */
function normalizeBridgeHistory(seed) {
    if (!seed || typeof seed !== 'object' || Array.isArray(seed)) {
        return undefined;
    }
    return normalizeBridgeHistorySeedWithNative(seed);
}
export function buildResponsesRequestFromChat(payload, ctx, extras) {
    const chat = unwrapData(payload);
    const out = {};
    const envelopeMetadata = ctx?.metadata && typeof ctx.metadata === 'object' ? ctx.metadata : undefined;
    const requestMetadata = chat && typeof chat === 'object' && chat.metadata && typeof chat.metadata === 'object'
        ? chat.metadata
        : undefined;
    // 基本字段
    out.model = chat.model;
    let messages = Array.isArray(chat.messages) ? chat.messages : [];
    let bridgeMetadata;
    try {
        const bridgePolicy = resolveBridgePolicy({ protocol: 'openai-responses', moduleType: 'openai-responses' });
        const policyActions = filterRedundantResponsesReasoningAction(resolvePolicyActions(bridgePolicy, 'request_outbound'));
        if (policyActions?.length) {
            const actionState = runNativeResponsesBridgePipeline({
                stage: 'request_outbound',
                actions: policyActions,
                protocol: (bridgePolicy?.protocol ?? 'openai-responses'),
                moduleType: (bridgePolicy?.moduleType ?? 'openai-responses'),
                requestId: ctx?.requestId,
                messages,
                rawRequest: chat
            });
            messages = actionState.messages;
            if (actionState.metadata && Object.keys(actionState.metadata).length) {
                bridgeMetadata = actionState.metadata;
            }
        }
    }
    catch {
        // ignore policy errors
    }
    const metadataExtraFields = extractMetadataExtraFields(envelopeMetadata);
    const bridgeDecisions = resolveResponsesRequestBridgeDecisionsWithNative({
        context: ctx && typeof ctx === 'object' ? ctx : undefined,
        requestMetadata,
        envelopeMetadata,
        bridgeMetadata,
        extraBridgeHistory: extras?.bridgeHistory
    });
    const forceWebSearch = bridgeDecisions.forceWebSearch === true;
    const toolCallIdStyle = bridgeDecisions.toolCallIdStyle;
    const historySeed = bridgeDecisions.historySeed;
    // tools: 反向映射为 ResponsesToolDefinition 形状
    const chatTools = Array.isArray(chat.tools) ? chat.tools : [];
    const responsesToolsFromChat = mapChatToolsToBridge(chatTools, {
        sanitizeName: sanitizeResponsesFunctionName
    });
    const originalTools = Array.isArray(ctx?.toolsRaw) ? ctx.toolsRaw : undefined;
    const resolvedBridgeTools = resolveResponsesBridgeToolsWithNative({
        originalTools: Array.isArray(originalTools) ? originalTools : undefined,
        chatTools: Array.isArray(responsesToolsFromChat) ? responsesToolsFromChat : undefined,
        hasServerSideWebSearch: !forceWebSearch,
        passthroughKeys: [
            'temperature',
            'tool_choice',
            'parallel_tool_calls',
            'response_format',
            'user',
            'top_p',
            'prompt_cache_key',
            'reasoning',
            'logit_bias',
            'seed'
        ],
        request: chat
    });
    const mergedTools = resolvedBridgeTools.mergedTools;
    if (mergedTools?.length) {
        out.tools = mergedTools;
    }
    if (resolvedBridgeTools.request && typeof resolvedBridgeTools.request === 'object') {
        for (const [key, value] of Object.entries(resolvedBridgeTools.request)) {
            if (out[key] === undefined) {
                out[key] = value;
            }
        }
    }
    const history = historySeed ??
        buildBridgeHistoryWithNative({
            messages,
            tools: Array.isArray(out.tools) ? out.tools : undefined
        });
    const callIdTransformer = createToolCallIdTransformer(toolCallIdStyle);
    if (callIdTransformer) {
        enforceToolCallIdStyle(history.input, callIdTransformer);
    }
    const { input, combinedSystemInstruction, originalSystemMessages } = history;
    // 不追加 metadata，以便 roundtrip 与原始 payload 对齐；系统提示直接写入 instructions。
    const upstreamInput = filterBridgeInputForUpstreamWithNative({
        input,
        allowToolCallId: toolCallIdStyle === 'preserve'
    }).input;
    if (upstreamInput.length) {
        out.input = upstreamInput;
    }
    const streamFromChat = typeof chat.stream === 'boolean' ? chat.stream : undefined;
    const streamFromParameters = chat?.parameters && typeof chat.parameters?.stream === 'boolean'
        ? chat.parameters.stream
        : undefined;
    const stripHostFields = shouldStripHostManagedFields(ctx);
    const preparedEnvelope = prepareResponsesRequestEnvelopeWithNative({
        request: out,
        contextSystemInstruction: ctx?.systemInstruction,
        extraSystemInstruction: extras?.systemInstruction,
        metadataSystemInstruction: envelopeMetadata?.systemInstruction,
        combinedSystemInstruction,
        reasoningInstructionSegments: ctx?.__rcc_reasoning_instructions_segments,
        contextParameters: undefined,
        chatParameters: chat.parameters,
        metadataParameters: stripToolControlFieldsFromParameterObject(metadataExtraFields?.parameters),
        contextStream: undefined,
        metadataStream: undefined,
        chatStream: streamFromChat,
        chatParametersStream: streamFromParameters,
        contextInclude: undefined,
        metadataInclude: undefined,
        contextStore: undefined,
        metadataStore: undefined,
        stripHostFields,
        contextToolChoice: undefined,
        contextParallelToolCalls: undefined,
        contextResponseFormat: undefined,
        metadataResponseFormat: undefined,
        contextServiceTier: undefined,
        metadataServiceTier: undefined,
        contextTruncation: undefined,
        metadataTruncation: undefined,
        contextMetadata: stripToolControlFieldsFromContextMetadata(ctx?.metadata),
        metadataMetadata: metadataExtraFields?.metadata
    });
    Object.assign(out, preparedEnvelope.request);
    delete out.parameters;
    ensureBridgeInstructions(out);
    return { request: out, originalSystemMessages };
}
function extractMetadataExtraFields(metadata) {
    if (!metadata) {
        return undefined;
    }
    const extras = metadata.extraFields;
    if (extras && typeof extras === 'object' && !Array.isArray(extras)) {
        return extras;
    }
    return undefined;
}
function stripToolControlFieldsFromContextMetadata(metadata) {
    if (!metadata) {
        return undefined;
    }
    const cloned = jsonClone(metadata);
    const extras = cloned.extraFields;
    if (!extras || !isPlainObject(extras)) {
        return cloned;
    }
    delete extras.tool_choice;
    delete extras.parallel_tool_calls;
    if (Object.keys(extras).length === 0) {
        delete cloned.extraFields;
    }
    return cloned;
}
function stripToolControlFieldsFromParameterObject(value) {
    if (!value) {
        return undefined;
    }
    const cloned = jsonClone(value);
    delete cloned.tool_choice;
    delete cloned.parallel_tool_calls;
    return Object.keys(cloned).length ? cloned : undefined;
}
function isPlainObject(value) {
    return Boolean(value && typeof value === 'object' && !Array.isArray(value));
}
function unwrapData(value) {
    let current = value;
    const seen = new Set();
    while (current && typeof current === 'object' && !Array.isArray(current) && !seen.has(current)) {
        seen.add(current);
        if ('choices' in current || 'message' in current)
            break;
        if ('data' in current && typeof current.data === 'object') {
            current = current.data;
            continue;
        }
        break;
    }
    return current;
}
function resolveSnapshotLookupKey(response, context) {
    if (typeof response?.request_id === 'string') {
        return response.request_id;
    }
    if (typeof context?.requestId === 'string') {
        return context.requestId;
    }
    if (typeof response?.id === 'string') {
        return response.id;
    }
    return undefined;
}
function shouldStripHostManagedFields(context) {
    const result = evaluateResponsesHostPolicy(context, typeof context?.targetProtocol === 'string' ? context?.targetProtocol : undefined);
    return result.shouldStripHostManagedFields;
}
export { buildResponsesPayloadFromChat, extractRequestIdFromResponse } from './responses-openai-bridge/response-payload.js';
export { buildChatResponseFromResponses } from '../shared/responses-response-utils.js';
// (imports moved to top)
