import { getLogger } from "./utils/logger.js";
import { trackSessionEnd, trackSpan, trackToolCall } from "./core/tracking.js";
import { SessionContext, _sessionStorage, getActiveContext } from "./context.js";
import { AMP_INPUT_STATE, AMP_OUTPUT_STATE, AMP_SPAN_KIND } from "./otel/conventions.js";
import { createRequire } from "node:module";
import { randomUUID } from "node:crypto";

//#region src/decorators.ts
/**
* Higher-order functions for automatic tracking of function calls as tool and span events.
*
* TypeScript equivalent of Python's @tool and @observe decorators.
* Since TS/JS does not have native decorators with the same power as Python,
* these are implemented as HOFs (higher-order functions) that wrap callables.
*/
const _require = createRequire(import.meta.url);
var ToolCallTracker = class ToolCallTracker {
	static _amplitude = null;
	static _userId = null;
	static _sessionId = null;
	static _traceId = null;
	static _turnId = 1;
	static _env = null;
	static _agentId = null;
	static _parentAgentId = null;
	static _customerOrgId = null;
	static _agentVersion = null;
	static _context = null;
	static _privacyConfig = null;
	static _eventProperties = null;
	static _userProperties = null;
	static _groups = null;
	static setAmplitude(amplitude, userId, opts = {}) {
		ToolCallTracker._amplitude = amplitude;
		ToolCallTracker._userId = userId;
		ToolCallTracker._sessionId = opts.sessionId ?? null;
		ToolCallTracker._traceId = opts.traceId ?? null;
		ToolCallTracker._turnId = opts.turnId ?? 1;
		ToolCallTracker._env = opts.env ?? null;
		ToolCallTracker._agentId = opts.agentId ?? null;
		ToolCallTracker._parentAgentId = opts.parentAgentId ?? null;
		ToolCallTracker._customerOrgId = opts.customerOrgId ?? null;
		ToolCallTracker._agentVersion = opts.agentVersion ?? null;
		ToolCallTracker._context = opts.context ?? null;
		ToolCallTracker._privacyConfig = opts.privacyConfig ?? null;
		ToolCallTracker._eventProperties = opts.eventProperties ?? null;
		ToolCallTracker._userProperties = opts.userProperties ?? null;
		ToolCallTracker._groups = opts.groups ?? null;
	}
	static clear() {
		ToolCallTracker._amplitude = null;
		ToolCallTracker._userId = null;
		ToolCallTracker._sessionId = null;
		ToolCallTracker._traceId = null;
		ToolCallTracker._turnId = 1;
		ToolCallTracker._env = null;
		ToolCallTracker._agentId = null;
		ToolCallTracker._parentAgentId = null;
		ToolCallTracker._customerOrgId = null;
		ToolCallTracker._agentVersion = null;
		ToolCallTracker._context = null;
		ToolCallTracker._privacyConfig = null;
		ToolCallTracker._eventProperties = null;
		ToolCallTracker._userProperties = null;
		ToolCallTracker._groups = null;
	}
	static isConfigured() {
		return ToolCallTracker._amplitude != null && ToolCallTracker._userId != null;
	}
};
function _resolveContextFields(opts) {
	const ctx = getActiveContext();
	function resolve(decoratorVal, trackerVal, ctxVal = null) {
		if (decoratorVal !== void 0 && decoratorVal !== null) return decoratorVal;
		if (ctxVal !== void 0 && ctxVal !== null) return ctxVal;
		if (trackerVal !== void 0 && trackerVal !== null) return trackerVal;
		return null;
	}
	return {
		amplitude: opts.amplitude ?? ctx?.amplitude ?? ToolCallTracker._amplitude ?? null,
		userId: resolve(opts.userId, ToolCallTracker._userId, ctx?.userId),
		sessionId: resolve(opts.sessionId, ToolCallTracker._sessionId, ctx?.sessionId),
		traceId: resolve(opts.traceId, ToolCallTracker._traceId, ctx?.traceId),
		turnId: opts.turnId ?? ToolCallTracker._turnId,
		env: resolve(opts.env, ToolCallTracker._env, ctx?.env),
		agentId: resolve(opts.agentId, ToolCallTracker._agentId, ctx?.agentId),
		parentAgentId: resolve(opts.parentAgentId, ToolCallTracker._parentAgentId, ctx?.parentAgentId),
		customerOrgId: resolve(opts.customerOrgId, ToolCallTracker._customerOrgId, ctx?.customerOrgId),
		agentVersion: resolve(opts.agentVersion, ToolCallTracker._agentVersion, ctx?.agentVersion),
		context: resolve(opts.context, ToolCallTracker._context, ctx?.context),
		privacyConfig: resolve(opts.privacyConfig, ToolCallTracker._privacyConfig),
		eventProperties: resolve(opts.eventProperties, ToolCallTracker._eventProperties),
		userProperties: resolve(opts.userProperties, ToolCallTracker._userProperties),
		groups: resolve(opts.groups, ToolCallTracker._groups, ctx?.groups)
	};
}
function tool(fnOrOpts, maybeOpts) {
	if (typeof fnOrOpts === "function") return _wrapTool(fnOrOpts, maybeOpts ?? {});
	return (fn) => _wrapTool(fn, fnOrOpts);
}
function _wrapTool(fn, opts) {
	const toolName = opts.name || fn.name || "anonymous";
	const schemaJson = opts.inputSchema ? JSON.stringify(opts.inputSchema) : null;
	const wrapper = async function(...args) {
		const r = _resolveContextFields(opts);
		if (r.amplitude == null || r.userId == null) {
			getLogger().warn(`Tool '${toolName}' called but tracking not configured. Call ToolCallTracker.setAmplitude() or pass amplitude/userId to tool().`);
			return fn.apply(this, args);
		}
		const toolInput = args.length === 1 && typeof args[0] === "object" && args[0] != null ? args[0] : args.length > 0 ? { args } : void 0;
		const startTime = performance.now();
		let success = true;
		let errorMsg = null;
		let errorStack = null;
		let result = void 0;
		try {
			if (opts.timeoutMs != null) result = await _runWithTimeout(fn.bind(this, ...args), opts.timeoutMs, toolName);
			else result = await fn.apply(this, args);
			return result;
		} catch (e) {
			success = false;
			errorMsg = e instanceof Error ? e.message : String(e);
			errorStack = e instanceof Error ? e.stack ?? null : null;
			if (opts.onError != null) try {
				opts.onError(e instanceof Error ? e : new Error(String(e)), toolName);
			} catch {}
			getLogger().error(`Tool '${toolName}' failed: ${errorMsg}`);
			throw e;
		} finally {
			const latencyMs = performance.now() - startTime;
			const extraProps = {};
			if (schemaJson != null) extraProps["[Agent] Tool Input Schema"] = schemaJson;
			const mergedEvtProps = {
				...r.eventProperties ?? {},
				...extraProps
			};
			const captureStack = r.privacyConfig != null && r.privacyConfig.captureStackTrace === true;
			try {
				trackToolCall({
					amplitude: r.amplitude,
					userId: r.userId,
					toolName,
					toolType: opts.toolType,
					toolOwner: opts.toolOwner,
					success,
					latencyMs,
					sessionId: r.sessionId,
					traceId: r.traceId,
					turnId: r.turnId ?? void 0,
					toolInput,
					toolOutput: success ? result : void 0,
					errorMessage: errorMsg,
					stackTrace: captureStack && errorStack ? errorStack : void 0,
					env: r.env,
					agentId: r.agentId,
					parentAgentId: r.parentAgentId,
					customerOrgId: r.customerOrgId,
					agentVersion: r.agentVersion,
					context: r.context,
					eventProperties: Object.keys(extraProps).length > 0 ? mergedEvtProps : r.eventProperties,
					userProperties: r.userProperties,
					groups: r.groups,
					privacyConfig: r.privacyConfig
				});
			} catch (trackError) {
				getLogger().error(`Failed to track tool call '${toolName}': ${trackError}`);
			}
		}
	};
	Object.defineProperty(wrapper, "name", {
		value: fn.name,
		configurable: true
	});
	return wrapper;
}
async function _runWithTimeout(fn, timeoutMs, toolName) {
	let timer;
	const timeoutPromise = new Promise((_resolve, reject) => {
		timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Tool '${toolName}' exceeded ${timeoutMs}ms timeout`)), timeoutMs);
	});
	try {
		return await Promise.race([fn(), timeoutPromise]);
	} finally {
		clearTimeout(timer);
	}
}
function observe(fnOrOpts, maybeOpts) {
	if (typeof fnOrOpts === "function") return _wrapObserve(fnOrOpts, maybeOpts ?? {});
	return (fn) => _wrapObserve(fn, fnOrOpts);
}
function _resolveObserveParams(opts) {
	const ctx = getActiveContext();
	return {
		amplitude: opts.amplitude ?? ctx?.amplitude ?? ToolCallTracker._amplitude ?? null,
		userId: opts.userId ?? ctx?.userId ?? ToolCallTracker._userId ?? "",
		agentId: opts.agentId ?? ctx?.agentId ?? ToolCallTracker._agentId ?? null,
		env: opts.env ?? ctx?.env ?? ToolCallTracker._env ?? null,
		privacyConfig: opts.privacyConfig ?? ToolCallTracker._privacyConfig ?? null,
		context: ctx
	};
}
function _serializeState(value, pc) {
	if (value == null) return null;
	if (pc != null) {
		const mode = pc.contentMode;
		if (mode === "metadata_only" || mode == null && pc.privacyMode) return null;
	}
	if (typeof value === "object" && !Array.isArray(value)) return value;
	return { value: String(value) };
}
function _getOtelTracer() {
	try {
		const provider = _require("@opentelemetry/api").trace.getTracerProvider();
		const delegateName = provider._delegate?.constructor?.name;
		const providerName = provider.constructor?.name;
		if (delegateName === "BasicTracerProvider" || delegateName === "NodeTracerProvider" || providerName === "BasicTracerProvider" || providerName === "NodeTracerProvider") return provider.getTracer?.("@amplitude/ai") ?? null;
		return null;
	} catch {
		return null;
	}
}
function _wrapObserve(fn, opts) {
	const spanName = opts.name || fn.name || "anonymous";
	const spanKind = opts.type ?? "span";
	const wrapper = async function(...args) {
		const params = _resolveObserveParams(opts);
		const ctx = getActiveContext();
		const ownsSession = ctx == null;
		const sessionId = ctx?.sessionId ?? randomUUID();
		const runFn = async () => {
			const inputState = _serializeState(args.length === 1 ? args[0] : args.length > 0 ? { args } : null, params.privacyConfig);
			const tracer = _getOtelTracer();
			if (tracer != null) return tracer.startActiveSpan(spanName, async (span) => {
				span.setAttribute(AMP_SPAN_KIND, spanKind);
				if (inputState != null) span.setAttribute(AMP_INPUT_STATE, JSON.stringify(inputState));
				let result$1 = void 0;
				try {
					result$1 = await fn.apply(this, args);
					const pc = params.privacyConfig;
					if (!(pc != null && (pc.contentMode === "metadata_only" || pc.contentMode == null && pc.privacyMode))) {
						const outputState = _serializeState(result$1, params.privacyConfig);
						if (outputState != null) span.setAttribute(AMP_OUTPUT_STATE, JSON.stringify(outputState));
					}
					return result$1;
				} catch (exc) {
					const msg = exc instanceof Error ? exc.message : String(exc);
					span.setStatus({
						code: 2,
						message: msg
					});
					throw exc;
				} finally {
					span.end();
					if (ownsSession && params.amplitude != null) try {
						trackSessionEnd({
							amplitude: params.amplitude,
							userId: params.userId,
							sessionId,
							env: params.env,
							agentId: params.agentId || spanName,
							privacyConfig: params.privacyConfig
						});
					} catch (e) {
						getLogger().error(`Failed to end @observe session '${spanName}': ${e}`);
					}
				}
			});
			const start = performance.now();
			let isError = false;
			let errorMessage = null;
			let result = void 0;
			try {
				result = await fn.apply(this, args);
				return result;
			} catch (exc) {
				isError = true;
				errorMessage = exc instanceof Error ? exc.message : String(exc);
				throw exc;
			} finally {
				const latencyMs = performance.now() - start;
				const outputState = _serializeState(result, params.privacyConfig);
				const activeCtx = getActiveContext();
				if (params.amplitude != null) {
					try {
						trackSpan({
							amplitude: params.amplitude,
							userId: params.userId,
							spanName,
							traceId: activeCtx?.traceId ?? randomUUID(),
							latencyMs,
							inputState,
							outputState,
							isError,
							errorMessage,
							sessionId,
							agentId: params.agentId || spanName,
							env: params.env,
							privacyConfig: params.privacyConfig
						});
					} catch (e) {
						getLogger().error(`Failed to track @observe span '${spanName}': ${e}`);
					}
					if (ownsSession) try {
						trackSessionEnd({
							amplitude: params.amplitude,
							userId: params.userId,
							sessionId,
							env: params.env,
							agentId: params.agentId || spanName,
							privacyConfig: params.privacyConfig
						});
					} catch (e) {
						getLogger().error(`Failed to end @observe session '${spanName}': ${e}`);
					}
				}
			}
		};
		if (ownsSession) {
			const newCtx = new SessionContext({
				sessionId,
				userId: params.userId,
				agentId: params.agentId || spanName,
				env: params.env,
				amplitude: params.amplitude
			});
			return _sessionStorage.run(newCtx, runFn);
		}
		return runFn();
	};
	Object.defineProperty(wrapper, "name", {
		value: fn.name,
		configurable: true
	});
	return wrapper;
}

//#endregion
export { ToolCallTracker, observe, tool };
//# sourceMappingURL=decorators.js.map