import { ProviderProtocolError } from '../provider-protocol-error.js';
import { convertResponsesOutputToInputItemsWithNative, pickResponsesPersistedFieldsWithNative, prepareResponsesConversationEntryWithNative, resumeResponsesConversationPayloadWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
const TTL_MS = 1000 * 60 * 30; // 30min
function isRecord(value) {
    return Boolean(value && typeof value === 'object' && !Array.isArray(value));
}
function pickPersistedFields(payload) {
    return pickResponsesPersistedFieldsWithNative(payload);
}
function convertOutputToInputItems(response) {
    return convertResponsesOutputToInputItemsWithNative(response);
}
function assertResponsesConversationStoreNativeAvailable() {
    if (typeof pickResponsesPersistedFieldsWithNative !== 'function' ||
        typeof convertResponsesOutputToInputItemsWithNative !== 'function' ||
        typeof prepareResponsesConversationEntryWithNative !== 'function' ||
        typeof resumeResponsesConversationPayloadWithNative !== 'function') {
        throw new Error('[responses-conversation-store] native bindings unavailable');
    }
}
class ResponsesConversationStore {
    requestMap = new Map();
    responseIndex = new Map();
    rebindRequestId(oldId, newId) {
        if (!oldId || !newId || oldId === newId) {
            return;
        }
        const entry = this.requestMap.get(oldId);
        if (!entry) {
            return;
        }
        this.requestMap.delete(oldId);
        entry.requestId = newId;
        this.requestMap.set(newId, entry);
    }
    captureRequestContext(args) {
        const { requestId, payload, context } = args;
        if (!requestId || !payload)
            return;
        this.prune();
        assertResponsesConversationStoreNativeAvailable();
        const prepared = prepareResponsesConversationEntryWithNative(payload, context);
        const entry = {
            requestId,
            basePayload: isRecord(prepared.basePayload) ? prepared.basePayload : pickPersistedFields(payload),
            input: Array.isArray(prepared.input) ? prepared.input : [],
            tools: Array.isArray(prepared.tools) ? prepared.tools : undefined,
            createdAt: Date.now(),
            updatedAt: Date.now()
        };
        this.requestMap.set(requestId, entry);
    }
    recordResponse(args) {
        const entry = args.requestId ? this.requestMap.get(args.requestId) : undefined;
        if (!entry)
            return;
        const response = args.response;
        const responseId = typeof response.id === 'string' ? response.id : undefined;
        if (!responseId)
            return;
        const assistantBlocks = convertOutputToInputItems(response);
        if (assistantBlocks.length) {
            entry.input.push(...assistantBlocks);
        }
        entry.lastResponseId = responseId;
        entry.updatedAt = Date.now();
        this.responseIndex.set(responseId, entry);
    }
    resumeConversation(responseId, submitPayload, options) {
        if (typeof responseId !== 'string' || !responseId.trim()) {
            throw new ProviderProtocolError('Responses conversation requires valid response_id', {
                code: 'MALFORMED_REQUEST',
                protocol: 'openai-responses',
                providerType: 'responses',
                details: {
                    context: 'responses-conversation-store.resumeConversation',
                    reason: 'missing_or_empty_response_id'
                }
            });
        }
        this.prune();
        const entry = this.responseIndex.get(responseId);
        if (!entry) {
            throw new ProviderProtocolError('Responses conversation expired or not found', {
                code: 'MALFORMED_REQUEST',
                protocol: 'openai-responses',
                providerType: 'responses',
                details: {
                    context: 'responses-conversation-store.resumeConversation',
                    reason: 'expired_or_unknown_response_id',
                    responseId
                }
            });
        }
        const toolOutputs = Array.isArray(submitPayload.tool_outputs) ? submitPayload.tool_outputs : [];
        if (!toolOutputs.length) {
            throw new ProviderProtocolError('tool_outputs array is required when submitting Responses tool results', {
                code: 'MALFORMED_REQUEST',
                protocol: 'openai-responses',
                providerType: 'responses',
                details: {
                    context: 'responses-conversation-store.resumeConversation',
                    reason: 'missing_tool_outputs',
                    responseId
                }
            });
        }
        assertResponsesConversationStoreNativeAvailable();
        const resumed = resumeResponsesConversationPayloadWithNative({
            requestId: entry.requestId,
            basePayload: entry.basePayload,
            input: entry.input,
            tools: entry.tools
        }, responseId, submitPayload, options?.requestId);
        this.cleanupEntry(entry, responseId);
        return {
            payload: resumed.payload,
            meta: resumed.meta
        };
    }
    clearRequest(requestId) {
        if (!requestId)
            return;
        const entry = this.requestMap.get(requestId);
        if (!entry)
            return;
        this.requestMap.delete(requestId);
        if (entry.lastResponseId) {
            this.responseIndex.delete(entry.lastResponseId);
        }
    }
    cleanupEntry(entry, responseId) {
        this.responseIndex.delete(responseId);
        this.requestMap.delete(entry.requestId);
    }
    prune() {
        const now = Date.now();
        for (const [requestId, entry] of this.requestMap.entries()) {
            if (now - entry.updatedAt > TTL_MS) {
                this.requestMap.delete(requestId);
                if (entry.lastResponseId) {
                    this.responseIndex.delete(entry.lastResponseId);
                }
            }
        }
        for (const [respId, entry] of this.responseIndex.entries()) {
            if (!this.requestMap.has(entry.requestId)) {
                this.responseIndex.delete(respId);
            }
        }
    }
}
const store = new ResponsesConversationStore();
const RESPONSES_DEBUG = (process.env.ROUTECODEX_RESPONSES_DEBUG || '').trim() === '1';
export function captureResponsesRequestContext(args) {
    try {
        if (RESPONSES_DEBUG) {
            console.log('[responses-store] capture', args.requestId);
        }
        store.captureRequestContext(args);
    }
    catch {
        /* ignore capture failures */
    }
}
export function recordResponsesResponse(args) {
    try {
        if (RESPONSES_DEBUG) {
            console.log('[responses-store] record', args.requestId, args.response?.id);
        }
        store.recordResponse(args);
    }
    catch {
        /* ignore */
    }
}
export function resumeResponsesConversation(responseId, submitPayload, options) {
    if (RESPONSES_DEBUG) {
        console.log('[responses-store] resume', responseId);
    }
    return store.resumeConversation(responseId, submitPayload, options);
}
export function clearResponsesConversationByRequestId(requestId) {
    if (RESPONSES_DEBUG && requestId) {
        console.log('[responses-store] clear', requestId);
    }
    store.clearRequest(requestId);
}
export function rebindResponsesConversationRequestId(oldId, newId) {
    if (RESPONSES_DEBUG && oldId && newId) {
        console.log('[responses-store] rebind', oldId, '->', newId);
    }
    store.rebindRequestId(oldId, newId);
}
export { store as responsesConversationStore };
