import { extractToolCallsFromReasoningText } from '../../shared/reasoning-tool-parser.js';
import { deriveToolCallKey } from '../../shared/tool-call-utils.js';
import { createBridgeActionState, runBridgeActionPipeline } from '../../bridge-actions.js';
import { resolveBridgePolicy, resolvePolicyActions } from '../../bridge-policies.js';
import { normalizeAnthropicToolName } from '../../shared/anthropic-message-utils.js';
import { buildAnthropicResponseFromChatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js';
import { registerResponsesReasoning, consumeResponsesReasoning, registerResponsesOutputTextMeta, consumeResponsesOutputTextMeta, consumeResponsesPayloadSnapshot, registerResponsesPayloadSnapshot, consumeResponsesPassthrough, registerResponsesPassthrough } from '../../shared/responses-reasoning-registry.js';
function flattenAnthropicContent(content) {
    if (typeof content === 'string')
        return content;
    if (Array.isArray(content)) {
        return content.map(flattenAnthropicContent).filter(Boolean).join('');
    }
    if (content && typeof content === 'object') {
        const block = content;
        if (typeof block.text === 'string')
            return block.text;
        if (typeof block.content === 'string')
            return block.content;
        if (Array.isArray(block.content))
            return block.content.map(flattenAnthropicContent).filter(Boolean).join('');
    }
    return '';
}
function sanitizeAnthropicToolUseId(raw) {
    if (typeof raw === 'string') {
        const trimmed = raw.trim();
        if (trimmed && /^[A-Za-z0-9_-]+$/.test(trimmed)) {
            return trimmed;
        }
    }
    return `call_${Math.random().toString(36).slice(2, 10)}`;
}
function coerceNonNegativeNumber(...candidates) {
    for (const value of candidates) {
        if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {
            return value;
        }
        if (typeof value === 'string') {
            const trimmed = value.trim();
            if (!trimmed.length)
                continue;
            const parsed = Number(trimmed);
            if (Number.isFinite(parsed) && parsed >= 0) {
                return parsed;
            }
        }
    }
    return undefined;
}
function createToolNameResolver(options) {
    const reverse = new Map();
    const shouldReplaceAlias = (existingCanonical, nextCanonical, providerKey) => {
        const existing = existingCanonical.trim().toLowerCase();
        const next = nextCanonical.trim().toLowerCase();
        if (!existing) {
            return true;
        }
        // Prefer exact identity mapping first (providerName == canonicalName).
        if (next === providerKey && existing !== providerKey) {
            return true;
        }
        if (existing === providerKey && next !== providerKey) {
            return false;
        }
        // When provider emits exec_command, never downgrade back to shell_command.
        if (providerKey === 'exec_command') {
            if (next === 'exec_command' && existing !== 'exec_command') {
                return true;
            }
            if (existing === 'exec_command' && next !== 'exec_command') {
                return false;
            }
        }
        return false;
    };
    const aliasMap = options?.aliasMap;
    if (aliasMap && typeof aliasMap === 'object') {
        for (const [canonical, providerName] of Object.entries(aliasMap)) {
            if (typeof canonical !== 'string' || typeof providerName !== 'string')
                continue;
            const canonicalName = canonical.trim();
            if (!canonicalName.length)
                continue;
            const normalizedProvider = providerName.trim().toLowerCase();
            if (!normalizedProvider.length)
                continue;
            const existing = reverse.get(normalizedProvider);
            if (!existing || shouldReplaceAlias(existing, canonicalName, normalizedProvider)) {
                reverse.set(normalizedProvider, canonicalName);
            }
        }
    }
    return (rawName) => {
        const trimmed = typeof rawName === 'string' ? rawName.trim() : '';
        if (!trimmed.length) {
            return '';
        }
        const lookup = reverse.get(trimmed.toLowerCase());
        if (lookup && lookup.trim().length) {
            return lookup.trim();
        }
        const normalized = normalizeAnthropicToolName(trimmed);
        return (normalized && normalized.trim().length ? normalized : trimmed).trim();
    };
}
function extractAliasMapFromChatPayload(payload) {
    // Deprecated: tool-name alias maps are mappable semantics and must be carried via requestSemantics
    // (ChatSemantics.tools.toolNameAliasMap). Do not embed them inside response payload fields/metadata.
    void payload;
    return undefined;
}
function createToolAliasSerializer(aliasMap) {
    if (!aliasMap || typeof aliasMap !== 'object') {
        return (name) => name;
    }
    const lookup = new Map();
    for (const [canonical, providerName] of Object.entries(aliasMap)) {
        if (typeof canonical !== 'string' || typeof providerName !== 'string')
            continue;
        const key = canonical.trim().toLowerCase();
        if (!key.length)
            continue;
        if (!lookup.has(key)) {
            lookup.set(key, providerName);
        }
    }
    if (!lookup.size) {
        return (name) => name;
    }
    return (name) => {
        const trimmed = typeof name === 'string' ? name.trim() : '';
        if (!trimmed.length) {
            return name;
        }
        const resolved = lookup.get(trimmed.toLowerCase());
        return resolved ? resolved : name;
    };
}
export function buildOpenAIChatFromAnthropicMessage(payload, options) {
    const content = Array.isArray(payload?.content) ? payload.content : [];
    const textParts = [];
    const toolCalls = [];
    const aliasCollector = {};
    const inferredToolCalls = [];
    const reasoningParts = [];
    const resolveToolName = createToolNameResolver(options);
    if (typeof payload?.reasoning_content === 'string' && payload.reasoning_content.trim().length) {
        reasoningParts.push(String(payload.reasoning_content).trim());
    }
    for (const part of content) {
        if (!part || typeof part !== 'object')
            continue;
        const kind = String(part.type || '').toLowerCase();
        if (kind === 'text' && typeof part.text === 'string') {
            const rawText = part.text;
            if (/<\s*(tool_call|function_call)\b|\[\s*(tool_call|function_call)\b/i.test(rawText)) {
                const { cleanedText, toolCalls: inferred } = extractToolCallsFromReasoningText(rawText, { idPrefix: 'anthropic_text' });
                const trimmed = cleanedText.trim();
                if (trimmed.length) {
                    textParts.push(trimmed);
                }
                if (Array.isArray(inferred) && inferred.length) {
                    inferredToolCalls.push(...inferred);
                }
            }
            else {
                textParts.push(rawText);
            }
        }
        else if (kind === 'tool_use') {
            const rawName = typeof part.name === 'string'
                ? String(part.name)
                : '';
            const name = rawName ? resolveToolName(rawName) : '';
            const id = typeof part.id === 'string'
                ? String(part.id)
                : `call_${Math.random().toString(36).slice(2, 10)}`;
            const input = part.input;
            let args = '';
            if (typeof input === 'string') {
                args = input;
            }
            else {
                try {
                    args = JSON.stringify(input ?? {});
                }
                catch {
                    args = '{}';
                }
            }
            if (name) {
                toolCalls.push({ id, name, args });
                const trimmedRaw = rawName.trim();
                if (trimmedRaw.length && !aliasCollector[name]) {
                    aliasCollector[name] = trimmedRaw;
                }
            }
        }
        else if (kind === 'thinking' || kind === 'reasoning') {
            const text = typeof part.text === 'string'
                ? part.text
                : flattenAnthropicContent(part);
            if (text) {
                const { cleanedText, toolCalls: inferred } = extractToolCallsFromReasoningText(text, { idPrefix: 'anthropic_reasoning' });
                const trimmed = cleanedText.trim();
                if (trimmed.length) {
                    reasoningParts.push(trimmed);
                }
                if (Array.isArray(inferred) && inferred.length) {
                    inferredToolCalls.push(...inferred);
                }
            }
        }
    }
    const mapFinishReason = (reason) => {
        switch (reason) {
            case 'tool_use': return 'tool_calls';
            case 'max_tokens': return 'length';
            case 'stop_sequence': return 'stop';
            default: return 'stop';
        }
    };
    const includeToolCallIds = options?.includeToolCallIds === true;
    const canonicalToolCalls = toolCalls.map((tc) => ({
        ...(includeToolCallIds ? { id: tc.id, call_id: tc.id, tool_call_id: tc.id } : {}),
        type: 'function',
        function: { name: tc.name, arguments: tc.args }
    }));
    if (inferredToolCalls.length) {
        const seen = new Set();
        for (const existing of canonicalToolCalls) {
            const key = deriveToolCallKey(existing);
            if (key)
                seen.add(key);
        }
        for (const inferred of inferredToolCalls) {
            const key = deriveToolCallKey(inferred);
            if (key && seen.has(key))
                continue;
            if (includeToolCallIds && typeof inferred.id === 'string') {
                const inferredId = String(inferred.id);
                if (!("call_id" in inferred))
                    inferred.call_id = inferredId;
                if (!("tool_call_id" in inferred))
                    inferred.tool_call_id = inferredId;
            }
            else if (!includeToolCallIds) {
                if ("id" in inferred)
                    delete inferred.id;
                if ("call_id" in inferred)
                    delete inferred.call_id;
                if ("tool_call_id" in inferred)
                    delete inferred.tool_call_id;
            }
            canonicalToolCalls.push(inferred);
            if (key)
                seen.add(key);
        }
    }
    for (const call of canonicalToolCalls) {
        const cid = typeof call.id === 'string' ? String(call.id) : '';
        if (includeToolCallIds) {
            if (cid) {
                if (!("call_id" in call))
                    call.call_id = cid;
                if (!("tool_call_id" in call))
                    call.tool_call_id = cid;
                if (!("id" in call))
                    call.id = cid;
            }
        }
        else {
            if ("id" in call)
                delete call.id;
            if ("call_id" in call)
                delete call.call_id;
            if ("tool_call_id" in call)
                delete call.tool_call_id;
        }
    }
    const message = {
        role: typeof payload.role === 'string' ? payload.role : 'assistant',
        content: textParts.join('\n')
    };
    if (canonicalToolCalls.length) {
        message.tool_calls = canonicalToolCalls;
    }
    if (reasoningParts.length) {
        message.reasoning_content = reasoningParts.join('\n');
    }
    try {
        const bridgePolicy = resolveBridgePolicy({ protocol: 'anthropic-messages' });
        const actions = resolvePolicyActions(bridgePolicy, 'response_inbound');
        if (actions?.length) {
            const actionState = createBridgeActionState({
                messages: [message],
                rawResponse: payload
            });
            runBridgeActionPipeline({
                stage: 'response_inbound',
                actions,
                protocol: bridgePolicy?.protocol ?? 'anthropic-messages',
                moduleType: bridgePolicy?.moduleType ?? 'anthropic-messages',
                requestId: typeof payload.id === 'string' ? payload.id : undefined,
                state: actionState
            });
        }
    }
    catch {
        // ignore policy failures
    }
    const stopReason = typeof payload['stop_reason'] === 'string' ? payload['stop_reason'] : undefined;
    const finishReason = canonicalToolCalls.length ? 'tool_calls' : mapFinishReason(stopReason);
    const chatResponse = {
        id: typeof payload.id === 'string' ? payload.id : `chatcmpl_${Date.now()}`,
        object: 'chat.completion',
        created: typeof payload?.['created'] === 'number' ? payload['created'] : Math.floor(Date.now() / 1000),
        model: typeof payload.model === 'string' ? payload.model : 'unknown',
        choices: [
            {
                index: 0,
                finish_reason: finishReason,
                message
            }
        ],
        usage: payload['usage'] && typeof payload['usage'] === 'object'
            ? payload['usage']
            : undefined
    };
    const localReasoning = reasoningParts.length
        ? {
            content: reasoningParts.map((text) => ({ type: 'reasoning_text', text }))
        }
        : undefined;
    const preserved = consumeResponsesReasoning(chatResponse.id);
    if (preserved) {
        chatResponse.__responses_reasoning = preserved;
    }
    else if (localReasoning) {
        chatResponse.__responses_reasoning = localReasoning;
    }
    const preservedOutputMeta = consumeResponsesOutputTextMeta(chatResponse.id);
    if (preservedOutputMeta) {
        chatResponse.__responses_output_text_meta = preservedOutputMeta;
    }
    const payloadSnapshot = consumeResponsesPayloadSnapshot(chatResponse.id);
    if (payloadSnapshot) {
        registerResponsesPayloadSnapshot(chatResponse.id, payloadSnapshot);
        chatResponse.__responses_payload_snapshot = payloadSnapshot;
        if (typeof chatResponse.request_id !== 'string') {
            chatResponse.request_id = chatResponse.id;
        }
    }
    const passthroughPayload = consumeResponsesPassthrough(chatResponse.id);
    if (passthroughPayload) {
        registerResponsesPassthrough(chatResponse.id, passthroughPayload);
        chatResponse.__responses_passthrough = passthroughPayload;
        if (typeof chatResponse.request_id !== 'string') {
            chatResponse.request_id = chatResponse.id;
        }
    }
    return chatResponse;
}
function mapShellCommandArgsForAnthropic(raw) {
    const result = {};
    const source = (raw && typeof raw === 'object' && !Array.isArray(raw)) ? raw : {};
    const coerceCommand = (value) => {
        if (typeof value === 'string' && value.trim().length) {
            return value.trim();
        }
        if (Array.isArray(value)) {
            const parts = value
                .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))
                .filter((entry) => entry.length > 0);
            if (parts.length) {
                return parts.join(' ');
            }
        }
        return '';
    };
    const commandRaw = coerceCommand(source.command) || coerceCommand(source.cmd);
    const command = commandRaw.trim();
    if (command) {
        result.command = command;
    }
    const timeoutRaw = source.timeout_ms ?? source.timeout;
    if (typeof timeoutRaw === 'number' && Number.isFinite(timeoutRaw)) {
        result.timeout = timeoutRaw;
    }
    else if (typeof timeoutRaw === 'string' && timeoutRaw.trim().length) {
        const parsed = Number(timeoutRaw.trim());
        if (Number.isFinite(parsed)) {
            result.timeout = parsed;
        }
    }
    if (typeof source.description === 'string' && source.description.trim().length) {
        result.description = source.description;
    }
    if (typeof source.run_in_background === 'boolean') {
        result.run_in_background = source.run_in_background;
    }
    if (typeof source.dangerouslyDisableSandbox === 'boolean') {
        result.dangerouslyDisableSandbox = source.dangerouslyDisableSandbox;
    }
    return result;
}
export function buildAnthropicResponseFromChat(chatResponse, options) {
    const choice = Array.isArray(chatResponse?.choices) ? chatResponse.choices[0] : undefined;
    const message = choice && typeof choice === 'object' ? choice.message : undefined;
    const aliasMap = options?.aliasMap;
    if (message) {
        try {
            const bridgePolicy = resolveBridgePolicy({ protocol: 'anthropic-messages' });
            const actions = resolvePolicyActions(bridgePolicy, 'response_outbound');
            if (actions?.length) {
                const actionState = createBridgeActionState({
                    messages: [message]
                });
                runBridgeActionPipeline({
                    stage: 'response_outbound',
                    actions,
                    protocol: bridgePolicy?.protocol ?? 'anthropic-messages',
                    moduleType: bridgePolicy?.moduleType ?? 'anthropic-messages',
                    requestId: typeof chatResponse.id === 'string' ? chatResponse.id : undefined,
                    state: actionState
                });
            }
        }
        catch {
            // ignore policy failures
        }
    }
    const sanitized = buildAnthropicResponseFromChatWithNative(chatResponse, aliasMap);
    if (chatResponse?.__responses_reasoning) {
        registerResponsesReasoning(sanitized.id, chatResponse.__responses_reasoning);
    }
    if (chatResponse?.__responses_output_text_meta) {
        registerResponsesOutputTextMeta(sanitized.id, chatResponse.__responses_output_text_meta);
    }
    return sanitized;
}
function sanitizeAnthropicMessage(message) {
    const sanitized = {};
    const allowedTopLevel = new Set(['id', 'type', 'role', 'content', 'model', 'stop_reason', 'stop_sequence', 'usage']);
    for (const key of allowedTopLevel) {
        if (message[key] !== undefined) {
            sanitized[key] = message[key];
        }
    }
    const content = Array.isArray(message.content) ? message.content : [];
    sanitized.content = content
        .map((block) => sanitizeContentBlock(block))
        .filter((block) => block !== null);
    const usage = message.usage;
    if (usage && typeof usage === 'object') {
        sanitized.usage = JSON.parse(JSON.stringify(usage));
    }
    return sanitized;
}
function sanitizeContentBlock(block) {
    if (!block || typeof block !== 'object')
        return null;
    const type = typeof block.type === 'string' ? block.type : '';
    if (type === 'text') {
        if (typeof block.text !== 'string')
            return null;
        return { type: 'text', text: block.text };
    }
    if (type === 'thinking' || type === 'reasoning') {
        const text = typeof block.text === 'string'
            ? block.text
            : flattenAnthropicContent(block.content);
        if (!text || !String(text).trim().length)
            return null;
        return {
            type: type === 'reasoning' ? 'reasoning' : 'thinking',
            text: String(text)
        };
    }
    if (type === 'tool_use') {
        const id = typeof block.id === 'string' && block.id.trim() ? block.id : `call_${Math.random().toString(36).slice(2, 8)}`;
        const name = typeof block.name === 'string' ? block.name : '';
        if (!name)
            return null;
        return {
            type: 'tool_use',
            id,
            name,
            input: block.input ?? {}
        };
    }
    if (type === 'tool_result') {
        if (typeof block.tool_use_id !== 'string' || !block.tool_use_id.trim())
            return null;
        const sanitized = {
            type: 'tool_result',
            tool_use_id: block.tool_use_id,
            content: block.content ?? ''
        };
        if (typeof block.is_error === 'boolean') {
            sanitized.is_error = block.is_error;
        }
        return sanitized;
    }
    return null;
}
function extractToolResultBlocks(chatResponse) {
    const results = [];
    const seen = new Set();
    const append = (candidate) => {
        if (!candidate)
            return;
        if (seen.has(candidate.tool_use_id)) {
            return;
        }
        seen.add(candidate.tool_use_id);
        results.push(candidate);
    };
    const primary = Array.isArray(chatResponse.tool_outputs)
        ? chatResponse.tool_outputs
        : [];
    primary.forEach(entry => append(normalizeToolResultEntry(entry)));
    const meta = chatResponse.metadata;
    if (meta && typeof meta === 'object') {
        const captured = meta.capturedToolResults;
        if (Array.isArray(captured)) {
            captured.forEach(entry => append(normalizeToolResultEntry(entry)));
        }
    }
    if (choiceHasCapturedResults(chatResponse)) {
        try {
            const choice = Array.isArray(chatResponse?.choices) ? chatResponse.choices[0] : undefined;
            const msgMeta = choice && typeof choice === 'object' ? choice.message : undefined;
            const captured = msgMeta && typeof msgMeta === 'object'
                ? msgMeta.capturedToolResults
                : undefined;
            if (Array.isArray(captured)) {
                captured.forEach(entry => append(normalizeToolResultEntry(entry)));
            }
        }
        catch {
            /* ignore best-effort metadata extraction */
        }
    }
    return results;
}
function choiceHasCapturedResults(chatResponse) {
    if (!Array.isArray(chatResponse?.choices)) {
        return false;
    }
    const first = chatResponse.choices[0];
    if (!first || typeof first !== 'object') {
        return false;
    }
    const message = first.message;
    if (!message || typeof message !== 'object') {
        return false;
    }
    return Array.isArray(message.capturedToolResults);
}
function normalizeToolResultEntry(entry) {
    if (!entry || typeof entry !== 'object') {
        return null;
    }
    const rawId = entry.tool_call_id ?? entry.call_id ?? entry.id;
    if (typeof rawId !== 'string' || !rawId.trim().length) {
        return null;
    }
    const toolUseId = rawId.trim();
    const rawContent = 'content' in entry ? entry.content : entry.output;
    const content = normalizeToolContent(rawContent);
    const isError = typeof entry.is_error === 'boolean' ? entry.is_error : undefined;
    return {
        tool_use_id: toolUseId,
        content,
        is_error: isError
    };
}
function normalizeToolContent(value) {
    if (value == null) {
        return undefined;
    }
    if (typeof value === 'string') {
        return value;
    }
    try {
        const serialized = JSON.stringify(value);
        return serialized;
    }
    catch {
        return String(value);
    }
}
