import { createProtocolPlans } from './registry.js';
import { runInboundPipeline } from './pipelines/inbound.js';
import { runOutboundPipeline } from './pipelines/outbound.js';
import { createSnapshotRecorder } from './snapshot-recorder.js';
import { ConversionError } from './types/errors.js';
import { isHubProtocolEnabled } from './hub-feature.js';
import { chatEnvelopeToStandardizedWithNative } from '../../router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js';
import { standardizedToChatEnvelopeWithNative } from '../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
export function shouldUseHub(protocol) {
    return isHubProtocolEnabled(protocol);
}
export async function runHubInboundConversion(options) {
    const adapterContext = deriveAdapterContext(options.nodeContext, options.protocol);
    const rawPayload = ensureJsonObject(options.rawRequest, options.nodeId);
    const stageRecorder = maybeCreateStageRecorder(adapterContext, options.nodeContext.request.endpoint);
    const { inbound } = createProtocolPlans(options.protocol);
    const chatEnvelope = await runInboundPipeline({
        rawRequest: rawPayload,
        context: adapterContext,
        plan: inbound,
        stageRecorder
    });
    const standardized = chatEnvelopeToStandardizedWithNative({
        chatEnvelope: chatEnvelope,
        adapterContext: adapterContext,
        endpoint: options.nodeContext.request.endpoint,
        requestId: options.nodeContext.request.id
    });
    return buildHubNodeResult({
        nodeId: options.nodeId,
        startTime: options.startTime,
        standardized,
        inputFormat: options.inputFormat,
        outputFormat: options.outputFormat
    });
}
export async function runHubOutboundConversion(options) {
    const adapterContext = deriveAdapterContext(options.nodeContext, options.protocol);
    // Snapshots must be grouped by the client-facing entry endpoint, not by the provider protocol.
    // A /v1/responses request routed to an anthropic upstream is still a responses *entry* request.
    const stageRecorder = maybeCreateStageRecorder(adapterContext, options.nodeContext.request.endpoint);
    const { outbound } = createProtocolPlans(options.protocol);
    const chatEnvelope = standardizedToChatEnvelopeWithNative({
        request: options.request,
        adapterContext: adapterContext
    });
    const payload = await runOutboundPipeline({
        chat: chatEnvelope,
        context: adapterContext,
        plan: outbound,
        stageRecorder
    });
    return payload;
}
function maybeCreateStageRecorder(adapterContext, defaultEndpoint) {
    const flag = process.env.ROUTECODEX_HUB_SNAPSHOTS;
    if (flag && flag.trim() === '0') {
        return undefined;
    }
    const endpoint = defaultEndpoint || adapterContext.entryEndpoint || '/v1/chat/completions';
    return createSnapshotRecorder(adapterContext, endpoint);
}
function resolveEndpointForProtocol(protocol) {
    if (!protocol)
        return undefined;
    const value = protocol.toLowerCase();
    if (value === 'openai-responses')
        return '/v1/responses';
    if (value === 'openai-chat')
        return '/v1/chat/completions';
    if (value === 'anthropic-messages' || value === 'anthropic')
        return '/v1/messages';
    if (value === 'gemini' || value === 'google-gemini')
        return '/v1/responses';
    return undefined;
}
function ensureJsonObject(value, nodeId) {
    if (!value || typeof value !== 'object') {
        throw new ConversionError('Hub inbound requires JSON object payload', nodeId);
    }
    return value;
}
function buildHubNodeResult(options) {
    return {
        success: true,
        data: {
            standardizedRequest: options.standardized,
            originalFormat: options.inputFormat,
            targetFormat: options.outputFormat
        },
        metadata: {
            node: options.nodeId,
            executionTime: Date.now() - options.startTime,
            startTime: options.startTime,
            endTime: Date.now(),
            dataProcessed: {
                messages: options.standardized.messages.length,
                tools: options.standardized.tools?.length ?? 0
            }
        }
    };
}
function deriveAdapterContext(context, fallbackProtocol) {
    const requestContext = (context.request.context ?? {});
    const metadata = (requestContext.metadata ?? {});
    const target = metadata.target;
    const providerProtocol = target?.outboundProfile ||
        (typeof metadata.providerProtocol === 'string' ? metadata.providerProtocol : undefined) ||
        fallbackProtocol;
    const streamingHint = metadata.stream === true ? 'force' : metadata.stream === false ? 'disable' : 'auto';
    const toolCallIdStyle = normalizeToolCallIdStyleCandidate(metadata.toolCallIdStyle);
    return {
        requestId: context.request.id,
        entryEndpoint: (typeof requestContext.entryEndpoint === 'string' ? requestContext.entryEndpoint : context.request.endpoint) ||
            '/v1/chat/completions',
        providerProtocol,
        providerId: (target?.providerKey || metadata.providerKey),
        routeId: metadata.routeName,
        profileId: metadata.pipelineId,
        streamingHint,
        toolCallIdStyle
    };
}
function normalizeToolCallIdStyleCandidate(value) {
    if (typeof value !== 'string') {
        return undefined;
    }
    const normalized = value.trim().toLowerCase();
    if (normalized === 'fc') {
        return 'fc';
    }
    if (normalized === 'preserve') {
        return 'preserve';
    }
    return undefined;
}
