/**
 * Native Anthropic web search extension hooks.
 *
 * The injection logic (before_provider_request) lives in the native provider middleware:
 *   packages/coding-agent/src/core/providers/web-search-middleware.ts
 *
 * This file owns only the extension-layer concerns: model_select diagnostics,
 * active-tool management, session reset, and PREFERENCES.md-aware provider resolution.
 */
import {
	CUSTOM_SEARCH_TOOL_NAMES,
	setPreferBraveResolver,
	webSearchMiddleware,
} from "@singularity-forge/coding-agent";
import { resolveSearchProviderFromPreferences } from "../sf/preferences.js";
/** Tool names for the Brave-backed custom search tools */
export const BRAVE_TOOL_NAMES = ["search-the-web", "search_and_read"];
/** When true, skip native web search injection and keep Brave/custom tools active on Anthropic. */
export function preferBraveSearch() {
	// PREFERENCES.md takes priority over env var
	const prefsPref = resolveSearchProviderFromPreferences();
	if (
		prefsPref === "brave" ||
		prefsPref === "tavily" ||
		prefsPref === "minimax" ||
		prefsPref === "serper" ||
		prefsPref === "exa" ||
		prefsPref === "ollama" ||
		prefsPref === "combosearch"
	)
		return true;
	if (prefsPref === "native") return false;
	// Fall back to env var
	return (
		process.env.PREFER_BRAVE_SEARCH === "1" ||
		process.env.PREFER_BRAVE_SEARCH === "true"
	);
}
/**
 * Register model_select, before_provider_request, and session_start hooks for native Anthropic web search.
 *
 * before_provider_request delegates to the webSearchMiddleware singleton so that tests
 * exercise the same code path as production (sdk.ts calls it natively first; the extension
 * delegate is a no-op in production due to the double-injection guard in the middleware).
 */
export function registerNativeSearchHooks(pi) {
	// null = unknown (model_select not yet fired); true/false = provider is/isn't Anthropic.
	let isAnthropicProvider = null;
	// Register the PREFERENCES.md-aware resolver so the native middleware (shared
	// singleton in web-search-middleware.ts) respects search_provider overrides.
	// Called here so each test invocation resets the resolver to the current context.
	setPreferBraveResolver(preferBraveSearch);
	// Reset the shared middleware session counter for this registration.
	// In tests, each registerNativeSearchHooks() call starts a fresh counter.
	// In production, the session_start handler below resets it on each new session.
	webSearchMiddleware.resetSession();
	// Track provider changes via model selection — also handles diagnostics
	// since model_select fires AFTER session_start and knows the provider.
	pi.on("model_select", async (event, ctx) => {
		const wasAnthropic = isAnthropicProvider;
		isAnthropicProvider = event.model.provider === "anthropic";
		const hasSearchKey = !!(
			process.env.BRAVE_API_KEY ||
			process.env.TAVILY_API_KEY ||
			process.env.MINIMAX_CODE_PLAN_KEY ||
			process.env.MINIMAX_CODING_API_KEY ||
			process.env.MINIMAX_API_KEY ||
			process.env.SERPER_API_KEY ||
			process.env.EXA_API_KEY ||
			process.env.OLLAMA_API_KEY
		);
		// When Anthropic (and not preferring Brave): disable custom search tools —
		// native web_search is server-side and more reliable.
		if (isAnthropicProvider && !preferBraveSearch()) {
			const active = pi.getActiveTools();
			pi.setActiveTools(
				active.filter((t) => !CUSTOM_SEARCH_TOOL_NAMES.includes(t)),
			);
		} else if (!isAnthropicProvider && wasAnthropic) {
			// Switching away from Anthropic — re-enable custom search tools (they
			// were disabled while native search was active). If keys are missing,
			// user sees the error rather than tools silently vanishing.
			const active = pi.getActiveTools();
			const toAdd = CUSTOM_SEARCH_TOOL_NAMES.filter((t) => !active.includes(t));
			if (toAdd.length > 0) {
				pi.setActiveTools([...active, ...toAdd]);
			}
		}
		// Show provider-aware diagnostics on first selection or provider change
		if (
			isAnthropicProvider &&
			!preferBraveSearch() &&
			!wasAnthropic &&
			event.source !== "restore"
		) {
			ctx.ui.notify("Native Anthropic web search active", "info");
		} else if (
			isAnthropicProvider &&
			preferBraveSearch() &&
			!wasAnthropic &&
			event.source !== "restore"
		) {
			ctx.ui.notify("Brave search active (PREFER_BRAVE_SEARCH)", "info");
		} else if (!isAnthropicProvider && !hasSearchKey) {
			ctx.ui.notify(
				"Web search: Set BRAVE_API_KEY, TAVILY_API_KEY, MINIMAX_CODE_PLAN_KEY, SERPER_API_KEY, EXA_API_KEY, or OLLAMA_API_KEY, or use an Anthropic model for built-in search",
				"warning",
			);
		}
	});
	// Delegate before_provider_request to the native middleware singleton.
	// In production, sdk.ts already ran applyToPayload before extension hooks fire,
	// so the double-injection guard makes this a no-op. In tests (mock PI without
	// sdk.ts), this is the only path that exercises the injection logic.
	pi.on("before_provider_request", async (event, _ctx) => {
		let modelHint = event.model;
		if (!modelHint && isAnthropicProvider !== null) {
			modelHint = {
				provider: isAnthropicProvider ? "anthropic" : "not-anthropic",
			};
		}
		return webSearchMiddleware.applyToPayload(event.payload, modelHint);
	});
	pi.on("session_start", async (_event, _ctx) => {
		// Reset the shared middleware session budget (#1309).
		webSearchMiddleware.resetSession();
	});
}
