import { calculateCost } from "../utils/costs.js";
import { tryRequire } from "../utils/resolve-module.js";
import { StreamingAccumulator } from "../utils/streaming.js";
import { BaseAIProvider, applySessionContext, contextFields } from "./base.js";

//#region src/providers/google-genai.ts
const _resolved = tryRequire("@google/genai");
const GOOGLE_GENAI_AVAILABLE = _resolved != null;
const _GoogleGenAIModule = _resolved;
var GoogleGenAI = class extends BaseAIProvider {
	_client;
	constructor(options) {
		super({
			amplitude: options.amplitude,
			privacyConfig: options.privacyConfig,
			providerName: "gemini"
		});
		if (options.client != null) {
			this._client = options.client;
			return;
		}
		const mod = options.googleGenAIModule ?? _GoogleGenAIModule;
		if (mod == null) throw new Error("@google/genai package is required. Install it with: npm install @google/genai — or pass the module directly via the googleGenAIModule option.");
		const GoogleGenAICtor = mod.GoogleGenAI;
		this._client = new GoogleGenAICtor({
			apiKey: options.apiKey ?? "",
			...options.clientOptions ?? {}
		});
	}
	_models() {
		return this._client.models;
	}
	async generateContent(params) {
		const model = String(params.model ?? "");
		const models = this._models();
		const generateFn = models.generateContent;
		const startTime = performance.now();
		try {
			const response = await generateFn.call(models, params);
			const latencyMs = performance.now() - startTime;
			const extracted = extractGoogleGenAIResponse(response);
			let costUsd = null;
			if (extracted.inputTokens != null && extracted.outputTokens != null) try {
				costUsd = calculateCost({
					modelName: model,
					inputTokens: extracted.inputTokens,
					outputTokens: extracted.outputTokens,
					cacheReadInputTokens: extracted.cacheReadTokens ?? 0,
					defaultProvider: "google"
				});
			} catch {}
			const ctx = applySessionContext();
			this._track({
				...contextFields(ctx),
				modelName: model,
				provider: "gemini",
				responseContent: extracted.text,
				latencyMs,
				inputTokens: extracted.inputTokens,
				outputTokens: extracted.outputTokens,
				totalTokens: extracted.totalTokens,
				cacheReadInputTokens: extracted.cacheReadTokens,
				totalCostUsd: costUsd,
				finishReason: extracted.finishReason,
				toolCalls: extracted.functionCalls?.length ? extracted.functionCalls : void 0,
				toolDefinitions: extractGoogleGenAIToolDefinitions(params),
				systemPrompt: extractGoogleGenAISystemPrompt(params),
				temperature: extractGoogleGenAIConfigNumber(params, "temperature"),
				topP: extractGoogleGenAIConfigNumber(params, "topP"),
				maxOutputTokens: extractGoogleGenAIConfigNumber(params, "maxOutputTokens"),
				isStreaming: false
			});
			return response;
		} catch (error) {
			const latencyMs = performance.now() - startTime;
			const ctx = applySessionContext();
			this._track({
				...contextFields(ctx),
				modelName: model,
				provider: "gemini",
				responseContent: "",
				latencyMs,
				isError: true,
				errorMessage: error instanceof Error ? error.message : String(error)
			});
			throw error;
		}
	}
	async generateContentStream(params) {
		const model = String(params.model ?? "");
		const ctx = applySessionContext();
		const startTime = performance.now();
		try {
			const models = this._models();
			const streamFn = models.generateContentStream;
			if (typeof streamFn !== "function") throw new Error("@google/genai SDK does not expose models.generateContentStream");
			const stream = await streamFn.call(models, params);
			if (!_isAsyncIterable(stream)) throw new Error("@google/genai stream response is not AsyncIterable");
			return this._wrapStream(model, params, stream, ctx);
		} catch (error) {
			this._track({
				...contextFields(ctx),
				modelName: model,
				provider: "gemini",
				responseContent: "",
				latencyMs: performance.now() - startTime,
				isError: true,
				errorMessage: error instanceof Error ? error.message : String(error),
				isStreaming: true
			});
			throw error;
		}
	}
	get client() {
		return this._client;
	}
	async *_wrapStream(model, params, stream, ctx) {
		const accumulator = new StreamingAccumulator();
		try {
			for await (const chunk of stream) {
				const extracted = extractGoogleGenAIResponse(chunk);
				if (extracted.text) accumulator.addContent(extracted.text);
				if (Array.isArray(extracted.functionCalls)) for (const fc of extracted.functionCalls) accumulator.addToolCall(fc);
				if (extracted.finishReason != null) accumulator.finishReason = String(extracted.finishReason);
				accumulator.setUsage({
					inputTokens: extracted.inputTokens,
					outputTokens: extracted.outputTokens,
					totalTokens: extracted.totalTokens,
					cacheReadTokens: extracted.cacheReadTokens
				});
				yield chunk;
			}
		} catch (error) {
			accumulator.setError(error instanceof Error ? error.message : String(error));
			throw error;
		} finally {
			const state = accumulator.getState();
			let costUsd = null;
			if (state.inputTokens != null && state.outputTokens != null) try {
				costUsd = calculateCost({
					modelName: model,
					inputTokens: state.inputTokens,
					outputTokens: state.outputTokens,
					cacheReadInputTokens: state.cacheReadTokens ?? 0,
					defaultProvider: "google"
				});
			} catch {}
			this._track({
				...contextFields(ctx),
				modelName: model,
				provider: "gemini",
				responseContent: state.content,
				latencyMs: accumulator.elapsedMs,
				inputTokens: state.inputTokens,
				outputTokens: state.outputTokens,
				totalTokens: state.totalTokens,
				cacheReadInputTokens: state.cacheReadTokens,
				totalCostUsd: costUsd,
				finishReason: state.finishReason,
				toolCalls: state.toolCalls.length > 0 ? state.toolCalls : void 0,
				toolDefinitions: extractGoogleGenAIToolDefinitions(params),
				systemPrompt: extractGoogleGenAISystemPrompt(params),
				temperature: extractGoogleGenAIConfigNumber(params, "temperature"),
				topP: extractGoogleGenAIConfigNumber(params, "topP"),
				maxOutputTokens: extractGoogleGenAIConfigNumber(params, "maxOutputTokens"),
				providerTtfbMs: state.ttfbMs,
				isStreaming: true,
				isError: state.isError,
				errorMessage: state.errorMessage
			});
		}
	}
};
function extractGoogleGenAIResponse(response) {
	const resp = response ?? {};
	let text = "";
	try {
		if (typeof resp.text === "string") text = resp.text;
	} catch {}
	const usage = resp.usageMetadata;
	const candidate = resp.candidates?.[0];
	const finishReason = candidate?.finishReason;
	let functionCalls;
	if (Array.isArray(resp.functionCalls) && resp.functionCalls.length > 0) functionCalls = resp.functionCalls;
	else {
		const fromParts = (candidate?.content?.parts)?.filter((p) => p.functionCall != null).map((p) => p.functionCall);
		functionCalls = fromParts?.length ? fromParts : void 0;
	}
	return {
		text,
		inputTokens: usage?.promptTokenCount,
		outputTokens: usage?.candidatesTokenCount,
		totalTokens: usage?.totalTokenCount,
		cacheReadTokens: usage?.cachedContentTokenCount,
		finishReason,
		functionCalls
	};
}
function extractGoogleGenAIConfig(params) {
	const config = params.config;
	return config != null && typeof config === "object" ? config : void 0;
}
function extractGoogleGenAISystemPrompt(params) {
	const si = extractGoogleGenAIConfig(params)?.systemInstruction;
	if (si == null) return void 0;
	if (typeof si === "string") return si;
	const parts = si.parts;
	if (Array.isArray(parts)) return parts.map((p) => {
		const text = p?.text;
		return typeof text === "string" ? text : "";
	}).join("");
}
function extractGoogleGenAIConfigNumber(params, key) {
	const value = extractGoogleGenAIConfig(params)?.[key];
	return typeof value === "number" ? value : void 0;
}
function extractGoogleGenAIToolDefinitions(params) {
	const tools = extractGoogleGenAIConfig(params)?.tools ?? params.tools;
	return Array.isArray(tools) && tools.length > 0 ? tools : void 0;
}
function _isAsyncIterable(value) {
	return value != null && typeof value[Symbol.asyncIterator] === "function";
}

//#endregion
export { GOOGLE_GENAI_AVAILABLE, GoogleGenAI, _GoogleGenAIModule, extractGoogleGenAIResponse };
//# sourceMappingURL=google-genai.js.map