import { createBridgeActionState, runBridgeActionPipeline } from '../bridge-actions.js';
import { resolveBridgePolicy, resolvePolicyActions } from '../bridge-policies.js';
import { normalizeChatMessageContent } from './chat-output-normalizer.js';
import { mapBridgeToolsToChat, mapChatToolsToBridge } from './tool-mapping.js';
import { jsonClone } from '../hub/types/json.js';
import { ProviderProtocolError } from '../provider-protocol-error.js';
import { parseLenientJsonishWithNative } from '../../router/virtual-router/engine-selection/native-shared-conversion-semantics.js';
function isObject(v) {
    return !!v && typeof v === 'object' && !Array.isArray(v);
}
function safeJson(v) {
    try {
        return JSON.stringify(v ?? {});
    }
    catch {
        return '{}';
    }
}
function stripOpenAIChatToolAliasFields(messages) {
    // No-op: preserve tool_call_id/call_id for downstream consumers and regression parity.
    void messages;
}
function stripToolCallIdFieldsFromAssistant(messages) {
    for (const message of messages) {
        if (!message || typeof message !== 'object')
            continue;
        const role = String(message.role || '').toLowerCase();
        if (role !== 'assistant')
            continue;
        const calls = message.tool_calls;
        if (!Array.isArray(calls))
            continue;
        for (const call of calls) {
            if (!call || typeof call !== 'object')
                continue;
            delete call.call_id;
            delete call.tool_call_id;
        }
    }
}
function sanitizeToolUseId(raw) {
    const trimmed = typeof raw === 'string' ? raw.trim() : '';
    if (!trimmed) {
        return `call_${Math.random().toString(36).slice(2, 10)}`;
    }
    if (/^[A-Za-z0-9_-]+$/.test(trimmed)) {
        return trimmed;
    }
    const sanitized = trimmed
        .replace(/[^A-Za-z0-9_-]/g, '_')
        .replace(/_{2,}/g, '_')
        .replace(/^_+/, '')
        .replace(/_+$/, '');
    return sanitized || `call_${Math.random().toString(36).slice(2, 10)}`;
}
function flattenAnthropicText(content) {
    if (content == null)
        return '';
    if (typeof content === 'string')
        return content;
    if (Array.isArray(content))
        return content.map(flattenAnthropicText).join('');
    if (typeof content === 'object') {
        const t = String(content.type || '').toLowerCase();
        if (t === 'text' && typeof content.text === 'string')
            return String(content.text);
        if (Array.isArray(content.content))
            return content.content.map(flattenAnthropicText).join('');
        if (typeof content.content === 'string')
            return String(content.content);
    }
    return '';
}
function normalizeToolResultContent(block) {
    if (!block || typeof block !== 'object') {
        return '';
    }
    const content = block.content;
    if (typeof content === 'string') {
        return content;
    }
    if (Array.isArray(content)) {
        const segments = [];
        for (const entry of content) {
            const segment = extractToolResultSegment(entry);
            if (segment) {
                segments.push(segment);
            }
        }
        if (segments.length) {
            return segments.join('\n');
        }
    }
    else if (content != null) {
        try {
            return JSON.stringify(content);
        }
        catch {
            return String(content);
        }
    }
    return '';
}
function extractToolResultSegment(entry) {
    if (entry == null) {
        return '';
    }
    if (typeof entry === 'string') {
        return entry;
    }
    if (Array.isArray(entry)) {
        return entry.map(extractToolResultSegment).filter(Boolean).join('');
    }
    if (typeof entry === 'object') {
        const node = entry;
        const type = typeof node.type === 'string' ? node.type.toLowerCase() : '';
        if (type === 'input_text' || type === 'input_json' || type === 'tool_result_status' || type === 'status' || type === 'metadata') {
            return '';
        }
        if (type === 'output_text' || type === 'text' || type === 'reasoning' || type === 'log') {
            return flattenAnthropicText(entry);
        }
        if (type === 'output_json' || type === 'json') {
            const payload = node.content ?? node.text ?? node.data ?? node.output;
            if (payload === undefined) {
                return '';
            }
            try {
                return JSON.stringify(payload);
            }
            catch {
                return String(payload ?? '');
            }
        }
        if (typeof node.text === 'string') {
            return node.text;
        }
        if ('content' in node) {
            const nested = extractToolResultSegment(node.content);
            if (nested) {
                return nested;
            }
        }
        try {
            return JSON.stringify(entry);
        }
        catch {
            return '';
        }
    }
    return String(entry);
}
function resolveProtocolErrorCode(context) {
    const ctx = context.toLowerCase();
    return ctx.includes('tool') ? 'TOOL_PROTOCOL_ERROR' : 'MALFORMED_REQUEST';
}
function requireTrimmedString(value, context) {
    if (typeof value !== 'string') {
        throw new ProviderProtocolError(`Anthropic bridge constraint violated: ${context} must be a string`, {
            code: resolveProtocolErrorCode(context),
            protocol: 'anthropic-messages',
            providerType: 'anthropic',
            details: { context, actualType: typeof value }
        });
    }
    const trimmed = value.trim();
    if (!trimmed.length) {
        throw new ProviderProtocolError(`Anthropic bridge constraint violated: ${context} must not be empty`, {
            code: resolveProtocolErrorCode(context),
            protocol: 'anthropic-messages',
            providerType: 'anthropic',
            details: { context }
        });
    }
    return trimmed;
}
function requireSystemText(block, context) {
    const text = flattenAnthropicText(block).trim();
    if (!text) {
        throw new ProviderProtocolError(`Anthropic bridge constraint violated: ${context} must contain text`, {
            code: resolveProtocolErrorCode(context),
            protocol: 'anthropic-messages',
            providerType: 'anthropic',
            details: { context }
        });
    }
    return text;
}
const ANTHROPIC_TOOL_NAME_ALIASES = new Map();
const CANONICAL_TO_ANTHROPIC_TOOL_NAMES = new Map([['shell_command', 'Bash']]);
const ANTHROPIC_TOP_LEVEL_FIELDS = new Set([
    'model',
    'messages',
    'tools',
    'system',
    'stop_sequences',
    'temperature',
    'top_p',
    'top_k',
    'max_tokens',
    'max_output_tokens',
    'metadata',
    'stream',
    'tool_choice',
    'thinking'
]);
const ANTHROPIC_STABLE_TOOL_SCHEMA_NAMES = new Set([
    'exec_command',
    'write_stdin',
    'apply_patch',
    'request_user_input',
    'update_plan',
    'view_image',
    'web_search',
    'clock',
    'continue_execution',
    'review'
]);
const ANTHROPIC_STABLE_TOOL_SCHEMA_KEYS = new Map([
    ['exec_command', new Set(['cmd', 'command', 'workdir', 'justification', 'login', 'max_output_tokens', 'sandbox_permissions', 'shell', 'yield_time_ms', 'tty', 'prefix_rule'])],
    ['write_stdin', new Set(['session_id', 'chars', 'text', 'yield_time_ms', 'max_output_tokens'])],
    ['apply_patch', new Set(['patch', 'input', 'instructions', 'text', 'file', 'changes'])],
    ['request_user_input', new Set(['questions'])],
    ['update_plan', new Set(['explanation', 'plan'])],
    ['view_image', new Set(['path'])],
    ['web_search', new Set(['query', 'q', 'domains', 'recency'])],
    ['clock', new Set(['action', 'items', 'taskId'])]
]);
export function normalizeAnthropicToolName(value) {
    if (typeof value !== 'string') {
        return undefined;
    }
    const trimmed = value.trim();
    if (!trimmed) {
        return undefined;
    }
    const lower = trimmed.toLowerCase();
    const alias = ANTHROPIC_TOOL_NAME_ALIASES.get(lower);
    if (alias) {
        return alias;
    }
    if (lower.startsWith('mcp__')) {
        return lower;
    }
    return lower;
}
export function denormalizeAnthropicToolName(value) {
    if (typeof value !== 'string') {
        return undefined;
    }
    const trimmed = value.trim();
    if (!trimmed) {
        return undefined;
    }
    const lower = trimmed.toLowerCase();
    const mapped = CANONICAL_TO_ANTHROPIC_TOOL_NAMES.get(lower);
    if (mapped) {
        return mapped;
    }
    if (lower.startsWith('mcp__')) {
        return trimmed;
    }
    return trimmed;
}
function coerceShellLikeCommand(value) {
    if (typeof value === 'string') {
        const trimmed = value.trim();
        return trimmed.length ? trimmed : undefined;
    }
    if (Array.isArray(value)) {
        const parts = value
            .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))
            .filter((entry) => entry.length > 0);
        if (!parts.length) {
            return undefined;
        }
        return parts.join(' ');
    }
    return undefined;
}
function normalizeShellLikeToolInput(toolName, input) {
    const canonical = normalizeAnthropicToolName(toolName);
    if (canonical !== 'shell_command') {
        return input;
    }
    const rawToolName = typeof toolName === 'string' ? toolName.trim().toLowerCase() : '';
    const isExecCommand = rawToolName === 'exec_command';
    if (!input || typeof input !== 'object' || Array.isArray(input)) {
        const cmd = coerceShellLikeCommand(input);
        if (!cmd) {
            return {};
        }
        return isExecCommand ? { cmd } : { command: cmd };
    }
    const next = { ...input };
    const commandValue = coerceShellLikeCommand(next.command);
    const cmdValue = coerceShellLikeCommand(next.cmd);
    const fallbackValue = coerceShellLikeCommand(next.script) ??
        coerceShellLikeCommand(next.toon);
    if (commandValue) {
        next.command = commandValue;
    }
    if (cmdValue) {
        next.cmd = cmdValue;
    }
    if (!commandValue && !cmdValue && fallbackValue) {
        if (isExecCommand) {
            next.cmd = fallbackValue;
        }
        else {
            next.command = fallbackValue;
        }
    }
    else if (!commandValue && cmdValue && !isExecCommand) {
        next.command = cmdValue;
    }
    else if (!cmdValue && commandValue && isExecCommand) {
        next.cmd = commandValue;
    }
    if (!isExecCommand && 'cmd' in next) {
        delete next.cmd;
    }
    if (typeof next.workdir !== 'string' && typeof next.cwd === 'string' && next.cwd.trim().length > 0) {
        next.workdir = next.cwd.trim();
    }
    return next;
}
function invertAnthropicAliasMap(source) {
    if (!source) {
        return undefined;
    }
    const inverted = {};
    for (const [canonical, raw] of Object.entries(source)) {
        if (typeof canonical !== 'string' || typeof raw !== 'string') {
            continue;
        }
        const trimmedCanonical = canonical.trim();
        const trimmedRaw = raw.trim();
        if (!trimmedCanonical.length) {
            continue;
        }
        if (trimmedRaw.length) {
            inverted[trimmedRaw.toLowerCase()] = trimmedCanonical;
        }
        if (!inverted[trimmedCanonical.toLowerCase()]) {
            inverted[trimmedCanonical.toLowerCase()] = trimmedCanonical;
        }
    }
    return Object.keys(inverted).length ? inverted : undefined;
}
export function buildOpenAIChatFromAnthropic(payload, options) {
    const newMessages = [];
    const body = isObject(payload) ? payload : {};
    const canonicalAliasMap = coerceAliasRecord(buildAnthropicToolAliasMap(body.tools));
    const reverseAliasMap = invertAnthropicAliasMap(canonicalAliasMap);
    const resolveToolName = (candidate) => {
        if (typeof candidate !== 'string') {
            return '';
        }
        const trimmed = candidate.trim();
        if (!trimmed.length) {
            return trimmed;
        }
        const normalized = normalizeAnthropicToolName(trimmed) ?? trimmed;
        if (reverseAliasMap) {
            const direct = reverseAliasMap[trimmed.toLowerCase()];
            if (typeof direct === 'string' && direct.trim().length) {
                return direct.trim();
            }
            const normalizedLookup = reverseAliasMap[normalized.toLowerCase()];
            if (typeof normalizedLookup === 'string' && normalizedLookup.trim().length) {
                return normalizedLookup.trim();
            }
        }
        return normalized;
    };
    const rawSystem = body.system;
    const systemBlocks = Array.isArray(rawSystem)
        ? rawSystem
        : rawSystem !== undefined && rawSystem !== null
            ? [rawSystem]
            : [];
    for (const block of systemBlocks) {
        const text = requireSystemText(block, 'system entry');
        newMessages.push({ role: 'system', content: text });
    }
    const msgs = Array.isArray(body.messages) ? body.messages : [];
    for (const m of msgs) {
        if (!m || typeof m !== 'object')
            continue;
        const role = typeof m.role === 'string' ? String(m.role) : 'user';
        const content = m.content;
        if (!Array.isArray(content)) {
            const text = flattenAnthropicText(content);
            if (text) {
                const normalized = normalizeChatMessageContent(text);
                const message = {
                    role,
                    content: normalized.contentText ?? text
                };
                if (typeof normalized.reasoningText === 'string' && normalized.reasoningText.trim().length) {
                    message.reasoning_content = normalized.reasoningText.trim();
                }
                newMessages.push(message);
            }
            continue;
        }
        const textParts = [];
        const imageBlocks = [];
        const toolCalls = [];
        const reasoningParts = [];
        const toolResults = [];
        for (const block of content) {
            if (!block || typeof block !== 'object')
                continue;
            const t = String(block.type || '').toLowerCase();
            if (t === 'text' && typeof block.text === 'string') {
                const s = block.text.trim();
                if (s)
                    textParts.push(s);
            }
            else if (t === 'thinking' || t === 'reasoning') {
                const thinkingText = flattenAnthropicText(block).trim();
                if (thinkingText) {
                    reasoningParts.push(thinkingText);
                }
            }
            else if (t === 'image') {
                const source = block.source;
                if (source && typeof source === 'object') {
                    const s = source;
                    const srcType = typeof s.type === 'string' ? s.type.toLowerCase() : '';
                    let url;
                    if (srcType === 'url' && typeof s.url === 'string') {
                        url = s.url;
                    }
                    else if (srcType === 'base64' && typeof s.data === 'string') {
                        const mediaType = typeof s.media_type === 'string' && s.media_type.trim().length
                            ? s.media_type.trim()
                            : 'image/png';
                        url = `data:${mediaType};base64,${s.data}`;
                    }
                    if (url && url.trim().length) {
                        imageBlocks.push({
                            type: 'image_url',
                            image_url: { url: url.trim() }
                        });
                    }
                }
            }
            else if (t === 'tool_use') {
                const name = requireTrimmedString(block.name, 'tool_use.name');
                const id = requireTrimmedString(block.id, 'tool_use.id');
                const input = block.input ?? {};
                const args = safeJson(input);
                const canonicalName = resolveToolName(name) || name;
                const includeIds = options?.includeToolCallIds === true;
                toolCalls.push({
                    id,
                    ...(includeIds ? { call_id: id, tool_call_id: id } : {}),
                    type: 'function',
                    function: { name: canonicalName, arguments: args }
                });
            }
            else if (t === 'tool_result') {
                const callId = requireTrimmedString(block.tool_call_id ??
                    block.call_id ??
                    block.tool_use_id ??
                    block.id, 'tool_result.tool_use_id');
                const contentStr = normalizeToolResultContent(block);
                toolResults.push({ role: 'tool', tool_call_id: callId, content: contentStr });
            }
        }
        const combinedText = textParts.join('\n');
        const normalized = normalizeChatMessageContent(combinedText);
        const hasRawText = typeof combinedText === 'string' && combinedText.trim().length > 0;
        const mergedReasoning = [...reasoningParts];
        if (typeof normalized.reasoningText === 'string' && normalized.reasoningText.trim().length) {
            mergedReasoning.push(normalized.reasoningText.trim());
        }
        const hasText = typeof normalized.contentText === 'string' && normalized.contentText.length > 0;
        const hasReasoning = mergedReasoning.length > 0;
        if (hasText || hasRawText || toolCalls.length > 0 || hasReasoning || imageBlocks.length > 0) {
            let contentNode = (hasText ? normalized.contentText : undefined) ?? combinedText ?? '';
            if (imageBlocks.length > 0) {
                const blocks = [];
                const textPayload = (hasText ? normalized.contentText : undefined) ?? combinedText ?? '';
                if (typeof textPayload === 'string' && textPayload.trim().length) {
                    blocks.push({ type: 'text', text: textPayload.trim() });
                }
                for (const img of imageBlocks) {
                    blocks.push(jsonClone(img));
                }
                contentNode = blocks;
            }
            const msg = {
                role,
                content: contentNode
            };
            if (toolCalls.length)
                msg.tool_calls = toolCalls;
            if (hasReasoning) {
                msg.reasoning_content = mergedReasoning.join('\n');
            }
            newMessages.push(msg);
        }
        for (const tr of toolResults)
            newMessages.push(tr);
    }
    const request = { messages: newMessages };
    if (typeof body.model === 'string')
        request.model = body.model;
    if (typeof body.max_tokens === 'number')
        request.max_tokens = body.max_tokens;
    if (typeof body.temperature === 'number')
        request.temperature = body.temperature;
    if (typeof body.top_p === 'number')
        request.top_p = body.top_p;
    if (typeof body.stream === 'boolean')
        request.stream = body.stream;
    if (typeof body.id === 'string') {
        request.request_id = body.id;
    }
    else if (typeof body.request_id === 'string') {
        request.request_id = body.request_id;
    }
    if ('tool_choice' in body)
        request.tool_choice = body.tool_choice;
    const normalizedTools = mapAnthropicToolsToChat(body.tools);
    if (normalizedTools !== undefined) {
        request.tools = normalizedTools;
    }
    try {
        const bridgePolicy = resolveBridgePolicy({ protocol: 'anthropic-messages' });
        const actions = resolvePolicyActions(bridgePolicy, 'request_inbound');
        if (actions?.length) {
            const actionState = createBridgeActionState({
                messages: newMessages,
                rawRequest: body
            });
            runBridgeActionPipeline({
                stage: 'request_inbound',
                actions,
                protocol: bridgePolicy?.protocol ?? 'anthropic-messages',
                moduleType: bridgePolicy?.moduleType ?? 'anthropic-messages',
                requestId: typeof body?.id === 'string' ? String(body.id) : undefined,
                state: actionState
            });
        }
    }
    catch {
        // ignore policy failures
    }
    stripToolCallIdFieldsFromAssistant(newMessages);
    stripOpenAIChatToolAliasFields(newMessages);
    return request;
}
export function buildAnthropicFromOpenAIChat(oa, options) {
    const mapFinishReason = (reason) => {
        if (typeof reason !== 'string' || !reason.trim().length) {
            return undefined;
        }
        const mapping = {
            stop: 'end_turn',
            length: 'max_tokens',
            tool_calls: 'tool_use',
            content_filter: 'stop_sequence',
            function_call: 'tool_use'
        };
        return mapping[reason.trim()];
    };
    const body = isObject(oa) ? oa : {};
    const choices = Array.isArray(body.choices) ? body.choices : [];
    const primary = choices[0] && typeof choices[0] === 'object' ? choices[0] : {};
    const msg = isObject(primary.message) ? primary.message : {};
    const role = typeof msg.role === 'string' ? msg.role : 'assistant';
    const blocks = [];
    const content = msg?.content;
    const contentArray = Array.isArray(content) ? content : undefined;
    if (typeof content === 'string') {
        blocks.push({ type: 'text', text: content });
    }
    if (contentArray) {
        const text = contentArray
            .map((p) => p && typeof p.text === 'string'
            ? String(p.text)
            : typeof p === 'string'
                ? p
                : '')
            .filter(Boolean)
            .join('');
        if (text) {
            blocks.push({ type: 'text', text });
        }
    }
    const extensionReasoning = body?.__responses_reasoning;
    const extensionContent = Array.isArray(extensionReasoning?.content)
        ? extensionReasoning?.content?.map((entry) => String(entry.text ?? '')).filter((text) => text.trim().length > 0)
        : [];
    const extensionSummary = Array.isArray(extensionReasoning?.summary)
        ? extensionReasoning?.summary?.map((entry) => String(entry.text ?? '')).filter((text) => text.trim().length > 0)
        : [];
    const extensionText = extensionContent.length > 0
        ? extensionContent.join('\n')
        : (extensionSummary.length > 0 ? extensionSummary.join('\n') : undefined);
    const reasoningField = extensionText ??
        (typeof msg?.reasoning_content === 'string'
            ? msg.reasoning_content
            : typeof msg?.reasoning === 'string'
                ? msg.reasoning
                : undefined);
    if (reasoningField && reasoningField.trim().length) {
        blocks.push({ type: 'thinking', text: reasoningField.trim() });
    }
    const toolCalls = Array.isArray(msg?.tool_calls) ? msg.tool_calls : [];
    const toolNameResolver = createAnthropicToolNameResolver(options?.toolNameMap ?? extractToolNameMapFromPayload(oa));
    for (const tc of toolCalls) {
        try {
            const id = sanitizeToolUseId(requireTrimmedString(tc?.id, 'chat.tool_call.id'));
            const fn = isObject(tc?.function) ? tc.function : {};
            const canonicalName = requireTrimmedString(fn.name, 'chat.tool_call.function.name');
            const name = toolNameResolver ? toolNameResolver(canonicalName) : canonicalName;
            const argsRaw = fn.arguments;
            let input;
            if (typeof argsRaw === 'string') {
                const parsed = parseLenientJsonishWithNative(argsRaw);
                input = parsed && typeof parsed === 'object' ? parsed : { _raw: argsRaw };
            }
            else {
                input = argsRaw ?? {};
            }
            input = normalizeShellLikeToolInput(name, input);
            blocks.push({ type: 'tool_use', id, name, input });
        }
        catch {
            // ignore malformed tool call
        }
    }
    const usageChunk = isObject(body.usage) ? body.usage : {};
    const inputTokens = Number(usageChunk.prompt_tokens ?? usageChunk.input_tokens ?? 0);
    const outputTokens = Number(usageChunk.completion_tokens ?? usageChunk.output_tokens ?? 0);
    const finishReason = Array.isArray(body.choices) &&
        body.choices[0] &&
        typeof body.choices[0]?.finish_reason === 'string'
        ? String(body.choices[0].finish_reason).trim()
        : undefined;
    const stopReasonCandidate = mapFinishReason(finishReason) ||
        mapFinishReason(typeof primary?.finish_reason === 'string' ? String(primary.finish_reason) : undefined) ||
        (typeof body?.stop_reason === 'string' ? String(body.stop_reason).trim() : undefined) ||
        (typeof primary?.stop_reason === 'string' ? String(primary.stop_reason).trim() : undefined);
    const hasToolCalls = toolCalls.length > 0;
    const stopReason = stopReasonCandidate ?? (hasToolCalls ? 'tool_use' : 'end_turn');
    const resolveResponseId = () => {
        const preferred = [
            body?.id,
            body?.response?.id,
            body?.response_id
        ];
        for (const candidateRaw of preferred) {
            if (typeof candidateRaw !== 'string')
                continue;
            const candidate = candidateRaw.trim();
            if (!candidate.length)
                continue;
            if (candidate.startsWith('resp_')) {
                return candidate;
            }
        }
        const fromRequest = typeof options?.requestId === 'string' && options.requestId.trim().startsWith('resp_')
            ? options.requestId.trim()
            : undefined;
        return fromRequest ?? `resp_${Date.now()}`;
    };
    const resolveCreated = () => {
        const candidateNumbers = [
            body?.created,
            body?.created_at,
            body?.response?.created,
            body?.response?.created_at
        ];
        for (const candidate of candidateNumbers) {
            if (typeof candidate === 'number' && Number.isFinite(candidate)) {
                return Math.floor(candidate);
            }
        }
        return Math.floor(Date.now() / 1000);
    };
    return {
        id: resolveResponseId(),
        type: 'message',
        role,
        model: String(body.model || 'unknown'),
        created: resolveCreated(),
        content: blocks,
        usage: inputTokens || outputTokens ? { input_tokens: inputTokens, output_tokens: outputTokens } : undefined,
        stop_reason: stopReason
    };
}
function extractToolNameMapFromPayload(payload) {
    if (!payload || typeof payload !== 'object') {
        return undefined;
    }
    const candidateSources = [
        payload.anthropicToolNameMap,
        payload.__anthropicToolNameMap,
        payload.metadata &&
            typeof payload.metadata === 'object'
            ? payload.metadata.anthropicToolNameMap
            : undefined,
        payload.metadata &&
            typeof payload.metadata === 'object'
            ? payload.metadata.extraFields &&
                typeof payload.metadata.extraFields === 'object'
                ? payload.metadata.extraFields.anthropicToolNameMap
                : undefined
            : undefined,
        payload.metadata &&
            typeof payload.metadata === 'object' &&
            payload.metadata.capturedContext &&
            typeof payload.metadata.capturedContext === 'object'
            ? (payload.metadata.capturedContext.__hub_capture &&
                typeof payload.metadata.capturedContext.__hub_capture === 'object'
                ? payload.metadata.capturedContext.__hub_capture.extraFields &&
                    typeof payload.metadata.capturedContext.__hub_capture.extraFields === 'object'
                    ? payload.metadata.capturedContext.__hub_capture.extraFields
                    : undefined
                : undefined)
            : undefined
    ];
    for (const candidate of candidateSources) {
        const map = coerceAliasRecord(candidate);
        if (map) {
            return map;
        }
    }
    return undefined;
}
function coerceAliasRecord(candidate) {
    if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
        return undefined;
    }
    let hasEntry = false;
    const output = {};
    for (const [key, value] of Object.entries(candidate)) {
        if (typeof key !== 'string' || typeof value !== 'string') {
            continue;
        }
        const trimmedKey = key.trim();
        if (!trimmedKey.length) {
            continue;
        }
        output[trimmedKey] = value;
        hasEntry = true;
    }
    return hasEntry ? output : undefined;
}
function createAnthropicToolNameResolver(source) {
    if (!source) {
        return undefined;
    }
    const lookup = new Map();
    for (const [key, value] of Object.entries(source)) {
        if (typeof key !== 'string' || typeof value !== 'string')
            continue;
        const canonical = key.trim();
        if (!canonical.length)
            continue;
        const alias = value.trim() || canonical;
        lookup.set(canonical, alias);
        const lower = canonical.toLowerCase();
        if (!lookup.has(lower)) {
            lookup.set(lower, alias);
        }
    }
    if (!lookup.size) {
        return undefined;
    }
    return (name) => {
        const trimmed = name.trim();
        if (!trimmed.length) {
            return name;
        }
        const direct = lookup.get(trimmed) ?? lookup.get(trimmed.toLowerCase());
        if (direct) {
            return direct;
        }
        const normalized = normalizeAnthropicToolName(trimmed);
        if (normalized) {
            return lookup.get(normalized) ?? lookup.get(normalized.toLowerCase()) ?? trimmed;
        }
        return trimmed;
    };
}
function normalizeAnthropicToolChoice(value) {
    if (value === undefined || value === null) {
        return undefined;
    }
    if (isPlainRecord(value)) {
        // Already an object – best-effort clone while trimming type, and also support
        // Chat-style { type: 'function', function: { name } } by mapping to Anthropic's
        // { type: 'tool', name } shape.
        const cloned = cloneAnthropicSchema(value);
        const rawType = typeof cloned.type === 'string' ? String(cloned.type).trim() : '';
        if (rawType) {
            cloned.type = rawType;
            return cloned;
        }
        const selectorType = typeof cloned.type === 'string' ? String(cloned.type).trim() : '';
        const fn = cloned.function;
        if (selectorType === 'function' &&
            fn &&
            typeof fn === 'object' &&
            typeof fn.name === 'string' &&
            String(fn.name).trim().length) {
            return { type: 'tool', name: String(fn.name).trim() };
        }
        return cloned;
    }
    if (typeof value === 'string') {
        const trimmed = value.trim();
        if (!trimmed.length) {
            return undefined;
        }
        const lower = trimmed.toLowerCase();
        if (lower === 'auto') {
            return { type: 'auto' };
        }
        if (lower === 'none') {
            return { type: 'none' };
        }
        if (lower === 'any') {
            return { type: 'any' };
        }
        if (lower === 'required') {
            // "required" in canonical Chat roughly maps to Anthropic's "any" semantics:
            // the model must choose some tool if available.
            return { type: 'any' };
        }
        // Fallback: preserve custom mode as-is in type field.
        return { type: trimmed };
    }
    return undefined;
}
export function buildAnthropicRequestFromOpenAIChat(chatReq) {
    const requestBody = isObject(chatReq) ? chatReq : {};
    const model = String(requestBody?.model || 'unknown');
    const messages = [];
    try {
        const bridgePolicy = resolveBridgePolicy({ protocol: 'anthropic-messages' });
        const actions = resolvePolicyActions(bridgePolicy, 'request_outbound');
        if (actions?.length && Array.isArray(requestBody.messages)) {
            const actionState = createBridgeActionState({
                messages: requestBody.messages,
                rawRequest: requestBody
            });
            runBridgeActionPipeline({
                stage: 'request_outbound',
                actions,
                protocol: bridgePolicy?.protocol ?? 'anthropic-messages',
                moduleType: bridgePolicy?.moduleType ?? 'anthropic-messages',
                state: actionState
            });
        }
    }
    catch {
        // ignore policy errors
    }
    const collectText = (val) => {
        if (!val)
            return '';
        if (typeof val === 'string')
            return val;
        if (Array.isArray(val))
            return val.map(collectText).join('');
        if (typeof val === 'object') {
            if (typeof val.text === 'string')
                return String(val.text);
            if (Array.isArray(val.content))
                return collectText(val.content);
        }
        return '';
    };
    const msgs = Array.isArray(requestBody?.messages) ? requestBody.messages : [];
    const mirrorShapes = extractMirrorShapesFromRequest(requestBody);
    let mirrorIndex = 0;
    const knownToolCallIds = new Set();
    for (const m of msgs) {
        if (!m || typeof m !== 'object')
            continue;
        const role = String(m.role || 'user');
        if (role !== 'assistant')
            continue;
        const toolCalls = m.tool_calls;
        if (!Array.isArray(toolCalls))
            continue;
        for (const tc of toolCalls) {
            if (!tc || typeof tc !== 'object')
                continue;
            const id = sanitizeToolUseId(requireTrimmedString(tc.id, 'chat.tool_call.id'));
            knownToolCallIds.add(id);
        }
    }
    const systemBlocks = [];
    const pushSystemBlock = (text) => {
        const trimmed = text.trim();
        if (trimmed)
            systemBlocks.push({ type: 'text', text: trimmed });
    };
    try {
        const sys = requestBody.system;
        const ingestSystem = (val) => {
            if (!val)
                return;
            if (typeof val === 'string') {
                pushSystemBlock(requireSystemText(val, 'top-level system'));
                return;
            }
            if (Array.isArray(val)) {
                for (const entry of val)
                    ingestSystem(entry);
                return;
            }
            if (typeof val === 'object') {
                pushSystemBlock(requireSystemText(val, 'top-level system'));
                return;
            }
            throw new ProviderProtocolError('Anthropic bridge constraint violated: unsupported system payload type', {
                code: 'MALFORMED_REQUEST',
                protocol: 'anthropic-messages',
                providerType: 'anthropic',
                details: { context: 'top-level system', actualType: typeof val }
            });
        };
        ingestSystem(sys);
    }
    catch {
        // ignore system pre-scan errors
    }
    for (const m of msgs) {
        if (!m || typeof m !== 'object')
            continue;
        const role = String(m.role || 'user');
        let targetShape;
        if (role !== 'system' && Array.isArray(mirrorShapes)) {
            targetShape = mirrorShapes[mirrorIndex];
            mirrorIndex += 1;
        }
        const contentNode = m.content;
        const text = collectText(contentNode).trim();
        if (role === 'system') {
            if (!text) {
                throw new ProviderProtocolError('Anthropic bridge constraint violated: Chat system message must contain text', {
                    code: 'MALFORMED_REQUEST',
                    protocol: 'anthropic-messages',
                    providerType: 'anthropic',
                    details: { context: 'chat.system', original: contentNode }
                });
            }
            pushSystemBlock(text);
            continue;
        }
        if (role === 'tool') {
            const toolCallId = sanitizeToolUseId(requireTrimmedString(m.tool_call_id ?? m.call_id ?? m.tool_use_id ?? m.id, 'tool_result.tool_call_id'));
            if (!knownToolCallIds.has(toolCallId)) {
                throw new ProviderProtocolError(`Anthropic bridge constraint violated: tool result ${toolCallId} has no matching tool call`, {
                    code: 'TOOL_PROTOCOL_ERROR',
                    protocol: 'anthropic-messages',
                    providerType: 'anthropic',
                    details: { toolCallId }
                });
            }
            const block = {
                type: 'tool_result',
                content: text
            };
            block.tool_use_id = toolCallId;
            messages.push({
                role: 'user',
                content: [block]
            });
            continue;
        }
        const blocks = [];
        if (Array.isArray(contentNode)) {
            // Preserve or synthesize image blocks where possible, and fall back to text for the rest.
            for (const entry of contentNode) {
                if (!entry || typeof entry !== 'object')
                    continue;
                const node = entry;
                const t = typeof node.type === 'string' ? node.type.toLowerCase() : '';
                if (t === 'image' && node.source && typeof node.source === 'object') {
                    // Pass-through Anthropic image block as-is.
                    blocks.push({
                        type: 'image',
                        source: jsonClone(node.source)
                    });
                    continue;
                }
                if (t === 'image_url') {
                    let url = '';
                    const imageUrl = node.image_url;
                    if (typeof imageUrl === 'string') {
                        url = imageUrl;
                    }
                    else if (imageUrl && typeof imageUrl === 'object' && typeof imageUrl.url === 'string') {
                        url = imageUrl.url;
                    }
                    const trimmed = url.trim();
                    if (!trimmed.length)
                        continue;
                    const source = {};
                    if (trimmed.startsWith('data:')) {
                        const match = /^data:([^;,]+)?(?:;base64)?,(.*)$/s.exec(trimmed);
                        if (match) {
                            const mediaType = (match[1] || '').trim() || 'image/png';
                            source.type = 'base64';
                            source.media_type = mediaType;
                            source.data = match[2] || '';
                        }
                        else {
                            source.type = 'url';
                            source.url = trimmed;
                        }
                    }
                    else {
                        source.type = 'url';
                        source.url = trimmed;
                    }
                    blocks.push({
                        type: 'image',
                        source
                    });
                }
            }
        }
        if (text) {
            blocks.push({ type: 'text', text });
        }
        const toolCalls = Array.isArray(m.tool_calls) ? m.tool_calls : [];
        for (const tc of toolCalls) {
            if (!tc || typeof tc !== 'object')
                continue;
            const id = sanitizeToolUseId(requireTrimmedString(tc.id, 'chat.tool_call.id'));
            const fn = tc.function || {};
            const name = requireTrimmedString(fn.name, 'chat.tool_call.function.name');
            const argsRaw = fn.arguments;
            let input;
            if (typeof argsRaw === 'string') {
                const parsed = parseLenientJsonishWithNative(argsRaw);
                input = parsed && typeof parsed === 'object' ? parsed : { _raw: argsRaw };
            }
            else {
                input = argsRaw ?? {};
            }
            input = normalizeShellLikeToolInput(name, input);
            blocks.push({ type: 'tool_use', id, name, input });
        }
        if (blocks.length > 0) {
            const hasStructuredBlocks = blocks.some((block) => block && typeof block === 'object' && block.type !== 'text');
            let contentNode = blocks;
            if (!hasStructuredBlocks && (targetShape === 'string' || !targetShape)) {
                contentNode = text;
            }
            messages.push({ role, content: contentNode });
        }
    }
    const out = { model };
    if (systemBlocks.length) {
        out.system = systemBlocks;
    }
    out.messages = messages;
    const anthropicTools = mapChatToolsToAnthropicTools(requestBody.tools);
    if (anthropicTools !== undefined) {
        out.tools = anthropicTools;
    }
    const normalizedToolChoice = normalizeAnthropicToolChoice(requestBody.tool_choice);
    if (normalizedToolChoice !== undefined) {
        out.tool_choice = normalizedToolChoice;
    }
    if (requestBody.thinking !== undefined) {
        try {
            out.thinking = JSON.parse(JSON.stringify(requestBody.thinking));
        }
        catch {
            out.thinking = requestBody.thinking;
        }
    }
    try {
        if (requestBody.metadata && typeof requestBody.metadata === 'object') {
            out.metadata = JSON.parse(JSON.stringify(requestBody.metadata));
        }
    }
    catch {
        // best-effort metadata clone
    }
    const mt = Number(requestBody.max_tokens ??
        requestBody.maxTokens ??
        NaN);
    if (Number.isFinite(mt) && mt > 0)
        out.max_tokens = mt;
    if (typeof requestBody.temperature === 'number') {
        out.temperature = Number(requestBody.temperature);
    }
    if (typeof requestBody.top_p === 'number') {
        out.top_p = Number(requestBody.top_p);
    }
    if (typeof requestBody.stream === 'boolean') {
        out.stream = Boolean(requestBody.stream);
    }
    const stop = requestBody.stop;
    if (typeof stop === 'string' && stop.trim()) {
        out.stop_sequences = [stop.trim()];
    }
    else if (Array.isArray(stop) && stop.length > 0) {
        out.stop_sequences = stop.map((s) => String(s)).filter(Boolean);
    }
    return pruneAnthropicRequest(out);
}
function isPlainRecord(value) {
    return !!value && typeof value === 'object' && !Array.isArray(value);
}
function coerceJsonValue(value) {
    if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
        return value;
    }
    if (Array.isArray(value)) {
        return value.map((entry) => coerceJsonValue(entry));
    }
    if (isPlainRecord(value)) {
        const obj = {};
        for (const [key, entry] of Object.entries(value)) {
            obj[key] = coerceJsonValue(entry);
        }
        return obj;
    }
    return String(value ?? '');
}
function cloneAnthropicSchema(value) {
    if (isPlainRecord(value)) {
        try {
            return JSON.parse(JSON.stringify(value));
        }
        catch {
            return value;
        }
    }
    return { type: 'object', properties: {} };
}
function normalizeAnthropicSchemaType(value) {
    if (typeof value === 'string') {
        const trimmed = value.trim().toLowerCase();
        if (trimmed === 'string' ||
            trimmed === 'number' ||
            trimmed === 'integer' ||
            trimmed === 'boolean' ||
            trimmed === 'object' ||
            trimmed === 'array') {
            return trimmed;
        }
        return undefined;
    }
    if (Array.isArray(value)) {
        for (const entry of value) {
            const normalized = normalizeAnthropicSchemaType(entry);
            if (normalized) {
                return normalized;
            }
        }
    }
    return undefined;
}
function compactAnthropicPropertySchema(schema) {
    if (!isPlainRecord(schema)) {
        return { type: 'string' };
    }
    const out = {};
    const type = normalizeAnthropicSchemaType(schema.type);
    if (type) {
        out.type = type;
    }
    if (typeof schema.description === 'string' && schema.description.trim()) {
        out.description = schema.description;
    }
    const enumRaw = Array.isArray(schema.enum) ? schema.enum : undefined;
    if (enumRaw && enumRaw.length) {
        const enumValues = enumRaw
            .filter((entry) => {
            const entryType = typeof entry;
            return entryType === 'string' || entryType === 'number' || entryType === 'boolean';
        })
            .slice(0, 64);
        if (enumValues.length) {
            out.enum = enumValues;
            if (!out.type) {
                const inferred = typeof enumValues[0];
                out.type = inferred === 'boolean' ? 'boolean' : inferred === 'number' ? 'number' : 'string';
            }
        }
    }
    if (out.type === 'array') {
        const items = isPlainRecord(schema.items) ? schema.items : {};
        const itemType = normalizeAnthropicSchemaType(items.type) ?? 'string';
        out.items = { type: itemType };
    }
    else if (out.type === 'object') {
        out.properties = {};
        out.additionalProperties = false;
    }
    if (!out.type) {
        out.type = 'string';
    }
    return out;
}
function sanitizeAnthropicBuiltinInputSchema(toolName, schemaSource) {
    const normalizedName = toolName.trim().toLowerCase();
    if (!ANTHROPIC_STABLE_TOOL_SCHEMA_NAMES.has(normalizedName)) {
        return cloneAnthropicSchema(schemaSource);
    }
    const source = cloneAnthropicSchema(schemaSource);
    const sourceProperties = isPlainRecord(source.properties) ? source.properties : {};
    const allowedKeys = ANTHROPIC_STABLE_TOOL_SCHEMA_KEYS.get(normalizedName);
    const sanitizedProperties = {};
    for (const [key, value] of Object.entries(sourceProperties)) {
        if (allowedKeys && !allowedKeys.has(key)) {
            continue;
        }
        sanitizedProperties[key] = compactAnthropicPropertySchema(value);
    }
    const required = Array.isArray(source.required)
        ? source.required.filter((entry) => typeof entry === 'string' && entry.trim().length > 0)
        : [];
    const filteredRequired = allowedKeys
        ? required.filter((key) => allowedKeys.has(key))
        : required;
    for (const key of filteredRequired) {
        if (!Object.prototype.hasOwnProperty.call(sanitizedProperties, key)) {
            sanitizedProperties[key] = { type: 'string' };
        }
    }
    const output = {
        type: 'object',
        properties: sanitizedProperties,
        additionalProperties: false
    };
    if (filteredRequired.length) {
        output.required = Array.from(new Set(filteredRequired));
    }
    return output;
}
function prepareAnthropicBridgeTools(rawTools, missing) {
    if (!Array.isArray(rawTools) || rawTools.length === 0) {
        return undefined;
    }
    const result = [];
    rawTools.forEach((entry, index) => {
        if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
            missing?.push({
                path: `tools[${index}]`,
                reason: 'invalid_entry',
                originalValue: jsonClone(coerceJsonValue(entry))
            });
            return;
        }
        const name = typeof entry.name === 'string'
            ? entry.name
            : undefined;
        if (!name) {
            missing?.push({ path: `tools[${index}].name`, reason: 'missing_name' });
            return;
        }
        const description = typeof entry.description === 'string'
            ? entry.description
            : undefined;
        const schemaSource = entry.input_schema;
        const parameters = cloneAnthropicSchema(schemaSource);
        result.push({
            type: 'function',
            function: {
                name,
                description,
                parameters
            }
        });
    });
    return result.length ? result : undefined;
}
function convertBridgeToolToAnthropic(def) {
    if (!def || typeof def !== 'object') {
        return null;
    }
    const fnNode = def.function && typeof def.function === 'object' ? def.function : undefined;
    const name = typeof fnNode?.name === 'string'
        ? fnNode.name
        : typeof def.name === 'string'
            ? def.name
            : undefined;
    if (!name) {
        return null;
    }
    const description = typeof fnNode?.description === 'string'
        ? fnNode.description
        : typeof def.description === 'string'
            ? def.description
            : undefined;
    const schemaSource = fnNode?.parameters ?? def.parameters;
    const inputSchema = sanitizeAnthropicBuiltinInputSchema(name, schemaSource);
    const tool = {
        name,
        input_schema: inputSchema
    };
    if (description !== undefined) {
        tool.description = description;
    }
    return tool;
}
export function mapAnthropicToolsToChat(rawTools, missing) {
    const prepared = prepareAnthropicBridgeTools(rawTools, missing);
    if (prepared === undefined) {
        return undefined;
    }
    return mapBridgeToolsToChat(prepared, { sanitizeName: normalizeAnthropicToolName });
}
export function mapChatToolsToAnthropicTools(rawTools) {
    if (!Array.isArray(rawTools) || rawTools.length === 0) {
        return undefined;
    }
    const bridgeDefs = mapChatToolsToBridge(rawTools, { sanitizeName: denormalizeAnthropicToolName });
    if (!bridgeDefs || !bridgeDefs.length) {
        return undefined;
    }
    const converted = bridgeDefs
        .map((def) => convertBridgeToolToAnthropic(def))
        .filter((entry) => !!entry);
    return converted.length ? converted : undefined;
}
function pruneAnthropicRequest(payload) {
    for (const key of Object.keys(payload)) {
        if (!ANTHROPIC_TOP_LEVEL_FIELDS.has(key)) {
            delete payload[key];
        }
    }
    return payload;
}
function extractMirrorShapesFromRequest(source) {
    const directMirror = source &&
        typeof source === 'object' &&
        source.__anthropicMirror &&
        typeof source.__anthropicMirror === 'object'
        ? source.__anthropicMirror
        : extractMirrorFromMetadata(source);
    if (!directMirror) {
        return undefined;
    }
    const shapes = directMirror.messageContentShape;
    if (!Array.isArray(shapes)) {
        return undefined;
    }
    return shapes.map((entry) => (typeof entry === 'string' ? entry : String(entry ?? '')));
}
function extractMirrorFromMetadata(source) {
    if (!source || typeof source !== 'object') {
        return undefined;
    }
    const metadata = source.metadata;
    if (!metadata || typeof metadata !== 'object') {
        return undefined;
    }
    const extraFields = metadata.extraFields;
    if (!extraFields || typeof extraFields !== 'object') {
        return undefined;
    }
    const mirror = extraFields.anthropicMirror;
    return mirror && typeof mirror === 'object' ? mirror : undefined;
}
export function buildAnthropicToolAliasMap(rawTools) {
    if (!Array.isArray(rawTools) || rawTools.length === 0) {
        return undefined;
    }
    const aliasMap = new Map();
    for (const entry of rawTools) {
        if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
            continue;
        }
        const rawName = typeof entry.name === 'string'
            ? entry.name.trim()
            : undefined;
        if (!rawName) {
            continue;
        }
        const normalized = normalizeAnthropicToolName(rawName) ?? rawName;
        const canonicalKey = normalized.trim();
        if (!canonicalKey.length) {
            continue;
        }
        aliasMap.set(canonicalKey, rawName);
        const lowered = canonicalKey.toLowerCase();
        const lowerKey = canonicalKey.toLowerCase();
        if (lowerKey !== canonicalKey && !aliasMap.has(lowerKey)) {
            aliasMap.set(lowerKey, rawName);
        }
    }
    if (!aliasMap.size) {
        return undefined;
    }
    const serialized = {};
    for (const [key, value] of aliasMap.entries()) {
        serialized[key] = value;
    }
    return serialized;
}
