/**
 * workflow-plugins.ts — Unified discovery for workflow plugins.
 *
 * Discovers workflow definitions from three tiers (project > global > bundled)
 * in both YAML and markdown formats. Each plugin declares an execution mode
 * that controls how `/workflow <name>` dispatches it:
 *
 *   oneshot         — prompt-only, no state or scaffolding
 *   yaml-step       — CustomWorkflowEngine run with GRAPH.yaml
 *   markdown-phase  — STATE.json + phase gates (current md template behavior)
 *   auto-milestone  — hooks into /autonomous pipeline (full-project only)
 *
 * Precedence: project > global > bundled. Same-named file wins.
 */
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
import { basename, extname, join } from "node:path";
import { parse as parseYaml } from "yaml";
import { getErrorMessage } from "./error-utils.js";
import { sfHome } from "./sf-home.js";
import { loadRegistry } from "./workflow-templates.js";

// ─── Path resolution ─────────────────────────────────────────────────────
function resolveBundledDir() {
	const moduleDir = import.meta.dirname;
	const local = join(moduleDir, "workflow-templates");
	if (existsSync(local)) return local;
	const agentSfDir = join(
		sfHome(),
		"agent",
		"extensions",
		"sf",
		"workflow-templates",
	);
	if (existsSync(agentSfDir)) return agentSfDir;
	return local;
}
function globalPluginsDir() {
	return join(sfHome(), "workflows");
}
function projectPluginsDir(basePath) {
	return join(basePath, ".sf", "workflows");
}
function legacyDefsDir(basePath) {
	return join(basePath, ".sf", "workflow-defs");
}
// ─── Markdown frontmatter parsing ────────────────────────────────────────
/**
 * Parse the `<template_meta>` block from bundled/user markdown workflow files.
 * Returns a loose key-value map (strings only).
 */
