/**
 * SF Workflow Templates — Registry & Resolution
 *
 * Loads the workflow template registry and resolves templates by name,
 * alias, or trigger-keyword matching against user input.
 */
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { sfHome } from "./sf-home.js";

const __extensionDir = resolveSfExtensionDir();
const registryPath = join(
	__extensionDir,
	"workflow-templates",
	"registry.json",
);
/** Resolve the SF extension dir with fallback to ~/.sf/agent/extensions/sf/. */
/**
 * Resolve the SF extension directory with fallback to ~/.sf/agent/extensions/sf/.
 */
function resolveSfExtensionDir() {
	const moduleDir = import.meta.dirname;
	if (existsSync(join(moduleDir, "workflow-templates"))) return moduleDir;
	const agentSfDir = join(sfHome(), "agent", "extensions", "sf");
	if (existsSync(join(agentSfDir, "workflow-templates"))) return agentSfDir;
	return moduleDir;
}
// ─── Registry Cache ──────────────────────────────────────────────────────────
let cachedRegistry = null;
/**
 * Load and cache the workflow template registry.
 */
/**
 * Load and cache the workflow template registry.
 * Returns empty registry if file doesn't exist.
 */
export function loadRegistry() {
	if (cachedRegistry) return cachedRegistry;
	if (!existsSync(registryPath)) {
		cachedRegistry = { schemaVersion: 1, templates: {} };
		return cachedRegistry;
	}
	try {
		const content = readFileSync(registryPath, "utf-8");
		cachedRegistry = JSON.parse(content);
	} catch {
		cachedRegistry = { schemaVersion: 1, templates: {} };
	}
	return cachedRegistry;
}
/**
 * Return registry-backed workflow template command entries for completion and help.
 *
 * Purpose: keep `/start` discovery surfaces tied to the template registry so
 * newly registered workflows are visible without editing multiple hardcoded lists.
 *
 * Consumer: `/next` command completion catalogs and `/start` usage rendering.
 */
/**
 * Get registry-backed command definitions for /start completion and help.
 */
export function workflowTemplateCommandDefinitions() {
	const registry = loadRegistry();
	return Object.entries(registry.templates).map(([id, entry]) => ({
		cmd: id,
		desc: entry.description,
	}));
}
/**
 * Format registry-backed `/start` usage text.
 *
 * Purpose: present the autoflow template family from the same source of truth as
 * resolution and completion, preventing stale command help.
 *
 * Consumer: `/start` when called without a resolvable template.
 */
/**
 * Format usage text for /start command with available templates.
 */
export function formatStartUsage() {
	const templates = workflowTemplateCommandDefinitions()
		.map(({ cmd, desc }) => `  ${cmd.padEnd(16)} ${desc}`)
		.join("\n");
	return (
		"Usage: /start <template> [description]\n\n" +
		"/start templates are autoflow planning templates. " +
		"YAML workflow definitions use /workflow run <definition>.\n\n" +
		"Templates:\n" +
		templates +
		"\n\n" +
		"Examples:\n" +
		"  /start bugfix fix login button not responding\n" +
		"  /start product-plan plan the product we need to develop\n" +
		"  /start product-tracking create product analytics tracking plan\n" +
		"  /start spike evaluate auth libraries\n\n" +
		"Flags:\n" +
		"  --dry-run       Preview what would happen without executing\n" +
		"  --issue <ref>   Link to a GitHub issue\n\n" +
		"Run /templates for detailed template info."
	);
}
/**
 * Resolve a template by exact name or alias.
 * Returns null if no match found.
 */
/**
 * Resolve a template by exact name or alias.
 * Returns null if no match found.
 */
