import { defaultSseCodecRegistry } from '../../../../../../sse/index.js';
import { recordStage } from '../../../stages/utils.js';
import { ProviderProtocolError } from '../../../../../provider-protocol-error.js';
import { extractContextLengthDiagnosticsWithNative, extractSseWrapperErrorWithNative, isContextLengthExceededSignalWithNative, parseJsonObjectCandidateWithNative } from '../../../../../../router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js';
import { tryDecodeJsonBodyFromStream } from './stream-json-sniffer.js';
function resolveProviderType(protocol) {
    if (protocol === 'openai-chat')
        return 'openai';
    if (protocol === 'openai-responses')
        return 'responses';
    if (protocol === 'anthropic-messages')
        return 'anthropic';
    if (protocol === 'gemini-chat')
        return 'gemini';
    return undefined;
}
function readObjectFallback(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        return undefined;
    }
    return value;
}
export async function runRespInboundStage1SseDecode(options) {
    // Transport compatibility: some HTTP clients return JSON bodies as plain strings when the upstream
    // mislabels `Content-Type`. Best-effort parse JSON text early so downstream format adapters and
    // semantic mappers always see canonical objects.
    const maybeJsonText = tryDecodeJsonBodyFromText(options.payload);
    if (maybeJsonText) {
        recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
            streamDetected: false,
            decoded: false,
            protocol: options.providerProtocol,
            reason: 'text_body_is_json'
        });
        return { payload: maybeJsonText, decodedFromSse: false };
    }
    const wrapperError = extractSseWrapperErrorWithNative(options.payload);
    const stream = extractSseStream(options.payload);
    // 某些 mock-provider / 捕获样本在 SSE 连接被异常终止时会携带 error 标记，
    // 即使仍保留 __sse_responses 流，也应视为上游异常并终止。
    if (wrapperError) {
        recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
            streamDetected: Boolean(stream),
            decoded: false,
            protocol: options.providerProtocol,
            reason: 'sse_wrapper_error',
            error: wrapperError
        });
        throw new ProviderProtocolError(`[chat_process.resp.stage1.sse_decode] Upstream SSE terminated: ${wrapperError}`, {
            code: 'SSE_DECODE_ERROR',
            protocol: options.providerProtocol,
            providerType: resolveProviderType(options.providerProtocol),
            details: {
                phase: 'chat_process.resp.stage1.sse_decode',
                requestId: options.adapterContext.requestId,
                message: wrapperError
            }
        });
    }
    if (!stream) {
        recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
            streamDetected: false
        });
        return { payload: options.payload, decodedFromSse: false };
    }
    // Compatibility: when an upstream is asked for streaming but responds with a single JSON body
    // (common for mock servers and some OpenAI-compatible implementations), the provider wrapper may
    // still surface a Readable via `__sse_stream`. In that case we should treat it as JSON, not SSE.
    const maybeJson = await tryDecodeJsonBodyFromStream(stream);
    if (maybeJson) {
        recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
            streamDetected: true,
            decoded: false,
            protocol: options.providerProtocol,
            reason: 'stream_body_is_json'
        });
        return { payload: maybeJson, decodedFromSse: false };
    }
    if (!supportsSseProtocol(options.providerProtocol)) {
        recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
            streamDetected: true,
            decoded: false,
            reason: 'protocol_unsupported',
            protocol: options.providerProtocol
        });
        throw new ProviderProtocolError(`[chat_process.resp.stage1.sse_decode] Protocol ${options.providerProtocol} does not support SSE decoding`, {
            code: 'SSE_DECODE_ERROR',
            protocol: options.providerProtocol,
            providerType: resolveProviderType(options.providerProtocol),
            details: {
                phase: 'chat_process.resp.stage1.sse_decode',
                reason: 'protocol_unsupported'
            }
        });
    }
    try {
        const codec = defaultSseCodecRegistry.get(options.providerProtocol);
        const decoded = (await codec.convertSseToJson(stream, {
            requestId: options.adapterContext.requestId,
            model: options.adapterContext.modelId
        }));
        recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
            streamDetected: true,
            decoded: true,
            protocol: options.providerProtocol
        });
        return { payload: decoded, decodedFromSse: true };
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        const errRecord = error;
        const upstreamCode = typeof errRecord.code === 'string' ? errRecord.code : undefined;
        const upstreamContext = readObjectFallback(errRecord.context);
        const contextLengthExceeded = isContextLengthExceededSignalWithNative(upstreamCode, message, upstreamContext);
        const diagnostics = extractContextLengthDiagnosticsWithNative(options.adapterContext);
        recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
            streamDetected: true,
            decoded: false,
            protocol: options.providerProtocol,
            error: message,
            ...(upstreamCode ? { upstreamCode } : {}),
            ...(contextLengthExceeded ? { reason: 'context_length_exceeded' } : {}),
            ...(Object.keys(diagnostics).length ? diagnostics : {})
        });
        throw new ProviderProtocolError(`[chat_process.resp.stage1.sse_decode] Failed to decode SSE payload for protocol ${options.providerProtocol}: ${message}` +
            (contextLengthExceeded ? ' (context too long; please compress conversation context and retry)' : ''), {
            code: 'SSE_DECODE_ERROR',
            protocol: options.providerProtocol,
            providerType: resolveProviderType(options.providerProtocol),
            details: {
                phase: 'chat_process.resp.stage1.sse_decode',
                requestId: options.adapterContext.requestId,
                message,
                ...(upstreamCode ? { upstreamCode } : {}),
                ...(contextLengthExceeded ? { reason: 'context_length_exceeded' } : {}),
                ...(Object.keys(diagnostics).length ? diagnostics : {})
            }
        });
    }
}
function supportsSseProtocol(protocol) {
    return protocol === 'openai-chat' || protocol === 'openai-responses' || protocol === 'anthropic-messages' || protocol === 'gemini-chat';
}
function tryDecodeJsonBodyFromText(payload) {
    if (typeof payload !== 'string') {
        return null;
    }
    const parsed = parseJsonObjectCandidateWithNative(payload, 10 * 1024 * 1024);
    return parsed ?? null;
}
function extractSseStream(payload) {
    if (!payload || typeof payload !== 'object') {
        return undefined;
    }
    const direct = payload.__sse_responses || payload.__sse_stream;
    if (direct && typeof direct.pipe === 'function') {
        return direct;
    }
    const nested = payload.data;
    if (nested && typeof nested === 'object') {
        const inner = nested.__sse_responses || nested.__sse_stream;
        if (inner && typeof inner.pipe === 'function') {
            return inner;
        }
    }
    return undefined;
}
