/**
 * SF Command — /codebase
 *
 * Generate and manage the codebase map (.sf/CODEBASE.md).
 * Subcommands: generate, update, stats, indexer, help
 */
import { formatCodebaseIndexerStatus } from "./code-intelligence.js";
import {
	generateCodebaseMap,
	getCodebaseMapStats,
	readCodebaseMap,
	updateCodebaseMap,
	writeCodebaseMap,
} from "./codebase-generator.js";
import { loadEffectiveSFPreferences } from "./preferences.js";

const USAGE =
	"Usage: /codebase [generate|update|stats|indexer]\n\n" +
	"  generate [--max-files N] [--collapse-threshold N]  — Generate or regenerate CODEBASE.md\n" +
	"  update [--max-files N] [--collapse-threshold N]    — Refresh the CODEBASE.md cache immediately\n" +
	"  stats                                              — Show file count, coverage, and generation time\n" +
	"  indexer [status]                                   — Inspect Sift codebase-indexer status\n" +
	"  help                                               — Show this help\n\n" +
	"With no subcommand, shows stats if a map exists or help if not.\n" +
	"SF also refreshes CODEBASE.md automatically before prompt injection and after completed units when tracked files change.\n\n" +
	"Configure defaults via preferences.yaml:\n" +
	"  codebase:\n" +
	'    exclude_patterns: ["docs/", "fixtures/"]\n' +
	"    max_files: 1000\n" +
	"    collapse_threshold: 15\n" +
	"    indexer_backend: sift  # sift | none; omit for auto-detect";
export async function handleCodebase(args, ctx, _pi) {
	const basePath = process.cwd();
	const parts = args.trim().split(/\s+/);
	const sub = parts[0] ?? "";
	switch (sub) {
		case "generate": {
			const options = resolveCodebaseOptions(args, ctx);
			if (options === false) return; // validation failed, message already shown
			const existing = readCodebaseMap(basePath);
			const existingDescriptions = existing
				? (await import("./codebase-generator.js")).parseCodebaseMap(existing)
				: undefined;
			const result = generateCodebaseMap(
				basePath,
				options,
				existingDescriptions,
			);
			if (result.fileCount === 0) {
				ctx.ui.notify(
					"Codebase map generated with 0 files.\n" +
						"Is this a git repository? Run 'git ls-files' to verify.",
					"warning",
				);
				return;
			}
			const outPath = writeCodebaseMap(basePath, result.content);
			ctx.ui.notify(
				`Codebase map generated: ${result.fileCount} files\n` +
					`Written to: ${outPath}` +
					(result.truncated
						? `\n⚠ Truncated — increase --max-files to include all files`
						: ""),
				"success",
			);
			return;
		}
		case "update": {
			const existing = readCodebaseMap(basePath);
			if (!existing) {
				ctx.ui.notify(
					"No codebase map found. Run /codebase generate to create one.",
					"warning",
				);
				return;
			}
			const options = resolveCodebaseOptions(args, ctx);
			if (options === false) return;
			const result = updateCodebaseMap(basePath, options);
			writeCodebaseMap(basePath, result.content);
			ctx.ui.notify(
				`Codebase map updated: ${result.fileCount} files\n` +
					`  Added: ${result.added} | Removed: ${result.removed} | Unchanged: ${result.unchanged}` +
					(result.truncated
						? `\n⚠ Truncated — increase --max-files to include all files`
						: ""),
				"success",
			);
			return;
		}
		case "stats": {
			showStats(basePath, ctx);
			return;
		}
		case "indexer": {
			const action = (parts[1] ?? "status").toLowerCase();
			const prefs = loadEffectiveSFPreferences()?.preferences?.codebase;
			if (action === "status") {
				ctx.ui.notify(formatCodebaseIndexerStatus(basePath, prefs), "info");
				return;
			}
			ctx.ui.notify(
				`Unknown /codebase indexer action "${action}". Use status.`,
				"warning",
			);
			return;
		}
		case "help":
			ctx.ui.notify(USAGE, "info");
			return;
		case "": {
			// Safe default: show stats if map exists, help if not
			const existing = readCodebaseMap(basePath);
			if (existing) {
				showStats(basePath, ctx);
			} else {
				ctx.ui.notify(USAGE, "info");
			}
			return;
		}
		default:
			ctx.ui.notify(`Unknown subcommand "${sub}".\n\n${USAGE}`, "warning");
	}
}
function showStats(basePath, ctx) {
	const stats = getCodebaseMapStats(basePath);
	if (!stats.exists) {
		ctx.ui.notify(
			"No codebase map found. Run /codebase generate to create one.",
			"info",
		);
		return;
	}
	const coverage =
		stats.fileCount > 0
			? Math.round((stats.describedCount / stats.fileCount) * 100)
			: 0;
	ctx.ui.notify(
		`Codebase Map Stats:\n` +
			`  Files: ${stats.fileCount}\n` +
			`  Described: ${stats.describedCount} (${coverage}%)\n` +
			`  Undescribed: ${stats.undescribedCount}\n` +
			`  Generated: ${stats.generatedAt ?? "unknown"}\n\n` +
			(stats.undescribedCount > 0
				? `Tip: Auto-refresh keeps the cache current, but /codebase update forces an immediate refresh.`
				: `Coverage is complete.`),
		"info",
	);
}
/**
 * Resolve codebase map options by merging preferences with CLI flags.
 * CLI flags override preferences; preferences override built-in defaults.
 * Returns false if validation failed (error already shown to user).
 */
function resolveCodebaseOptions(args, ctx) {
	// Load preferences defaults
	const prefs = loadEffectiveSFPreferences()?.preferences?.codebase;
	// Parse CLI flags
	const maxFilesStr = extractFlag(args, "--max-files");
	const collapseStr = extractFlag(args, "--collapse-threshold");
	// Validate --max-files
	let maxFiles;
	if (maxFilesStr) {
		maxFiles = parseInt(maxFilesStr, 10);
		if (Number.isNaN(maxFiles) || maxFiles < 1) {
			ctx.ui.notify(
				"--max-files must be a positive integer (e.g. --max-files 200).",
				"warning",
			);
			return false;
		}
	}
	// Validate --collapse-threshold
	let collapseThreshold;
	if (collapseStr) {
		collapseThreshold = parseInt(collapseStr, 10);
		if (Number.isNaN(collapseThreshold) || collapseThreshold < 1) {
			ctx.ui.notify(
				"--collapse-threshold must be a positive integer (e.g. --collapse-threshold 15).",
				"warning",
			);
			return false;
		}
	}
	return {
		// CLI flags override preferences
		maxFiles: maxFiles ?? prefs?.max_files,
		collapseThreshold: collapseThreshold ?? prefs?.collapse_threshold,
		excludePatterns: prefs?.exclude_patterns,
	};
}
function extractFlag(args, flag) {
	const escaped = flag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
	const regex = new RegExp(`${escaped}[=\\s]+(\\S+)`);
	const match = args.match(regex);
	return match?.[1];
}
