import { trackToolCall, trackUserMessage } from "../core/tracking.js";
import { getActiveContext } from "../context.js";
import { calculateCost } from "../utils/costs.js";
import { getDefaultPropagateContext, injectContext } from "../propagation.js";
import { tryRequire } from "../utils/resolve-module.js";
import { StreamingAccumulator } from "../utils/streaming.js";
import { consumeToolUseLatencyMs, recordToolUsesFromResponse } from "../utils/tool-latency.js";
import { BaseAIProvider, applySessionContext, contextFields } from "./base.js";

//#region src/providers/anthropic.ts
/**
* Anthropic provider wrapper with automatic tracking.
*/
const _resolved = tryRequire("@anthropic-ai/sdk");
const ANTHROPIC_AVAILABLE = _resolved != null;
const _AnthropicModule = _resolved;
var Anthropic = class extends BaseAIProvider {
	_client;
	messages;
	_propagateContext;
	constructor(options) {
		super({
			amplitude: options.amplitude,
			privacyConfig: options.privacyConfig,
			providerName: "anthropic"
		});
		const mod = options.anthropicModule ?? _AnthropicModule;
		if (mod == null) throw new Error("@anthropic-ai/sdk package is required. Install it with: npm install @anthropic-ai/sdk — or pass the module directly via the anthropicModule option.");
		const AnthropicSDK = mod.Anthropic;
		const clientOpts = {};
		if (options.apiKey) clientOpts.apiKey = options.apiKey;
		this._client = new AnthropicSDK(clientOpts);
		this._propagateContext = options.propagateContext ?? getDefaultPropagateContext();
		this.messages = new WrappedMessages(this._client, this.trackFn(), this._amplitude, this._privacyConfig, this._propagateContext);
	}
	get client() {
		return this._client;
	}
};
var WrappedMessages = class {
	_original;
	_trackFn;
	_amplitude;
	_privacyConfig;
	_propagateContext;
	constructor(client, trackFn, amplitude, privacyConfig, propagateContext) {
		this._original = client.messages;
		this._trackFn = typeof trackFn === "function" ? trackFn : trackFn.trackFn();
		this._amplitude = amplitude;
		this._privacyConfig = privacyConfig;
		this._propagateContext = propagateContext;
	}
	async create(params, amplitudeOverrides) {
		const createFn = this._original.create;
		const startTime = performance.now();
		const requestParams = this._withContextHeaders(params);
		const ctx = applySessionContext(amplitudeOverrides);
		try {
			this._trackInputMessages(requestParams.messages, ctx, amplitudeOverrides?.trackInputMessages ?? true);
			const response = await createFn.call(this._original, requestParams);
			if (requestParams.stream === true && _isAsyncIterable(response)) return this._wrapStream(response, requestParams, startTime, ctx);
			const latencyMs = performance.now() - startTime;
			const resp = response;
			const usage = resp.usage;
			const extracted = extractAnthropicContent(resp.content);
			const firstTextBlock = resp.content?.find((b) => b.type === "text");
			const modelName = String(resp.model ?? requestParams.model ?? "unknown");
			const cacheRead = usage?.cache_read_input_tokens ?? 0;
			const cacheCreation = usage?.cache_creation_input_tokens ?? 0;
			const rawInput = usage?.input_tokens ?? 0;
			const normalizedInput = cacheRead || cacheCreation ? rawInput + cacheRead + cacheCreation : rawInput;
			let costUsd = null;
			if (usage?.input_tokens != null && usage?.output_tokens != null) try {
				costUsd = calculateCost({
					modelName,
					inputTokens: normalizedInput,
					outputTokens: usage.output_tokens,
					cacheReadInputTokens: cacheRead,
					cacheCreationInputTokens: cacheCreation,
					defaultProvider: "anthropic"
				});
			} catch {}
			this._trackFn({
				...contextFields(ctx),
				modelName,
				provider: "anthropic",
				responseContent: String(firstTextBlock?.text ?? ""),
				reasoningContent: extracted.reasoning,
				latencyMs,
				inputTokens: normalizedInput || void 0,
				outputTokens: usage?.output_tokens,
				cacheReadInputTokens: cacheRead || void 0,
				cacheCreationInputTokens: cacheCreation || void 0,
				totalCostUsd: costUsd,
				finishReason: resp.stop_reason,
				toolCalls: extracted.toolCalls.length > 0 ? extracted.toolCalls : void 0,
				isStreaming: false,
				toolDefinitions: extractAnthropicToolDefinitions(requestParams),
				systemPrompt: extractAnthropicSystemPrompt(requestParams.system),
				temperature: requestParams.temperature,
				maxOutputTokens: requestParams.max_tokens,
				topP: requestParams.top_p
			});
			recordToolUsesFromResponse(extracted.toolCalls, {
				sessionId: ctx.sessionId,
				agentId: ctx.agentId
			});
			return response;
		} catch (error) {
			const latencyMs = performance.now() - startTime;
			this._trackFn({
				...contextFields(ctx),
				modelName: String(requestParams.model ?? "unknown"),
				provider: "anthropic",
				responseContent: "",
				latencyMs,
				isError: true,
				errorMessage: error instanceof Error ? error.message : String(error)
			});
			throw error;
		}
	}
	async *_wrapStream(stream, params, _startTime, sessionCtx) {
		const accumulator = new StreamingAccumulator();
		accumulator.model = String(params.model ?? "unknown");
		let reasoningContent = "";
		try {
			for await (const event of stream) {
				const evt = event;
				const type = evt.type;
				if (type === "content_block_delta") {
					const delta = evt.delta;
					if (delta?.type === "text_delta" && delta.text != null) accumulator.addContent(String(delta.text));
					else if (delta?.type === "thinking_delta" && delta.thinking != null) reasoningContent += String(delta.thinking);
				} else if (type === "content_block_start") {
					const block = evt.content_block;
					if (block?.type === "tool_use") accumulator.addToolCall({
						type: "function",
						id: block.id,
						function: {
							name: String(block.name ?? ""),
							arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input ?? {})
						}
					});
				} else if (type === "message_delta") {
					const delta = evt.delta;
					if (delta?.stop_reason != null) accumulator.finishReason = String(delta.stop_reason);
					const usage = evt.usage;
					if (usage != null) accumulator.setUsage({ outputTokens: usage.output_tokens });
				} else if (type === "message_start") {
					const message = evt.message;
					if (message?.model != null) accumulator.model = String(message.model);
					const usage = message?.usage;
					if (usage != null) accumulator.setUsage({
						inputTokens: usage.input_tokens,
						cacheReadTokens: usage.cache_read_input_tokens,
						cacheCreationTokens: usage.cache_creation_input_tokens
					});
				}
				yield event;
			}
		} catch (error) {
			accumulator.setError(error instanceof Error ? error.message : String(error));
			throw error;
		} finally {
			const state = accumulator.getState();
			const modelName = String(accumulator.model ?? params.model ?? "unknown");
			const streamCacheRead = state.cacheReadTokens ?? 0;
			const streamCacheCreation = state.cacheCreationTokens ?? 0;
			const streamRawInput = state.inputTokens ?? 0;
			const streamNormalizedInput = streamCacheRead || streamCacheCreation ? streamRawInput + streamCacheRead + streamCacheCreation : streamRawInput;
			let costUsd = null;
			if (state.inputTokens != null && state.outputTokens != null) try {
				costUsd = calculateCost({
					modelName,
					inputTokens: streamNormalizedInput,
					outputTokens: state.outputTokens,
					cacheReadInputTokens: streamCacheRead,
					cacheCreationInputTokens: streamCacheCreation,
					defaultProvider: "anthropic"
				});
			} catch {}
			this._trackFn({
				...contextFields(sessionCtx),
				modelName,
				provider: "anthropic",
				responseContent: state.content,
				latencyMs: accumulator.elapsedMs,
				inputTokens: streamNormalizedInput || void 0,
				outputTokens: state.outputTokens,
				cacheReadInputTokens: streamCacheRead || void 0,
				cacheCreationInputTokens: streamCacheCreation || void 0,
				totalCostUsd: costUsd,
				finishReason: state.finishReason,
				toolCalls: state.toolCalls.length > 0 ? state.toolCalls : void 0,
				providerTtfbMs: state.ttfbMs,
				isStreaming: true,
				isError: state.isError,
				errorMessage: state.errorMessage,
				reasoningContent: reasoningContent || void 0,
				toolDefinitions: extractAnthropicToolDefinitions(params),
				systemPrompt: extractAnthropicSystemPrompt(params.system),
				temperature: params.temperature,
				maxOutputTokens: params.max_tokens,
				topP: params.top_p
			});
			recordToolUsesFromResponse(state.toolCalls, {
				sessionId: sessionCtx.sessionId,
				agentId: sessionCtx.agentId
			});
		}
	}
	_withContextHeaders(params) {
		if (!this._propagateContext) return params;
		const injected = injectContext(params.extra_headers ?? params.headers);
		return {
			...params,
			extra_headers: injected
		};
	}
	_trackInputMessages(messages, ctx, shouldTrackInputMessages) {
		if (!shouldTrackInputMessages) return;
		if (ctx.userId == null && ctx.deviceId == null || ctx.sessionId == null) return;
		if (getActiveContext()?.skipAutoUserTracking) return;
		if (!Array.isArray(messages)) return;
		const lastReplyIdx = messages.findLastIndex((m) => m?.role === "assistant");
		const newMessages = messages.slice(lastReplyIdx + 1);
		for (const msg of newMessages) {
			if (msg?.role !== "user") continue;
			const rawContent = msg?.content;
			const content = Array.isArray(rawContent) ? rawContent.map((part) => {
				if (typeof part === "string") return part;
				const text = part?.text;
				return typeof text === "string" ? text : "";
			}).join("") : typeof rawContent === "string" ? rawContent : "";
			if (!content) continue;
			trackUserMessage({
				amplitude: this._amplitude,
				userId: ctx.userId ?? void 0,
				deviceId: ctx.deviceId ?? void 0,
				messageContent: content,
				sessionId: ctx.sessionId,
				traceId: ctx.traceId,
				turnId: ctx.turnId ?? void 0,
				messageSource: ctx.parentAgentId ? "agent" : "user",
				agentId: ctx.agentId,
				parentAgentId: ctx.parentAgentId,
				customerOrgId: ctx.customerOrgId,
				agentVersion: ctx.agentVersion,
				context: ctx.context,
				env: ctx.env,
				groups: ctx.groups,
				eventProperties: ctx.eventProperties,
				privacyConfig: this._privacyConfig
			});
		}
		this._extractToolCalls(messages, ctx);
	}
	/**
	* Extract tool call events from Anthropic message arrays.
	*
	* Anthropic tool results appear as content blocks with type "tool_result"
	* inside role: "user" messages. We correlate tool_use_id with tool_use
	* blocks in the preceding assistant message to get tool names and inputs.
	*/
	_extractToolCalls(messages, ctx) {
		const lastAssistantIdx = messages.findLastIndex((m) => m?.role === "assistant");
		if (lastAssistantIdx < 0) return;
		const assistantMsg = messages[lastAssistantIdx];
		if (!assistantMsg) return;
		const assistantContent = assistantMsg.content;
		if (!Array.isArray(assistantContent)) return;
		const toolUseMap = /* @__PURE__ */ new Map();
		for (const block of assistantContent) if (block.type === "tool_use" && block.id) toolUseMap.set(String(block.id), {
			name: String(block.name ?? "unknown"),
			input: typeof block.input === "string" ? block.input : JSON.stringify(block.input ?? {})
		});
		if (toolUseMap.size === 0) return;
		for (let i = lastAssistantIdx + 1; i < messages.length; i++) {
			const msg = messages[i];
			if (msg?.role !== "user") continue;
			const content = msg.content;
			if (!Array.isArray(content)) continue;
			for (const block of content) {
				if (block.type !== "tool_result") continue;
				const toolUseId = String(block.tool_use_id ?? "");
				const matched = toolUseMap.get(toolUseId);
				const toolName = matched?.name ?? "unknown";
				const toolInput = matched?.input;
				const isError = block.is_error === true;
				const output = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((b) => typeof b.text === "string" ? b.text : "").join("") : void 0;
				const latencyMs = consumeToolUseLatencyMs({
					sessionId: ctx.sessionId,
					toolUseId,
					agentId: ctx.agentId
				});
				trackToolCall({
					amplitude: this._amplitude,
					userId: ctx.userId ?? void 0,
					deviceId: ctx.deviceId ?? void 0,
					toolName,
					success: !isError,
					latencyMs,
					sessionId: ctx.sessionId,
					traceId: ctx.traceId,
					turnId: ctx.turnId ?? void 0,
					toolInput: toolInput != null ? toolInput : void 0,
					toolOutput: output,
					errorMessage: isError ? output : void 0,
					agentId: ctx.agentId,
					parentAgentId: ctx.parentAgentId,
					customerOrgId: ctx.customerOrgId,
					agentVersion: ctx.agentVersion,
					context: ctx.context,
					env: ctx.env,
					groups: ctx.groups,
					eventProperties: ctx.eventProperties,
					privacyConfig: this._privacyConfig
				});
			}
		}
	}
};
function _isAsyncIterable(value) {
	return value != null && typeof value[Symbol.asyncIterator] === "function";
}
function extractAnthropicSystemPrompt(system) {
	if (typeof system === "string") return system;
	if (Array.isArray(system)) return system.map((block) => {
		if (typeof block === "string") return block;
		if (typeof block === "object" && block != null) return String(block.text ?? "");
		return "";
	}).filter(Boolean).join("\n");
}
function extractAnthropicContent(content) {
	let text = "";
	let reasoning;
	const toolCalls = [];
	if (!content) return {
		text,
		reasoning,
		toolCalls
	};
	for (const block of content) if (block.type === "text") text += String(block.text ?? "");
	else if (block.type === "thinking") reasoning = String(block.thinking ?? "");
	else if (block.type === "tool_use") toolCalls.push({
		type: "function",
		id: block.id,
		function: {
			name: String(block.name ?? ""),
			arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input ?? {})
		}
	});
	return {
		text,
		reasoning,
		toolCalls
	};
}
function extractAnthropicToolDefinitions(params) {
	const tools = params.tools;
	return Array.isArray(tools) && tools.length > 0 ? tools : void 0;
}

//#endregion
export { ANTHROPIC_AVAILABLE, Anthropic, WrappedMessages, _AnthropicModule, extractAnthropicContent, extractAnthropicSystemPrompt };
//# sourceMappingURL=anthropic.js.map