/**
 * web-search-retrieval-evidence.test.mjs — web search provenance coverage.
 *
 * Purpose: prove current/external fact lookups write DB evidence before agents
 * use web results as planning input.
 */
import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, test } from "vitest";
import {
	registerSearchTool,
	resetSearchLoopGuardState,
} from "../../search-the-web/tool-search.js";
import { closeDatabase, getRetrievalEvidence, openDatabase } from "../sf-db.js";

const tmpRoots = [];
const originalCwd = process.cwd();
const originalFetch = globalThis.fetch;
const originalBraveKey = process.env.BRAVE_API_KEY;
const originalTavilyKey = process.env.TAVILY_API_KEY;
const originalMiniMaxCodePlanKey = process.env.MINIMAX_CODE_PLAN_KEY;
const originalMiniMaxCodingKey = process.env.MINIMAX_CODING_API_KEY;
const originalMiniMaxKey = process.env.MINIMAX_API_KEY;
const originalSerperKey = process.env.SERPER_API_KEY;
const originalExaKey = process.env.EXA_API_KEY;
const originalOllamaKey = process.env.OLLAMA_API_KEY;

afterEach(() => {
	process.chdir(originalCwd);
	closeDatabase();
	globalThis.fetch = originalFetch;
	if (originalBraveKey === undefined) {
		delete process.env.BRAVE_API_KEY;
	} else {
		process.env.BRAVE_API_KEY = originalBraveKey;
	}
	restoreEnv("TAVILY_API_KEY", originalTavilyKey);
	restoreEnv("MINIMAX_CODE_PLAN_KEY", originalMiniMaxCodePlanKey);
	restoreEnv("MINIMAX_CODING_API_KEY", originalMiniMaxCodingKey);
	restoreEnv("MINIMAX_API_KEY", originalMiniMaxKey);
	restoreEnv("SERPER_API_KEY", originalSerperKey);
	restoreEnv("EXA_API_KEY", originalExaKey);
	restoreEnv("OLLAMA_API_KEY", originalOllamaKey);
	resetSearchLoopGuardState();
	for (const root of tmpRoots.splice(0)) {
		rmSync(root, { recursive: true, force: true });
	}
});

function restoreEnv(key, value) {
	if (value === undefined) {
		delete process.env[key];
	} else {
		process.env[key] = value;
	}
}

function makeProject() {
	const root = mkdtempSync(join(tmpdir(), "sf-web-search-evidence-"));
	tmpRoots.push(root);
	mkdirSync(join(root, ".sf"), { recursive: true });
	return root;
}

function captureSearchTool() {
	let tool = null;
	registerSearchTool({
		registerTool(definition) {
			tool = definition;
		},
		writeTempFile: async () => "/tmp/not-used",
	});
	assert.ok(tool, "search-the-web tool should register");
	return tool;
}

test("search_the_web_when_successful_records_retrieval_evidence", async () => {
	const project = makeProject();
	process.chdir(project);
	process.env.BRAVE_API_KEY = "test-brave-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 = "";
	assert.equal(openDatabase(join(project, ".sf", "sf.db")), true);
	globalThis.fetch = async (url) => {
		assert.match(String(url), /api\.search\.brave\.com/);
		return {
			ok: true,
			headers: { get: () => null },
			json: async () => ({
				web: {
					results: [
						{
							title: "Example Result",
							url: "https://example.com/result",
							description: "A result for testing.",
						},
					],
				},
				query: {
					original: "example query",
					altered: "example query",
				},
			}),
		};
	};

	const tool = captureSearchTool();
	const result = await tool.execute("call-1", {
		query: "example query",
		count: 1,
	});

	assert.equal(result.details.provider, "brave");
	assert.equal(result.details.count, 1);
	const rows = getRetrievalEvidence(5);
	assert.equal(rows.length, 1);
	assert.equal(rows[0].backend, "search-the-web");
	assert.equal(rows[0].sourceKind, "web");
	assert.equal(rows[0].strategy, "brave");
	assert.equal(rows[0].freshness, "external-live");
	assert.equal(rows[0].status, "ok");
	assert.equal(rows[0].hitCount, 1);
	assert.deepEqual(rows[0].result.results, [
		{
			title: "Example Result",
			url: "https://example.com/result",
		},
	]);
});

test("search_the_web_when_query_repeats_returns_loop_signal", async () => {
	const project = makeProject();
	process.chdir(project);
	process.env.BRAVE_API_KEY = "test-brave-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 = "";
	assert.equal(openDatabase(join(project, ".sf", "sf.db")), true);
	globalThis.fetch = async () => ({
		ok: true,
		headers: { get: () => null },
		json: async () => ({
			web: {
				results: [
					{
						title: "Example Result",
						url: "https://example.com/result",
						description: "A result for testing.",
					},
				],
			},
		}),
	});

	const tool = captureSearchTool();
	await tool.execute("call-1", {
		query: "duplicate query",
		count: 1,
	});
	const result = await tool.execute("call-2", {
		query: "duplicate query",
		count: 1,
	});

	assert.equal(result.isError, true);
	assert.equal(result.details.errorKind, "search_loop");
	assert.deepEqual(result.details.loopSignal, {
		scope: "tool",
		kind: "search:duplicate-query",
		severity: "fail",
		evidence: {
			query: "duplicate query",
			provider: "brave",
			domain: "",
			consecutiveDupeCount: 2,
		},
		recommendedAction: "block",
		source: "search-the-web",
	});

	const rows = getRetrievalEvidence(5);
	assert.equal(rows[0].status, "search_loop");
	assert.deepEqual(rows[0].result.loopSignal, result.details.loopSignal);
});
