/**
 * headless-import-backlog.ts — deterministic markdown→SF-DB backlog importer.
 *
 * Parses a flat markdown file with H2 sections (## Title) into SF milestones,
 * and bullet list items under each H2 into slices. No LLM, no RPC child.
 * Writes directly to `.sf/sf.db` via the SF DB layer.
 *
 * Usage: sf headless import-backlog <file.md>
 */

import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";

export interface ImportBacklogOptions {
	json: boolean;
}

interface ParsedSlice {
	title: string;
	status: string;
}

interface ParsedMilestone {
	title: string;
	slices: ParsedSlice[];
}

/**
 * Convert a title string to a safe kebab-case slug for use as an SF ID.
 * SF milestone IDs must match /^[a-z0-9]+(-[a-z0-9]+)*$/.
 */
function slugify(title: string): string {
	return title
		.toLowerCase()
		.replace(/[^a-z0-9\s-]/g, "")
		.trim()
		.replace(/[\s-]+/g, "-")
		.replace(/^-+|-+$/g, "")
		.slice(0, 48);
}

/**
 * Parse a markdown backlog document into a list of milestones with slices.
 *
 * Sections are delimited by H2 headings (## Title). Bullet list items directly
 * under a heading are collected as slices. Text paragraphs are ignored (they
 * become part of the milestone vision if present).
 */
export function parseBacklogMarkdown(text: string): ParsedMilestone[] {
	const milestones: ParsedMilestone[] = [];
	let current: ParsedMilestone | null = null;

	for (const rawLine of text.split("\n")) {
		const line = rawLine.trimEnd();

		const h2 = line.match(/^##\s+(.+)$/);
		if (h2) {
			current = { title: h2[1].trim(), slices: [] };
			milestones.push(current);
			continue;
		}

		if (!current) continue;

		// Bullet items: -, *, +, or numbered (1. ...) — strip status emoji/markers
		const bullet =
			line.match(/^\s*[-*+]\s+(.+)$/) ?? line.match(/^\s*\d+\.\s+(.+)$/);
		if (bullet) {
			let title = bullet[1].trim();
			// Strip leading status markers: ✅, 🟡, ⬜, ✓, x, [x], [ ], etc.
			title = title.replace(/^[✅🟡⬜✓✗x]\s+/u, "");
			title = title.replace(/^\[[x ]\]\s+/i, "");
			// Detect done status from emoji prefix in original
			const isDone =
				bullet[1].trim().startsWith("✅") ||
				bullet[1].trim().match(/^\[x\]/i) != null;
			if (title) {
				current.slices.push({
					title,
					status: isDone ? "complete" : "pending",
				});
			}
		}
	}

	return milestones.filter((m) => m.title.length > 0);
}

/**
 * Run the import. Opens the SF DB, parses the backlog file, and upserts
 * milestones + slices. Skips milestones whose slugged ID already exists.
 */
export async function runImportBacklog(
	filePath: string,
	cwd: string,
	opts: ImportBacklogOptions,
): Promise<number> {
	const log = opts.json
		? () => {}
		: (msg: string) => process.stderr.write(`[import-backlog] ${msg}\n`);

	if (!existsSync(filePath)) {
		process.stderr.write(
			`[import-backlog] Error: file not found: ${filePath}\n`,
		);
		return 1;
	}

	const sfDir = join(cwd, ".sf");
	if (!existsSync(sfDir)) {
		process.stderr.write(
			`[import-backlog] Error: no .sf directory found in ${cwd}\n` +
				`  Run 'sf headless init' first to bootstrap the project.\n`,
		);
		return 1;
	}

	// Open the SF database
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	const dynamicToolsPath =
		"./resources/extensions/sf/bootstrap/dynamic-tools.js";
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	const { ensureDbOpen } = (await import(dynamicToolsPath)) as any;
	const opened = await ensureDbOpen(cwd);
	if (!opened) {
		process.stderr.write(
			`[import-backlog] Error: could not open .sf/sf.db in ${cwd}\n`,
		);
		return 1;
	}

	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	const sfDbPath = "./resources/extensions/sf/sf-db.js";
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	const { insertMilestone, getMilestone, insertSlice, getAllMilestones } =
		(await import(sfDbPath)) as any;

	const text = readFileSync(filePath, "utf8");
	const parsed = parseBacklogMarkdown(text);

	if (parsed.length === 0) {
		process.stderr.write(
			`[import-backlog] No H2 sections found in ${filePath}\n` +
				`  Expected format: ## Section Title, with optional bullet items below.\n`,
		);
		return 1;
	}

	log(`Parsed ${parsed.length} milestone(s) from ${filePath}`);

	// Determine the next sequence number to preserve import order
	const existing = getAllMilestones();
	let sequence = existing.length;

	const results: {
		id: string;
		title: string;
		slices: number;
		skipped: boolean;
	}[] = [];

	for (const m of parsed) {
		const id = slugify(m.title);
		if (!id) {
			log(`Skipping milestone with unslugifiable title: "${m.title}"`);
			continue;
		}

		const exists = getMilestone(id) != null;
		if (exists) {
			log(`Skipping existing milestone: ${id}`);
			results.push({ id, title: m.title, slices: 0, skipped: true });
			continue;
		}

		insertMilestone({
			id,
			title: m.title,
			status: "queued",
			sequence: sequence++,
		});

		let sliceSeq = 0;
		for (const s of m.slices) {
			const sliceId = `s${String(++sliceSeq).padStart(2, "0")}`;
			insertSlice({
				milestoneId: id,
				id: sliceId,
				title: s.title,
				status: s.status,
				sequence: sliceSeq,
			});
		}

		log(`  + ${id}: "${m.title}" (${m.slices.length} slices)`);
		results.push({
			id,
			title: m.title,
			slices: m.slices.length,
			skipped: false,
		});
	}

	const imported = results.filter((r) => !r.skipped).length;
	const skipped = results.filter((r) => r.skipped).length;

	if (opts.json) {
		process.stdout.write(
			JSON.stringify({
				schemaVersion: 1,
				imported,
				skipped,
				milestones: results,
			}) + "\n",
		);
	} else {
		process.stderr.write(
			`[import-backlog] Done: ${imported} imported, ${skipped} skipped.\n`,
		);
	}

	return 0;
}