export function resolveByName(nameOrAlias) {
	const registry = loadRegistry();
	const normalized = nameOrAlias.toLowerCase().trim();
	// Exact key match
	if (registry.templates[normalized]) {
		return {
			id: normalized,
			template: registry.templates[normalized],
			confidence: "exact",
		};
	}
	// Match by template name (case-insensitive)
	for (const [id, entry] of Object.entries(registry.templates)) {
		if (entry.name.toLowerCase() === normalized) {
			return { id, template: entry, confidence: "exact" };
		}
	}
	// Common aliases
	const aliases = {
		bug: "bugfix",
		fix: "bugfix",
		feature: "small-feature",
		feat: "small-feature",
		research: "spike",
		investigate: "spike",
		hot: "hotfix",
		urgent: "hotfix",
		security: "security-audit",
		audit: "security-audit",
		upgrade: "dep-upgrade",
		deps: "dep-upgrade",
		"update-deps": "dep-upgrade",
		migration: "refactor",
		project: "full-project",
		full: "full-project",
		product: "product-plan",
		"product-planning": "product-plan",
		"plan-product": "product-plan",
		analytics: "product-tracking",
		instrumentation: "product-tracking",
		telemetry: "product-tracking",
		tracking: "product-tracking",
	};
	const aliasMatch = aliases[normalized];
	if (aliasMatch && registry.templates[aliasMatch]) {
		return {
			id: aliasMatch,
			template: registry.templates[aliasMatch],
			confidence: "high",
		};
	}
	// Fuzzy: prefix match on id
	for (const [id, entry] of Object.entries(registry.templates)) {
		if (id.startsWith(normalized) || normalized.startsWith(id)) {
			return { id, template: entry, confidence: "high" };
		}
	}
	return null;
}
/**
 * Auto-detect the best template based on user description text.
 * Returns ranked matches sorted by confidence.
 */
/**
 * Auto-detect the best template based on user description text.
 * Returns ranked matches sorted by confidence.
 */
export function autoDetect(description) {
	const registry = loadRegistry();
	const lower = description.toLowerCase();
	const words = lower.split(/\s+/);
	const matches = [];
	for (const [id, entry] of Object.entries(registry.templates)) {
		let bestScore = 0;
		let bestTrigger = "";
		for (const trigger of entry.triggers) {
			const triggerLower = trigger.toLowerCase();
			// Exact phrase match in description
			if (lower.includes(triggerLower)) {
				const score = triggerLower.split(/\s+/).length * 2; // multi-word triggers score higher
				if (score > bestScore) {
					bestScore = score;
					bestTrigger = trigger;
				}
				continue;
			}
			// Single-word trigger match against description words
			if (!triggerLower.includes(" ") && words.includes(triggerLower)) {
				if (1 > bestScore) {
					bestScore = 1;
					bestTrigger = trigger;
				}
			}
		}
		if (bestScore > 0) {
			const confidence =
				bestScore >= 4 ? "high" : bestScore >= 2 ? "medium" : "low";
			matches.push({
				id,
				template: entry,
				confidence,
				matchedTrigger: bestTrigger,
			});
		}
	}
	// Sort by confidence (high > medium > low), then alphabetically
	const order = { exact: 0, high: 1, medium: 2, low: 3 };
	matches.sort(
		(a, b) =>
			order[a.confidence] - order[b.confidence] || a.id.localeCompare(b.id),
	);
	return matches;
}
/**
 * List all templates as formatted text for display.
 */
export function listTemplates() {
	const registry = loadRegistry();
	const lines = ["Workflow Templates\n"];
	for (const [id, entry] of Object.entries(registry.templates)) {
		const phases = entry.phases.join(" → ");
		const complexity = entry.estimated_complexity;
		lines.push(`  ${id.padEnd(16)} ${entry.name}`);
		lines.push(`  ${"".padEnd(16)} ${entry.description}`);
		lines.push(
			`  ${"".padEnd(16)} Phases: ${phases}  |  Complexity: ${complexity}`,
		);
		lines.push("");
	}
	lines.push("Usage: /start <template> [description]");
	lines.push("       /templates info <name>");
	return lines.join("\n");
}
/**
 * Get detailed info about a specific template.
 */
export function getTemplateInfo(name) {
	const match = resolveByName(name);
	if (!match) return null;
	const { id, template: t } = match;
	const lines = [
		`Template: ${t.name} (${id})`,
		"",
		`Description: ${t.description}`,
		`Complexity:  ${t.estimated_complexity}`,
		`Requires .sf/: ${t.requires_project ? "yes" : "no"}`,
		"",
		"Phases:",
		...t.phases.map((p, i) => `  ${i + 1}. ${p}`),
		"",
		"Triggers:",
		`  ${t.triggers.join(", ")}`,
	];
	if (t.artifact_dir) {
		lines.push("", `Artifacts: ${t.artifact_dir}`);
	}
	const templateFilePath = join(__extensionDir, "workflow-templates", t.file);
	if (existsSync(templateFilePath)) {
		lines.push("", "Template file: loaded");
	} else {
		lines.push("", "Template file: not yet created");
	}
	return lines.join("\n");
}
/**
 * Load the raw content of a workflow template .md file.
 */
