import { init_paths, resolveConfigPath } from "../../config/paths.js";
import { init_loader, loadConfig } from "../../config/loader.js";
import { formatExamples, register } from "../registry.js";
import { colors } from "../utils/colors.js";
import { SETUP_EXIT } from "./setup-shared/types.js";
import { emitOutcome } from "./setup-shared/output.js";
import { isPromptCancelled, promptSecret } from "./setup-shared/prompts.js";
import { SetupValidationError, runSetup } from "./setup-shared/runner.js";
import "./setup-shared/index.js";
import { Command } from "commander";
//#region src/cli/commands/search.ts
/**
* `xopc search` — manage web-search providers.
*
* Backed by `cfg.tools.web.search.providers[]` per `SearchProviderEntrySchema`.
* Adds entries idempotently keyed by `type` (brave / tavily / bing / searxng).
*/
init_loader();
init_paths();
const SEARCH_TYPES = [
	"brave",
	"tavily",
	"bing",
	"searxng"
];
function isSearchType(value) {
	return SEARCH_TYPES.includes(value);
}
function readProviders(cfg) {
	return (cfg.tools?.web?.search?.providers ?? []).slice();
}
function searchEntriesEqual(a, b) {
	return a.type === b.type && (a.apiKey ?? void 0) === (b.apiKey ?? void 0) && (a.url ?? void 0) === (b.url ?? void 0) && Boolean(a.disabled) === Boolean(b.disabled);
}
function applySearchProviderUpsert(cfg, entry) {
	const list = cfg.tools?.web?.search?.providers ?? [];
	const existing = list.find((p) => p.type === entry.type);
	if (existing && searchEntriesEqual(existing, entry) && typeof cfg.tools?.web?.search?.maxResults === "number") return cfg;
	const tools = { ...cfg.tools ?? {} };
	const web = { ...tools.web ?? {} };
	const search = { ...web.search ?? {} };
	const next = list.filter((p) => p.type !== entry.type);
	next.push(entry);
	search.providers = next;
	if (typeof search.maxResults !== "number") search.maxResults = 5;
	web.search = search;
	tools.web = web;
	return {
		...cfg,
		tools
	};
}
function applySearchProviderRemove(cfg, type) {
	const list = readProviders(cfg);
	if (!list.some((p) => p.type === type)) throw new SetupValidationError([{ message: `No "${type}" search provider configured.` }]);
	const tools = { ...cfg.tools ?? {} };
	const web = { ...tools.web ?? {} };
	const search = { ...web.search ?? {} };
	search.providers = list.filter((p) => p.type !== type);
	web.search = search;
	tools.web = web;
	return {
		...cfg,
		tools
	};
}
function resolveConfigPathFromCommand(command) {
	let cur = command;
	while (cur?.parent) cur = cur.parent;
	return (cur?.opts())?.config?.trim() || process.env.XOPC_CONFIG_PATH?.trim() || process.env.XOPC_CONFIG?.trim() || resolveConfigPath();
}
function maskKey(key) {
	if (!key) return null;
	if (key.length <= 8) return "*".repeat(key.length);
	return `${key.slice(0, 4)}…${key.slice(-4)}`;
}
function summarizeEntry(entry) {
	return {
		type: entry.type,
		apiKey: maskKey(entry.apiKey),
		url: entry.url,
		disabled: entry.disabled ?? false
	};
}
function createSearchCommand(_ctx) {
	const cmd = new Command("search").description("Manage web-search providers (brave / tavily / bing / searxng)").addHelpText("after", formatExamples([
		"xopc search list",
		"xopc search list --json",
		"xopc search add brave --key brv-xxx",
		"xopc search add tavily --key tvly-xxx",
		"xopc search add searxng --url http://localhost:8080",
		"xopc search remove brave",
		"xopc search add brave --key brv-xxx --dry-run --json",
		"xopc search schema --json"
	]));
	cmd.command("list").description("List configured web-search providers").option("--json", "Output as JSON", false).action((opts, command) => {
		const providers = readProviders(loadConfig(resolveConfigPathFromCommand(command))).map(summarizeEntry);
		if (opts.json) {
			process.stdout.write(JSON.stringify({
				ok: true,
				providers,
				supportedTypes: [...SEARCH_TYPES]
			}) + "\n");
			return;
		}
		console.log("");
		console.log(colors.bold("SEARCH PROVIDERS"));
		if (providers.length === 0) console.log(colors.gray("  (none configured — HTML fallback only)"));
		else for (const p of providers) {
			const flag = p.disabled ? colors.gray("○ disabled") : colors.green("● active");
			const key = p.apiKey ? colors.gray(` key=${p.apiKey}`) : "";
			const url = p.url ? colors.gray(` url=${p.url}`) : "";
			console.log(`  ${String(p.type).padEnd(10)}  ${flag}${key}${url}`);
		}
		console.log("");
		console.log(colors.gray(`Supported: ${SEARCH_TYPES.join(", ")}`));
	});
	cmd.command("add <type>").description("Add or replace a search provider entry (idempotent by type)").option("--key <value>", "API key (brave / tavily / bing). Prompts securely if omitted.").option("--url <value>", "SearXNG instance URL (required for type=searxng)").option("--dry-run", "Show the change without writing", false).option("--json", "Emit a single JSON outcome line", false).action(async (type, opts, command) => {
		const dryRun = Boolean(opts.dryRun);
		const json = Boolean(opts.json);
		const t = type.trim().toLowerCase();
		if (!isSearchType(t)) {
			emitOutcome({
				ok: false,
				action: "add",
				domain: "search",
				target: t,
				changedPaths: [],
				dryRun,
				errors: [{ message: `Unknown search type "${t}". Use one of: ${SEARCH_TYPES.join(", ")}.` }]
			}, json);
			process.exitCode = SETUP_EXIT.ERROR;
			return;
		}
		const entry = { type: t };
		if (t === "searxng") {
			if (!opts.url) {
				emitOutcome({
					ok: false,
					action: "add",
					domain: "search",
					target: t,
					changedPaths: [],
					dryRun,
					errors: [{
						path: "url",
						message: "--url is required for type=searxng"
					}]
				}, json);
				process.exitCode = SETUP_EXIT.ERROR;
				return;
			}
			entry.url = opts.url.trim();
		} else {
			let key = opts.key?.trim();
			if (!key) try {
				key = await promptSecret(`API key for ${t}:`);
			} catch (error) {
				if (isPromptCancelled(error)) {
					emitOutcome({
						ok: false,
						action: "add",
						domain: "search",
						target: t,
						changedPaths: [],
						dryRun,
						errors: [{ message: "Cancelled by user" }]
					}, json);
					process.exitCode = SETUP_EXIT.CANCELLED;
					return;
				}
				throw error;
			}
			entry.apiKey = key;
		}
		await runSetup({
			configPath: resolveConfigPathFromCommand(command),
			options: {
				dryRun,
				json
			},
			mutator: {
				domain: "search",
				target: t,
				action: "add",
				mutate(cfg) {
					return applySearchProviderUpsert(cfg, entry);
				},
				resultValue: () => summarizeEntry(entry)
			}
		});
	});
	cmd.command("remove <type>").description("Remove a search provider entry").option("--dry-run", "Show the change without writing", false).option("--json", "Emit a single JSON outcome line", false).action(async (type, opts, command) => {
		const t = type.trim().toLowerCase();
		if (!isSearchType(t)) {
			emitOutcome({
				ok: false,
				action: "remove",
				domain: "search",
				target: t,
				changedPaths: [],
				dryRun: Boolean(opts.dryRun),
				errors: [{ message: `Unknown search type "${t}". Use one of: ${SEARCH_TYPES.join(", ")}.` }]
			}, Boolean(opts.json));
			process.exitCode = SETUP_EXIT.ERROR;
			return;
		}
		await runSetup({
			configPath: resolveConfigPathFromCommand(command),
			options: {
				dryRun: Boolean(opts.dryRun),
				json: Boolean(opts.json)
			},
			mutator: {
				domain: "search",
				target: t,
				action: "remove",
				mutate(cfg) {
					return applySearchProviderRemove(cfg, t);
				}
			}
		});
	});
	cmd.command("schema").description("Print web-search setup schema for agents/UIs").option("--json", "JSON output (default human-readable JSON)", false).action((opts) => {
		const payload = {
			ok: true,
			schema: {
				target: "cfg.tools.web.search.providers[]",
				fields: {
					type: {
						type: "enum",
						enum: [...SEARCH_TYPES],
						required: true
					},
					key: {
						type: "string",
						secret: true,
						description: "API key (brave / tavily / bing)."
					},
					url: {
						type: "url",
						description: "SearXNG instance URL (only for type=searxng)."
					}
				}
			}
		};
		if (opts.json) process.stdout.write(JSON.stringify(payload) + "\n");
		else console.log(JSON.stringify(payload, null, 2));
	});
	return cmd;
}
register({
	id: "search",
	name: "search",
	description: "Configure web-search providers",
	factory: createSearchCommand,
	metadata: {
		category: "setup",
		examples: [
			"xopc search list",
			"xopc search add brave --key brv-xxx",
			"xopc search add searxng --url http://localhost:8080",
			"xopc search remove brave",
			"xopc search schema --json"
		]
	}
});
//#endregion
export { applySearchProviderRemove, applySearchProviderUpsert };

//# sourceMappingURL=search.js.map