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/gemini.ts
const _resolved = tryRequire("@google/generative-ai");
const GEMINI_AVAILABLE = _resolved != null;
const _GeminiModule = _resolved;
var Gemini = 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.geminiModule ?? _GeminiModule;
		if (mod == null) throw new Error("@google/generative-ai package is required. Install it with: npm install @google/generative-ai — or pass the module directly via the geminiModule option.");
		const GoogleGenAI = mod.GoogleGenerativeAI;
		this._client = new GoogleGenAI(options.apiKey ?? "");
	}
	async generateContent(model, params) {
		const genModel = this._client.getGenerativeModel.call(this._client, { model });
		const generateFn = genModel.generateContent;
		const startTime = performance.now();
		try {
			const response = await generateFn.call(genModel, params);
			const latencyMs = performance.now() - startTime;
			const extracted = extractGeminiResponse(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: extractGeminiToolDefinitions(params),
				systemPrompt: extractGeminiSystemPrompt(params),
				temperature: extractGeminiTemperature(params),
				topP: extractGeminiTopP(params),
				maxOutputTokens: extractGeminiMaxOutputTokens(params),
				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(model, params) {
		const ctx = applySessionContext();
		const startTime = performance.now();
		try {
			const genModel = this._client.getGenerativeModel.call(this._client, { model });
			const streamFn = genModel.generateContentStream;
			if (typeof streamFn !== "function") throw new Error("Gemini SDK does not expose generateContentStream");
			const streamResponse = await streamFn.call(genModel, params);
			const stream = streamResponse.stream;
			const finalResponse = streamResponse.response;
			if (!_isAsyncIterable(stream)) throw new Error("Gemini stream response is not AsyncIterable");
			return {
				...streamResponse,
				stream: this._wrapStream(model, params, stream, finalResponse, 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, finalResponse, ctx) {
		const accumulator = new StreamingAccumulator();
		try {
			for await (const chunk of stream) {
				const extracted = extractGeminiResponse(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 {
			if (finalResponse != null) try {
				const extractedFinal = extractGeminiResponse(await finalResponse);
				accumulator.setUsage({
					inputTokens: extractedFinal.inputTokens,
					outputTokens: extractedFinal.outputTokens,
					totalTokens: extractedFinal.totalTokens,
					cacheReadTokens: extractedFinal.cacheReadTokens
				});
				if (extractedFinal.finishReason != null) accumulator.finishReason = String(extractedFinal.finishReason);
				if (Array.isArray(extractedFinal.functionCalls) && accumulator.toolCalls.length === 0) for (const fc of extractedFinal.functionCalls) accumulator.addToolCall(fc);
			} catch {}
			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: extractGeminiToolDefinitions(params),
				systemPrompt: extractGeminiSystemPrompt(params),
				temperature: extractGeminiTemperature(params),
				topP: extractGeminiTopP(params),
				maxOutputTokens: extractGeminiMaxOutputTokens(params),
				providerTtfbMs: state.ttfbMs,
				isStreaming: true,
				isError: state.isError,
				errorMessage: state.errorMessage
			});
		}
	}
};
function extractGeminiResponse(response) {
	const resp = response;
	const respObj = resp.response ?? resp;
	let text = "";
	if (typeof respObj.text === "function") try {
		text = String(respObj.text());
	} catch {}
	const usage = respObj.usageMetadata;
	const candidate = respObj.candidates?.[0];
	const finishReason = candidate?.finishReason;
	const functionCalls = (candidate?.content?.parts)?.filter((p) => p.functionCall != null).map((p) => p.functionCall);
	return {
		text,
		inputTokens: usage?.promptTokenCount,
		outputTokens: usage?.candidatesTokenCount,
		totalTokens: usage?.totalTokenCount,
		cacheReadTokens: usage?.cachedContentTokenCount,
		finishReason,
		functionCalls: functionCalls?.length ? functionCalls : void 0
	};
}
function extractGeminiSystemPrompt(params) {
	return params.systemInstruction ?? params.generationConfig?.systemInstruction;
}
function extractGeminiTemperature(params) {
	return params.generationConfig?.temperature;
}
function extractGeminiTopP(params) {
	return params.generationConfig?.topP;
}
function extractGeminiMaxOutputTokens(params) {
	return params.generationConfig?.maxOutputTokens;
}
function extractGeminiToolDefinitions(params) {
	const 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 { GEMINI_AVAILABLE, Gemini, _GeminiModule, extractGeminiResponse };
//# sourceMappingURL=gemini.js.map