export function loadWorkflowTemplate(templateId) {
	const match = resolveByName(templateId);
	if (!match) return null;
	const filePath = join(
		__extensionDir,
		"workflow-templates",
		match.template.file,
	);
	if (!existsSync(filePath)) return null;
	return readFileSync(filePath, "utf-8");
}
export function scaffoldMilestoneSlices(templateId) {
	const match = resolveByName(templateId);
	if (!match) return null;
	switch (match.id) {
		case "product-plan":
			return [
				{
					sliceId: "S01",
					title: "Model the product and value flow",
					risk: "medium",
					depends: [],
					demo: "The team can explain who the product serves, what value it creates, and which domain entities matter.",
					goal: "Capture the product purpose, users, workflows, entities, value moments, and unknowns before implementation planning.",
					successCriteria:
						"A product model exists with named consumers, core journeys, and explicit open questions.",
					proofLevel: "product contract",
					integrationClosure:
						"Product intent is connected to real code, docs, or requested user outcomes instead of generic assumptions.",
					observabilityImpact:
						"Important product questions and future telemetry needs are visible early.",
				},
				{
					sliceId: "S02",
					title: "Define the target capability boundary",
					risk: "medium",
					depends: ["S01"],
					demo: "The intended product increment has a clear MVP boundary and non-goals.",
					goal: "Translate the product model into a concrete capability, acceptance path, scope boundary, and release assumptions.",
					successCriteria:
						"The planned increment names what will ship, who will use it, and what is deliberately deferred.",
					proofLevel: "scope contract",
					integrationClosure:
						"The capability boundary maps to production-facing surfaces and integration points.",
					observabilityImpact:
						"Success signals, diagnostics, and audit needs are captured before code work starts.",
				},
				{
					sliceId: "S03",
					title: "Plan implementation slices",
					risk: "medium",
					depends: ["S02"],
					demo: "The product increment is broken into buildable slices with evidence and dependencies.",
					goal: "Create a development plan with ordered slices, tests, integration closure, and verification commands.",
					successCriteria:
						"Every slice has purpose, consumer, acceptance evidence, and a bounded implementation surface.",
					proofLevel: "slice plan",
					integrationClosure:
						"The plan shows how partial slices compose into the product capability.",
					observabilityImpact:
						"Risks, telemetry candidates, and release-readiness checks are attached to slices.",
				},
				{
					sliceId: "S04",
					title: "Confirm implementation readiness",
					risk: "low",
					depends: ["S03"],
					demo: "The team can start implementation without re-deciding product scope.",
					goal: "Review the plan for missing purpose, missing consumers, untestable behavior, and unresolved blockers.",
					successCriteria:
						"The product plan is ready for execution or records concrete blockers and follow-up decisions.",
					proofLevel: "readiness review",
					integrationClosure:
						"Implementation can begin from the plan with clear proof obligations.",
					observabilityImpact:
						"Release, support, and analytics follow-ups are explicit instead of implicit.",
				},
			];
		case "bugfix":
		case "hotfix":
			return [
				{
					sliceId: "S01",
					title: "Reproduce and bound the failure",
					risk: "high",
					depends: [],
					demo: "The team can reproduce the failure and point to the exact broken boundary.",
					goal: "Capture the failing boundary, root-cause scope, and the contract that proves the bug is real.",
					successCriteria:
						"A concrete repro path and failing proof exist before any fix is treated as valid.",
					proofLevel: "runtime repro",
					integrationClosure:
						"The same repro path is available for post-fix verification.",
					observabilityImpact:
						"Logs, traces, or repro notes make the failure boundary explicit.",
				},
				{
					sliceId: "S02",
					title: "Implement the fix",
					risk: "medium",
					depends: ["S01"],
					demo: "The broken behavior is corrected without widening scope.",
					goal: "Change the implementation to satisfy the failing contract and remove the root cause.",
					successCriteria:
						"The contract test passes and the fix path is minimal and purpose-aligned.",
					proofLevel: "contract + integration",
					integrationClosure:
						"Callers continue to work on the corrected boundary with no workaround branch left behind.",
					observabilityImpact:
						"Failures surface clearly if the same path regresses.",
				},
				{
					sliceId: "S03",
					title: "Verify and guard against regression",
					risk: "low",
					depends: ["S02"],
					demo: "The solved boundary is proven end-to-end and the fix stays in place.",
					goal: "Re-run the repro, add regression coverage, and verify the shipped behavior.",
					successCriteria:
						"The original failure path is green and regression evidence is captured.",
					proofLevel: "runtime + regression",
					integrationClosure:
						"Verification proves the real consumer path, not just a unit-level surrogate.",
					observabilityImpact:
						"Follow-up diagnostics and regression checks are documented.",
				},
			];
		case "refactor":
			return [
				{
					sliceId: "S01",
					title: "Map the current boundary",
					risk: "medium",
					depends: [],
					demo: "The team can explain what is being reshaped and what must not regress.",
					goal: "Capture current responsibilities, callers, and the contract that must survive the refactor.",
					successCriteria:
						"The pre-refactor contract and blast radius are explicit before code movement starts.",
					proofLevel: "contract inventory",
					integrationClosure:
						"Downstream boundaries and unchanged behavior are named up front.",
					observabilityImpact:
						"Audit notes capture the before-state and likely regression surfaces.",
				},
				{
					sliceId: "S02",
					title: "Restructure the implementation",
					risk: "medium",
					depends: ["S01"],
					demo: "The code is reorganized into the intended shape without changing the consumer contract.",
					goal: "Apply the structural change while preserving verified behavior.",
					successCriteria:
						"The code layout improves and the existing contract stays green.",
					proofLevel: "contract + quality",
					integrationClosure:
						"Callers, imports, and runtime paths remain coherent after the move.",
					observabilityImpact:
						"Lint/type/test failures clearly point to missed migration edges.",
				},
				{
					sliceId: "S03",
					title: "Stabilize and verify",
					risk: "low",
					depends: ["S02"],
					demo: "The new structure is validated and safe to build on.",
					goal: "Prove the refactor is complete, regression-safe, and simpler to extend.",
					successCriteria:
						"Quality gates and targeted regression checks pass on the new structure.",
					proofLevel: "quality + regression",
					integrationClosure:
						"No stale compatibility shims or duplicate paths remain without reason.",
					observabilityImpact:
						"Future maintainers can see the intended shape and the critical checks.",
				},
			];
		case "small-feature":
			return [
				{
					sliceId: "S01",
					title: "Define the user-facing contract",
					risk: "medium",
					depends: [],
					demo: "The feature outcome, acceptance path, and boundary are clear before implementation.",
					goal: "State what the feature must do, for whom, and how success will be verified.",
					successCriteria:
						"A concrete contract exists for the intended feature behavior.",
					proofLevel: "contract",
					integrationClosure:
						"The consumer path and integration boundary are identified before code is written.",
					observabilityImpact:
						"Acceptance criteria and diagnostics are explicit.",
				},
				{
					sliceId: "S02",
					title: "Implement the core behavior",
					risk: "medium",
					depends: ["S01"],
					demo: "The feature works on its main path.",
					goal: "Build the smallest complete implementation that satisfies the agreed contract.",
					successCriteria:
						"The main feature path works and the contract test passes.",
					proofLevel: "contract + integration",
					integrationClosure:
						"Primary callers and dependent boundaries work together on the new path.",
					observabilityImpact: "Failures in the feature path are diagnosable.",
				},
				{
					sliceId: "S03",
					title: "Polish, verify, and close",
					risk: "low",
					depends: ["S02"],
					demo: "The feature is verified, documented, and safe to hand over.",
					goal: "Finish verification, edge handling, and any required docs or follow-up notes.",
					successCriteria:
						"The feature is truthfully ready for users or the next milestone.",
					proofLevel: "verification",
					integrationClosure:
						"The end-to-end user path is proven and any gaps are recorded explicitly.",
					observabilityImpact: "Tests/docs/notes make the feature supportable.",
				},
			];
		default:
			return null;
	}
}
