/**
 * prompt-parts.js — declarative prompt section registry for SF agents.
 *
 * Purpose: make `.agent.yaml` promptParts executable instead of decorative:
 * agents declare which standard sections they need, while this module owns
 * normalization, validation, ordering, and composition.
 *
 * Consumer: subagent agent discovery, subagent process launch, and headless
 * triage apply when composing built-in agent prompts.
 */
/**
 * Render named, reusable sections for agent prompt composition.
 *
 * Purpose: keep `.agent.yaml` promptParts declarations backed by one executable
 * registry instead of copying prompt boilerplate into every agent definition.
 *
 * Consumer: composeAgentPrompt and prompt-parts validation tests.
 */
export const PROMPT_PARTS = {
	aiSafety: {
		label: "AI Safety",
		render: () =>
			"Follow the active safety and execution policies. Refuse unsafe requests and surface uncertainty instead of fabricating evidence.",
	},
	toolInstructions: {
		label: "Tool Instructions",
		render: (ctx = {}) => {
			const tools = Array.isArray(ctx.tools) ? ctx.tools.filter(Boolean) : [];
			if (tools.length === 0) {
				return "Use only the tools exposed by the runtime for this agent.";
			}
			return `Available tools for this agent: ${tools.join(", ")}. Stay within this surface.`;
		},
	},
	parallelToolCalling: {
		label: "Parallel Tool Calling",
		render: () =>
			"When independent read-only checks are needed, batch them in parallel. Do not parallelize dependent or mutating steps.",
	},
	customAgentInstructions: {
		label: "Custom Agent Instructions",
		render: (ctx = {}) =>
			typeof ctx.customAgentInstructions === "string"
				? ctx.customAgentInstructions
				: "",
	},
	environmentContext: {
		label: "Environment Context",
		render: (ctx = {}) => {
			const rows = [];
			if (ctx.cwd) rows.push(`Working directory: ${ctx.cwd}`);
			if (ctx.surface) rows.push(`Surface: ${ctx.surface}`);
			return rows.join("\n");
		},
	},
	agentBody: {
		label: "Agent Instructions",
		render: (ctx = {}) => ctx.agentBody ?? "",
	},
	outputContract: {
		label: "Output Contract",
		render: (ctx = {}) =>
			typeof ctx.outputContract === "string" ? ctx.outputContract : "",
	},
};

/**
 * Define the canonical order for reusable prompt sections.
 *
 * Purpose: make YAML declaration order non-semantic so agents cannot create
 * inconsistent safety/tool/body ordering by accident.
 *
 * Consumer: normalizePromptParts and composeAgentPrompt.
 */
export const PROMPT_PART_ORDER = [
	"aiSafety",
	"toolInstructions",
	"parallelToolCalling",
	"customAgentInstructions",
	"environmentContext",
	"agentBody",
	"outputContract",
];

/**
 * Provide the valid prompt part key set for schema-like validation.
 *
 * Purpose: reject stale Copilot-style boolean fields and misspelled named parts
 * before an agent prompt reaches runtime.
 *
 * Consumer: validatePromptParts.
 */
export const KNOWN_PROMPT_PART_KEYS = new Set(PROMPT_PART_ORDER);

/**
 * Normalize a promptParts declaration into canonical execution order.
 *
 * Purpose: let agents declare the named parts they need while guaranteeing that
 * the agent body is still included and ordered consistently.
 *
 * Consumer: composeAgentPrompt.
 */
export function normalizePromptParts(value) {
	if (value === undefined) return null;
	if (Array.isArray(value)) {
		const enabled = new Set();
		for (const key of value) {
			if (typeof key === "string" && PROMPT_PARTS[key]) enabled.add(key);
		}
		enabled.add("agentBody");
		return PROMPT_PART_ORDER.filter((key) => enabled.has(key));
	}
	return null;
}

/**
 * Validate a promptParts declaration.
 *
 * Purpose: make the canonical named-array shape enforceable for built-in,
 * project, and user agent definitions.
 *
 * Consumer: validateAgentDefinition and validateManifestPromptParts.
 */
export function validatePromptParts(value) {
	const errors = [];
	if (value === undefined) return errors;
	if (!Array.isArray(value)) {
		return ["promptParts must be an array of canonical part names"];
	}
	for (const [idx, key] of value.entries()) {
		if (typeof key !== "string") {
			errors.push(`promptParts[${idx}] must be a string`);
		} else if (!KNOWN_PROMPT_PART_KEYS.has(key)) {
			errors.push(`promptParts[${idx}] "${key}" is not a known prompt part`);
		}
	}
	return errors;
}

/**
 * Compose an agent prompt from named registry parts plus the agent body.
 *
 * Purpose: make promptParts declarations executable on every subagent launch
 * while preserving raw prompts for definitions that do not opt in.
 *
 * Consumer: subagent process launch and headless triage apply.
 */
export function composeAgentPrompt(agent, context = {}) {
	const body = agent?.systemPrompt ?? "";
	const partKeys = normalizePromptParts(agent?.promptParts);
	if (!partKeys) return body;
	const blocks = [];
	for (const key of partKeys) {
		const part = PROMPT_PARTS[key];
		if (!part) continue;
		const text = part.render({
			...context,
			agentBody: body,
		});
		if (typeof text !== "string" || text.trim().length === 0) continue;
		blocks.push(`## ${part.label}\n\n${text.trim()}`);
	}
	return blocks.join("\n\n");
}
