/**
 * tool-command-registry.js - worker tool/command registry helpers.
 *
 * Purpose: preserve Droid's useful command boundary as structured SF data so
 * workers receive explicit repo tools/commands instead of free-form prose.
 *
 * Consumer: worker context pack projections and future dispatch prompts that
 * need a tool-command allowlist without treating ad hoc mission files as
 * canonical.
 */

function stringOrNull(value) {
	return typeof value === "string" && value.trim().length > 0
		? value.trim()
		: null;
}

function objectOrEmpty(value) {
	return value && typeof value === "object" && !Array.isArray(value)
		? value
		: {};
}

function normalizeStringList(value) {
	if (Array.isArray(value)) {
		return [
			...new Set(value.map(stringOrNull).filter((item) => item !== null)),
		];
	}
	const single = stringOrNull(value);
	return single === null ? [] : [single];
}

function normalizeCommandMap(commands) {
	const source = objectOrEmpty(commands);
	const normalized = {};
	for (const [name, command] of Object.entries(source)) {
		const normalizedName = stringOrNull(name);
		const normalizedCommand = stringOrNull(command);
		if (normalizedName && normalizedCommand) {
			normalized[normalizedName] = normalizedCommand;
		}
	}
	return normalized;
}

/**
 * Normalize an SF worker tool/command registry.
 *
 * Purpose: project Droid-style command boundaries into a predictable
 * tool-first shape while leaving durable source-of-truth ownership to SF
 * DB/runtime state.
 *
 * Consumer: context-pack builders and prompt adapters before rendering worker
 * instructions.
 */
export function normalizeToolCommandRegistry(registry = {}) {
	const source = objectOrEmpty(registry);
	const commands = normalizeCommandMap(source.commands);
	const daemonServices = normalizeStringList(
		source.daemonServices ?? source.services,
	);
	return {
		schemaVersion: 1,
		commands,
		daemonServices,
		allowedCommands: Object.values(commands),
		allowedCommandNames: Object.keys(commands),
		allowedDaemonServices: daemonServices,
	};
}

/**
 * Validate a worker tool/command registry.
 *
 * Purpose: reject malformed command boundaries before workers treat them as
 * executable instructions.
 *
 * Consumer: context-pack projection validation and tests.
 */
export function validateToolCommandRegistry(registry) {
	const issues = [];
	if (!registry || typeof registry !== "object" || Array.isArray(registry)) {
		return { ok: false, issues: ["registry must be an object"] };
	}
	if ("commands" in registry) {
		if (
			!registry.commands ||
			typeof registry.commands !== "object" ||
			Array.isArray(registry.commands)
		) {
			issues.push("commands must be an object");
		} else {
			for (const [name, command] of Object.entries(registry.commands)) {
				if (!stringOrNull(name)) issues.push("command name must be non-empty");
				if (!stringOrNull(command)) {
					issues.push(`commands.${name} must be a non-empty string`);
				}
			}
		}
	}
	const servicesValue = registry.daemonServices ?? registry.services;
	if (servicesValue !== undefined && !Array.isArray(servicesValue)) {
		issues.push("daemonServices must be an array");
	}
	if (Array.isArray(servicesValue)) {
		servicesValue.forEach((service, index) => {
			if (!stringOrNull(service)) {
				issues.push(`daemonServices[${index}] must be a non-empty string`);
			}
		});
	}
	return { ok: issues.length === 0, issues };
}

export const normalizeCommandRegistry = normalizeToolCommandRegistry;
export const validateCommandRegistry = validateToolCommandRegistry;
