import { buildResponsesPayloadFromChat, runStandardChatRequestFilters } from '../index.js';
import { captureResponsesContext, buildChatRequestFromResponses, buildResponsesRequestFromChat } from '../responses/responses-openai-bridge.js';
import { captureResponsesRequestContext } from '../shared/responses-conversation-store.js';
import { FilterEngine, ResponseToolTextCanonicalizeFilter, ResponseToolArgumentsStringifyFilter, ResponseFinishInvariantsFilter } from '../../filters/index.js';
// Ported from root package (no behavior change). Types relaxed.
export class ResponsesOpenAIConversionCodec {
    dependencies;
    id = 'responses-openai';
    initialized = false;
    ctxMap = new Map();
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    constructor(dependencies) {
        this.dependencies = dependencies;
    }
    async initialize() { this.initialized = true; }
    async convertRequest(payload, profile, context) {
        await this.ensureInit();
        const dto = {
            data: payload,
            route: {
                providerId: 'unknown',
                modelId: (payload && typeof payload === 'object' && payload.model) ? String(payload.model) : 'unknown',
                requestId: context.requestId ?? `req_${Date.now()}`,
                timestamp: Date.now()
            },
            metadata: {
                endpoint: context.endpoint ?? context.entryEndpoint,
                entryEndpoint: context.entryEndpoint,
                targetProtocol: profile.outgoingProtocol
            },
            debug: { enabled: false, stages: {} }
        };
        // Build context and convert to OpenAI Chat request
        const ctx = captureResponsesContext(dto.data, dto);
        let { request: chatRequest, toolsNormalized } = buildChatRequestFromResponses(dto.data, ctx);
        // V3 专用：确保进入下游 Provider 的是“非流式 Chat”形状。
        // Responses 入口的流式语义由 llmswitch-core/上层 SSE 合成处理；
        // 这里显式移除 Chat 请求上的顶层 stream 标记，避免部分上游 Provider
        // 因为 stream=true 直接切换到 SSE，而下游仍按 JSON 客户端解析。
        try {
            if (chatRequest && typeof chatRequest === 'object' && 'stream' in chatRequest) {
                delete chatRequest.stream;
            }
        }
        catch {
            // best-effort：失败时保留原样，但不会阻断请求
        }
        if (toolsNormalized)
            ctx.toolsNormalized = toolsNormalized;
        // Capture tool results from Responses request input for pairing on response mapping
        try {
            const inputArr = Array.isArray(dto.data?.input) ? dto.data.input : [];
            const results = [];
            for (const it of inputArr) {
                if (!it || typeof it !== 'object')
                    continue;
                const t = String(it.type || '').toLowerCase();
                if (t === 'tool_result' || t === 'tool_message' || t === 'function_call_output') {
                    const tool_call_id = it.tool_call_id || it.call_id || it.tool_use_id;
                    let output = undefined;
                    const rawOut = it.output;
                    if (typeof rawOut === 'string')
                        output = rawOut;
                    else if (rawOut && typeof rawOut === 'object') {
                        try {
                            output = JSON.stringify(rawOut);
                        }
                        catch { /* ignore */ }
                    }
                    results.push({ tool_call_id, output });
                }
            }
            if (results.length > 0)
                ctx.__captured_tool_results = results;
        }
        catch { /* ignore */ }
        if (context.requestId)
            this.ctxMap.set(context.requestId, ctx);
        // 统一走 Chat 请求过滤链路，保证 /v1/responses 与 /v1/chat 在工具治理上的一致性
        const ctxForFilters = {
            ...context,
            requestId: dto.route.requestId,
            entryEndpoint: context.entryEndpoint ?? dto.metadata?.entryEndpoint ?? context.endpoint,
            endpoint: context.endpoint ?? dto.metadata?.endpoint
        };
        const filtered = await runStandardChatRequestFilters(chatRequest, profile, ctxForFilters);
        try {
            const rebuilt = buildResponsesRequestFromChat(filtered, ctx);
            const payloadForStore = rebuilt?.request;
            if (payloadForStore && typeof payloadForStore === 'object') {
                captureResponsesRequestContext({
                    requestId: dto.route.requestId,
                    payload: payloadForStore,
                    context: ctx
                });
            }
        }
        catch {
            // best-effort capture
        }
        if (filtered && typeof filtered === 'object') {
            const maybe = filtered;
            if (maybe.max_tokens === undefined && typeof maybe.max_output_tokens === 'number') {
                maybe.max_tokens = maybe.max_output_tokens;
            }
        }
        return filtered;
    }
    async convertResponse(payload, _profile, context) {
        await this.ensureInit();
        // unwrap nested { data: {...} } wrappers until we reach an object
        const unwrap = (obj) => {
            let cur = obj;
            const guard = new Set();
            while (cur && typeof cur === 'object' && !Array.isArray(cur) && !guard.has(cur)) {
                guard.add(cur);
                if ('choices' in cur || 'id' in cur || 'object' in cur) {
                    break;
                }
                if ('data' in cur && cur.data && typeof cur.data === 'object') {
                    cur = cur.data;
                    continue;
                }
                break;
            }
            return cur;
        };
        const unwrapped = unwrap(payload);
        const dto = {
            data: unwrapped,
            metadata: {
                requestId: context.requestId ?? `req_${Date.now()}`,
                pipelineId: context.metadata?.pipelineId ?? 'conversion-router',
                processingTime: 0,
                stages: []
            }
        };
        const ctx = context.requestId ? this.ctxMap.get(context.requestId) : undefined;
        if (context.requestId)
            this.ctxMap.delete(context.requestId);
        // Run response-side filter pipeline与 Chat 通路保持一致（工具治理统一在 Chat canonical 上完成）
        const resCtxBase = {
            requestId: context.requestId ?? dto.metadata.requestId,
            model: undefined,
            endpoint: context.entryEndpoint || context.endpoint,
            profile: 'openai-chat',
            debug: { emit: () => { } }
        };
        const engine = new FilterEngine();
        // Response-side filters：文本标准化 → apply_patch 结构化补丁规范化 → shell/basics → finish_reason 不变式
        engine.registerFilter(new ResponseToolTextCanonicalizeFilter()); // response_pre
        engine.registerFilter(new ResponseToolArgumentsStringifyFilter()); // response_post
        engine.registerFilter(new ResponseFinishInvariantsFilter()); // response_post
        let staged = await engine.run('response_pre', dto.data, resCtxBase);
        staged = await engine.run('response_map', staged, resCtxBase);
        staged = await engine.run('response_post', staged, resCtxBase);
        const governedChat = staged;
        // Convert provider chat response back to Responses payload using captured context（仅做形状映射）
        const responsesJson = buildResponsesPayloadFromChat(governedChat, ctx);
        // No fallback append here: pairing must be produced upstream or by standard bridge mapping.
        // Debug snapshot after mapping to Responses JSON (non-stream)
        try {
            const { writeSnapshotViaHooks } = await import('../snapshot-utils.js');
            await writeSnapshotViaHooks({
                endpoint: '/v1/responses',
                stage: 'response_mapped_json',
                requestId: context.requestId || `req_${Date.now()}`,
                data: responsesJson,
                channel: 'responses',
                verbosity: 'verbose'
            });
        }
        catch { /* ignore */ }
        return responsesJson;
    }
    async ensureInit() { if (!this.initialized)
        await this.initialize(); }
}
