import { EVENT_AI_RESPONSE, EVENT_SESSION_END, EVENT_TOOL_CALL, PROP_AGENT_ID, PROP_INPUT_TOKENS, PROP_LATENCY_MS, PROP_MODEL_NAME, PROP_OUTPUT_TOKENS, PROP_SESSION_ID, PROP_TOOL_NAME, PROP_TRACE_ID, PROP_TURN_ID } from "./core/constants.js";
import { AmplitudeAI } from "./client.js";

//#region src/testing.ts
var MockAmplitudeAI = class extends AmplitudeAI {
	events = [];
	_simulatedError = null;
	_simulatedLatencyMs = 0;
	constructor(config) {
		const mockAmplitude = {
			track: () => {},
			flush: () => [],
			shutdown: () => {},
			init: () => mockAmplitude
		};
		super({
			amplitude: mockAmplitude,
			config
		});
		this._amplitude.track = (event) => {
			this.events.push(event);
		};
		this._installTrackHook();
	}
	getEvents(eventType) {
		if (eventType == null) return [...this.events];
		return this.events.filter((e) => e.event_type === eventType);
	}
	assertEventTracked(eventType, options = {}) {
		const { userId, deviceId, ...expectedProps } = options;
		const candidates = this.getEvents(eventType);
		if (!candidates.length) {
			const trackedTypes = new Set(this.events.map((e) => e.event_type));
			throw new Error(`No '${eventType}' event tracked. Tracked types: ${[...trackedTypes].join(", ") || "(none)"}`);
		}
		for (const event of candidates) {
			if (userId != null && event.user_id !== userId) continue;
			if (deviceId != null && event.device_id !== deviceId) continue;
			if (Object.keys(expectedProps).length > 0) {
				const props = event.event_properties ?? {};
				if (Object.entries(expectedProps).every(([k, v]) => props[k] === v)) return event;
			} else return event;
		}
		const filters = [];
		if (userId != null) filters.push(`userId=${userId}`);
		if (deviceId != null) filters.push(`deviceId=${deviceId}`);
		for (const [k, v] of Object.entries(expectedProps)) filters.push(`${k}=${String(v)}`);
		throw new Error(`Found ${candidates.length} '${eventType}' event(s) but none matched filters: ${filters.join(", ")}`);
	}
	/**
	* Configure the mock to throw an error on the next tracking call.
	* Useful for testing error-handling paths in your application.
	*
	* @param type - Error type (e.g., 'rate_limit', 'network', 'auth')
	* @param message - Optional custom error message
	*/
	simulateError(type, message) {
		this._simulatedError = {
			type,
			message: message ?? `Simulated ${type} error`
		};
		this._amplitude.track = () => {
			const err = this._simulatedError;
			if (err) throw new Error(`[MockAmplitudeAI] ${err.type}: ${err.message}`);
		};
	}
	/**
	* Configure a simulated latency (in ms) added to each tracking call.
	* The delay is non-blocking (uses `setTimeout`), so the event loop
	* remains responsive during tests.
	*
	* Because tracking becomes asynchronous, use `await` or
	* `vi.advanceTimersByTime()` in tests to let events resolve.
	*/
	simulateLatency(ms) {
		this._simulatedLatencyMs = ms;
		const originalCapture = (event) => {
			this.events.push(event);
		};
		this._amplitude.track = (event) => {
			if (this._simulatedLatencyMs > 0) setTimeout(() => originalCapture(event), this._simulatedLatencyMs);
			else originalCapture(event);
		};
	}
	/**
	* Clear any simulated error or latency, restoring normal capture behavior.
	*/
	clearSimulations() {
		this._simulatedError = null;
		this._simulatedLatencyMs = 0;
		this._amplitude.track = (event) => {
			this.events.push(event);
		};
	}
	reset() {
		this.events = [];
		this._simulatedError = null;
		this._simulatedLatencyMs = 0;
		this._amplitude.track = (event) => {
			this.events.push(event);
		};
		if (this._config.debug || this._config.dryRun || this._config.onEventCallback != null) this._installTrackHook?.();
		this._sessionTurnCounters.clear();
	}
	eventsForSession(sessionId) {
		return this.events.filter((e) => e.event_properties?.[PROP_SESSION_ID] === sessionId);
	}
	eventsForAgent(agentId) {
		return this.events.filter((e) => e.event_properties?.[PROP_AGENT_ID] === agentId);
	}
	assertSessionClosed(sessionId) {
		const candidates = this.events.filter((e) => e.event_type === EVENT_SESSION_END && e.event_properties?.[PROP_SESSION_ID] === sessionId);
		if (!candidates.length) {
			const ended = this.events.filter((e) => e.event_type === EVENT_SESSION_END).map((e) => e.event_properties?.[PROP_SESSION_ID]);
			throw new Error(`No '${EVENT_SESSION_END}' event for session '${sessionId}'. Ended sessions: ${ended.length ? ended.join(", ") : "(none)"}`);
		}
		return candidates[0];
	}
	/**
	* Fill-rate report for local verification.
	*
	* Checks 8 gates across tracked events and returns a human-readable
	* summary with impact/fix hints for missing fields. Useful for verifying
	* instrumentation completeness during development.
	*/
	summary() {
		const lines = [];
		const total = this.events.length;
		lines.push("=== AmplitudeAI Instrumentation Summary ===");
		lines.push(`Total events: ${total}`);
		if (total === 0) {
			lines.push("No events tracked. Call track* methods or run your agent.");
			return lines.join("\n");
		}
		const byType = /* @__PURE__ */ new Map();
		for (const e of this.events) {
			const t = e.event_type ?? "unknown";
			byType.set(t, (byType.get(t) ?? 0) + 1);
		}
		lines.push("");
		lines.push("Event breakdown:");
		for (const [type, count] of byType) lines.push(`  ${type}: ${count}`);
		const gates = [];
		const hasSessionId = this.events.some((e) => e.event_properties?.[PROP_SESSION_ID]);
		gates.push({
			name: "Session ID",
			pass: hasSessionId,
			impact: "Events cannot be grouped into sessions",
			fix: "Pass sessionId to track* methods or use agent.session()"
		});
		const hasTraceId = this.events.some((e) => e.event_properties?.[PROP_TRACE_ID]);
		gates.push({
			name: "Trace ID",
			pass: hasTraceId,
			impact: "Events within a session cannot be grouped into traces",
			fix: "Pass traceId or use session.run() which auto-generates one"
		});
		const hasTurnId = this.events.some((e) => e.event_properties?.[PROP_TURN_ID] != null);
		gates.push({
			name: "Turn ID",
			pass: hasTurnId,
			impact: "Turn ordering cannot be reconstructed",
			fix: "Turn IDs are auto-incremented when using session context"
		});
		const hasAgentId = this.events.some((e) => e.event_properties?.[PROP_AGENT_ID]);
		gates.push({
			name: "Agent ID",
			pass: hasAgentId,
			impact: "Events cannot be attributed to a specific agent",
			fix: "Pass agentId to ai.agent() or track* calls"
		});
		const aiResponses = this.events.filter((e) => e.event_type === EVENT_AI_RESPONSE);
		const hasModel = aiResponses.some((e) => e.event_properties?.[PROP_MODEL_NAME]);
		gates.push({
			name: "Model Name",
			pass: aiResponses.length === 0 || hasModel,
			impact: "Cannot segment by model or calculate costs",
			fix: "Pass model to trackAiMessage()"
		});
		const hasTokens = aiResponses.some((e) => e.event_properties?.[PROP_INPUT_TOKENS] != null || e.event_properties?.[PROP_OUTPUT_TOKENS] != null);
		gates.push({
			name: "Token Usage",
			pass: aiResponses.length === 0 || hasTokens,
			impact: "Cannot calculate costs or monitor usage",
			fix: "Pass inputTokens/outputTokens to trackAiMessage()"
		});
		const toolCalls = this.events.filter((e) => e.event_type === EVENT_TOOL_CALL);
		const hasToolName = toolCalls.some((e) => e.event_properties?.[PROP_TOOL_NAME]);
		gates.push({
			name: "Tool Names",
			pass: toolCalls.length === 0 || hasToolName,
			impact: "Cannot identify which tools are called",
			fix: "Pass toolName to trackToolCall()"
		});
		const hasLatency = this.events.some((e) => (e.event_type === EVENT_AI_RESPONSE || e.event_type === EVENT_TOOL_CALL) && e.event_properties?.[PROP_LATENCY_MS] != null);
		gates.push({
			name: "Latency",
			pass: aiResponses.length === 0 && toolCalls.length === 0 || hasLatency,
			impact: "Cannot monitor response times",
			fix: "Pass latencyMs to trackAiMessage()/trackToolCall()"
		});
		lines.push("");
		lines.push("Gate checks:");
		const passed = gates.filter((g) => g.pass).length;
		lines.push(`  ${passed}/${gates.length} passed`);
		lines.push("");
		for (const gate of gates) {
			const status = gate.pass ? "✓" : "✗";
			lines.push(`  ${status} ${gate.name}`);
			if (!gate.pass) {
				lines.push(`    Impact: ${gate.impact}`);
				lines.push(`    Fix: ${gate.fix}`);
			}
		}
		if (this._config.contentMode === "metadata_only") {
			lines.push("");
			lines.push("⚠ Content mode is \"metadata_only\" — message content will not be stored.");
		}
		return lines.join("\n");
	}
};

//#endregion
export { MockAmplitudeAI };
//# sourceMappingURL=testing.js.map