/**
 * google-search-oauth-shape.test.ts — OAuth fallback must use cli-core.
 *
 * The OAuth fallback used to carry its own Cloud Code Assist HTTP client. SF now
 * delegates that transport to @google/gemini-cli-core, matching the main
 * google-gemini-cli provider boundary.
 */

import assert from "node:assert/strict";
import { afterEach, test, vi } from "vitest";
import { googleSearchExtension } from "../resources/extensions/search-the-web/tool-google-search.js";

const cliCoreMock = vi.hoisted(() => ({
	lastRequest: undefined as any,
	lastPromptId: undefined as string | undefined,
	lastUserTier: undefined as string | undefined,
}));

vi.mock("@google/gemini-cli-core", () => ({
	CodeAssistServer: class {
		async *generateContentStream(
			request: any,
			promptId: string,
			userTier: string,
		) {
			cliCoreMock.lastRequest = request;
			cliCoreMock.lastPromptId = promptId;
			cliCoreMock.lastUserTier = userTier;
			yield {
				candidates: [
					{
						content: {
							parts: [{ text: "Sunny, 85 F in Austin today." }],
						},
						groundingMetadata: {
							groundingChunks: [
								{
									web: {
										title: "weather.com",
										uri: "https://weather.com/austin",
										domain: "weather.com",
									},
								},
							],
							webSearchQueries: ["weather today in Austin Texas"],
						},
					},
				],
			};
		}
	},
}));

vi.mock("google-auth-library", () => ({
	OAuth2Client: class {
		setCredentials(_credentials: any) {}
	},
}));

function createMockPI() {
	const handlers: Array<{ event: string; handler: any }> = [];
	let registeredTool: any = null;

	return {
		handlers,
		get registeredTool() {
			return registeredTool;
		},
		on(event: string, handler: any) {
			handlers.push({ event, handler });
		},
		registerTool(tool: any) {
			registeredTool = tool;
		},
		async fire(event: string, eventData: any, ctx: any) {
			for (const h of handlers) {
				if (h.event === event) {
					await h.handler(eventData, ctx);
				}
			}
		},
	};
}

function mockModelRegistry(oauthJson?: string) {
	return {
		authStorage: {
			hasAuth: async (_id: string) => !!oauthJson,
		},
		getApiKeyForProvider: async (_provider: string) => oauthJson,
	};
}

async function runOAuthSearch(query: string) {
	const originalKey = process.env.GEMINI_API_KEY;
	const originalAlias = process.env.GOOGLE_GENERATIVE_AI_API_KEY;
	delete process.env.GEMINI_API_KEY;
	delete process.env.GOOGLE_GENERATIVE_AI_API_KEY;
	cliCoreMock.lastRequest = undefined;
	cliCoreMock.lastPromptId = undefined;
	cliCoreMock.lastUserTier = undefined;

	afterEach(() => {
		if (originalKey !== undefined) process.env.GEMINI_API_KEY = originalKey;
		else delete process.env.GEMINI_API_KEY;
		if (originalAlias !== undefined) {
			process.env.GOOGLE_GENERATIVE_AI_API_KEY = originalAlias;
		} else {
			delete process.env.GOOGLE_GENERATIVE_AI_API_KEY;
		}
	});

	const pi = createMockPI();
	googleSearchExtension(pi as any);

	const ctx = {
		ui: { notify() {} },
		modelRegistry: mockModelRegistry(
			JSON.stringify({ token: "tok", projectId: "proj" }),
		),
	};

	await pi.fire("session_start", {}, ctx);
	return pi.registeredTool.execute(
		"c1",
		{ query },
		new AbortController().signal,
		() => {},
		ctx,
	);
}

test("google_search_oauth_when_api_key_missing_uses_cli_core_request_shape", async () => {
	const result = await runOAuthSearch("weather tools test");

	assert.equal(result.isError, undefined);
	assert.ok(cliCoreMock.lastPromptId?.startsWith("sf-google-search-"));
	assert.equal(cliCoreMock.lastUserTier, "USER");
	assert.equal(cliCoreMock.lastRequest.model, "gemini-2.5-flash");
	assert.deepEqual(cliCoreMock.lastRequest.contents, [
		{ role: "user", parts: [{ text: "weather tools test" }] },
	]);
	assert.ok(
		cliCoreMock.lastRequest.config.tools.some(
			(tool: any) => tool.googleSearch !== undefined,
		),
	);
});

test("google_search_oauth_when_cli_core_streams_grounding_returns_sources", async () => {
	const result = await runOAuthSearch("weather");

	assert.equal(result.isError, undefined);
	assert.ok(result.content[0].text.includes("Sunny, 85 F"));
	assert.equal(result.details.sourceCount, 1);
	assert.equal(result.details.cached, false);
});