function parseTemplateMeta(content) {
	const match = content.match(/<template_meta>([\s\S]*?)<\/template_meta>/);
	if (!match) return {};
	const body = match[1];
	const result = {};
	for (const line of body.split(/\r?\n/)) {
		const trimmed = line.trim();
		if (!trimmed) continue;
		const colon = trimmed.indexOf(":");
		if (colon === -1) continue;
		const key = trimmed.slice(0, colon).trim();
		const value = trimmed.slice(colon + 1).trim();
		result[key] = value;
	}
	return result;
}
function parsePhasesFromMarkdown(content) {
	const match = content.match(/<phases>([\s\S]*?)<\/phases>/);
	if (!match) return [];
	const phases = [];
	for (const line of match[1].split(/\r?\n/)) {
		const m = line.match(/^\s*\d+\.\s*(\S+)/);
		if (m) phases.push(m[1]);
	}
	return phases;
}
function firstHeading(content) {
	const match = content.match(/^#\s+(.+)$/m);
	return match ? match[1].trim() : undefined;
}
function isValidMode(v) {
	return (
		v === "oneshot" ||
		v === "yaml-step" ||
		v === "markdown-phase" ||
		v === "auto-milestone"
	);
}
// ─── Single-file plugin loaders ──────────────────────────────────────────
function loadMarkdownPlugin(filePath, source) {
	const name = basenameNoExt(filePath);
	let content;
	try {
		content = readFileSync(filePath, "utf-8");
	} catch {
		return null;
	}
	const meta = parseTemplateMeta(content);
	const phases = parsePhasesFromMarkdown(content);
	const declaredMode = meta.mode;
	const mode = isValidMode(declaredMode) ? declaredMode : "markdown-phase";
	const triggers = meta.triggers
		? meta.triggers
				.split(",")
				.map((s) => s.trim())
				.filter(Boolean)
		: undefined;
	const artifactDirValue =
		meta.artifact_dir === "null" || meta.artifact_dir === ""
			? null
			: meta.artifact_dir;
	return {
		name,
		path: filePath,
		format: "md",
		source,
		meta: {
			displayName: meta.name || firstHeading(content) || name,
			description: meta.description,
			mode,
			phases: phases.length > 0 ? phases : undefined,
			triggers,
			complexity: meta.complexity,
			artifactDir: artifactDirValue ?? undefined,
			requiresProject: meta.requires_project === "true",
		},
	};
}
function loadYamlPlugin(filePath, source) {
	const name = basenameNoExt(filePath);
	let raw;
	try {
		raw = readFileSync(filePath, "utf-8");
	} catch {
		return null;
	}
	let parsed;
	try {
		parsed = parseYaml(raw);
	} catch (err) {
		return {
			name,
			path: filePath,
			format: "yaml",
			source,
			meta: { displayName: name, mode: "yaml-step" },
			error: `YAML parse error: ${getErrorMessage(err)}`,
		};
	}
	if (parsed == null || typeof parsed !== "object") {
		return {
			name,
			path: filePath,
			format: "yaml",
			source,
			meta: { displayName: name, mode: "yaml-step" },
			error: "Definition is not an object",
		};
	}
	const def = parsed;
	const declaredMode = def.mode;
	const mode = isValidMode(declaredMode) ? declaredMode : "yaml-step";
	const steps = Array.isArray(def.steps) ? def.steps : [];
	const phases = steps.map((s) => String(s.id ?? "")).filter(Boolean);
	return {
		name,
		path: filePath,
		format: "yaml",
		source,
		meta: {
			displayName:
				typeof def.name === "string" && def.name.trim() ? def.name : name,
			description:
				typeof def.description === "string" ? def.description : undefined,
			mode,
			phases: phases.length > 0 ? phases : undefined,
		},
	};
}
function basenameNoExt(filePath) {
	const ext = extname(filePath);
	return basename(filePath, ext);
}
// ─── Directory walkers ───────────────────────────────────────────────────
const PLUGIN_EXTENSIONS = new Set([".yaml", ".yml", ".md"]);
function walkPluginDir(dir, source, out) {
	if (!existsSync(dir)) return;
	let entries;
	try {
		entries = readdirSync(dir);
	} catch {
		return;
	}
	for (const entry of entries) {
		const full = join(dir, entry);
		let info;
		try {
			info = statSync(full);
		} catch {
			continue;
		}
		if (!info.isFile()) continue;
		const ext = extname(entry).toLowerCase();
		if (!PLUGIN_EXTENSIONS.has(ext)) continue;
		const plugin =
			ext === ".md"
				? loadMarkdownPlugin(full, source)
				: loadYamlPlugin(full, source);
		if (!plugin) continue;
		out.set(plugin.name, plugin);
	}
}
function loadBundledPlugins(out) {
	const bundledDir = resolveBundledDir();
	if (!existsSync(bundledDir)) return;
	const registry = loadRegistry();
	for (const [id, entry] of Object.entries(registry.templates)) {
		const filePath = join(bundledDir, entry.file);
		if (!existsSync(filePath)) continue;
		const ext = extname(entry.file).toLowerCase();
		const format = ext === ".md" ? "md" : "yaml";
		// TemplateEntry doesn't carry mode — default by format
		const mode = format === "yaml" ? "yaml-step" : "markdown-phase";
		out.set(id, {
			name: id,
			path: filePath,
			format,
			source: "bundled",
			meta: {
				displayName: entry.name,
				description: entry.description,
				mode,
				phases:
					Array.isArray(entry.phases) && entry.phases.length > 0
						? entry.phases
						: undefined,
				triggers: Array.isArray(entry.triggers) ? entry.triggers : undefined,
				complexity: entry.estimated_complexity,
				artifactDir: entry.artifact_dir,
				requiresProject: entry.requires_project,
			},
		});
	}
}
// ─── Public API ──────────────────────────────────────────────────────────
/**
 * Discover all workflow plugins. Project overrides global overrides bundled.
 *
 * The legacy `.sf/workflow-defs/*.yaml` directory is also scanned as a
 * fallback YAML source so existing user definitions keep working.
 */
export function discoverPlugins(basePath) {
	const out = new Map();
	loadBundledPlugins(out);
	walkPluginDir(globalPluginsDir(), "global", out);
	walkPluginDir(legacyDefsDir(basePath), "project", out);
	walkPluginDir(projectPluginsDir(basePath), "project", out);
	return out;
}
/**
 * Resolve a plugin by name using the precedence chain.
 * Returns null if no plugin by that name exists anywhere.
 */
export function resolvePlugin(basePath, name) {
	const plugins = discoverPlugins(basePath);
	return plugins.get(name) ?? null;
}
/**
 * Format all discovered plugins for display, grouped by mode.
 */
export function listPluginsFormatted(basePath) {
	const plugins = discoverPlugins(basePath);
	if (plugins.size === 0) {
		return "No workflow plugins found.\n\nRun /workflow new to author one.";
	}
	const groups = {
		oneshot: [],
		"yaml-step": [],
		"markdown-phase": [],
		"auto-milestone": [],
	};
	for (const p of plugins.values()) {
		groups[p.meta.mode].push(p);
	}
	const lines = ["Workflow Plugins\n"];
	const order = ["markdown-phase", "yaml-step", "oneshot", "auto-milestone"];
	for (const mode of order) {
		const list = groups[mode]
			.slice()
			.sort((a, b) => a.name.localeCompare(b.name));
		if (list.length === 0) continue;
		lines.push(`  [${mode}]`);
		for (const p of list) {
			const tag = `${p.source}/${p.format}`;
			const desc = p.meta.description ? ` — ${p.meta.description}` : "";
			lines.push(`    ${p.name.padEnd(22)} ${tag.padEnd(16)}${desc}`);
		}
		lines.push("");
	}
	lines.push("Usage:");
	lines.push("  /workflow <name>          Run a plugin directly");
	lines.push("  /workflow info <name>     Show plugin details");
	lines.push("  /workflow install <src>   Install a plugin from a URL");
	return lines.join("\n");
}
/**
 * Format a single plugin's metadata for `/workflow info <name>`.
 */
export function formatPluginInfo(plugin) {
	const lines = [
		`Plugin: ${plugin.meta.displayName} (${plugin.name})`,
		"",
		`Source:   ${plugin.source}`,
		`Format:   ${plugin.format}`,
		`Mode:     ${plugin.meta.mode}`,
		`Path:     ${plugin.path}`,
	];
	if (plugin.meta.description) {
		lines.push(`About:    ${plugin.meta.description}`);
	}
	if (plugin.meta.complexity) {
		lines.push(`Complexity: ${plugin.meta.complexity}`);
	}
	if (plugin.meta.phases && plugin.meta.phases.length > 0) {
		lines.push("", "Phases/Steps:");
		plugin.meta.phases.forEach((p, i) => lines.push(`  ${i + 1}. ${p}`));
	}
	if (plugin.meta.triggers && plugin.meta.triggers.length > 0) {
		lines.push("", `Triggers: ${plugin.meta.triggers.join(", ")}`);
	}
	if (plugin.meta.artifactDir) {
		lines.push("", `Artifacts: ${plugin.meta.artifactDir}`);
	}
	if (plugin.error) {
		lines.push("", `Warning: ${plugin.error}`);
	}
	return lines.join("\n");
}
/**
 * Get the plugin directory paths for the project/global/bundled tiers.
 * Exposed for the install command and tests.
 */
export function getPluginDirs(basePath) {
	return {
		project: projectPluginsDir(basePath),
		global: globalPluginsDir(),
		bundled: resolveBundledDir(),
		legacy: legacyDefsDir(basePath),
	};
}
