import { existsSync } from "node:fs";
import { join } from "node:path";
import {
	getReservedMilestoneIds,
	milestoneIdSort,
	nextMilestoneId,
	reserveMilestoneId,
} from "./milestone-ids.js";
import { resolveMilestoneFile, sfRoot } from "./paths.js";
import { getAllMilestones, getMilestone, isDbAvailable } from "./sf-db.js";

/**
 * A milestone is "reusable ghost" if it has no DB row, no worktree, and no
 * content files. This is a stricter definition than `isGhostMilestone`:
 * any DB row (including "queued") disqualifies the candidate — a queued row
 * is sufficient proof of a live in-flight ID reservation.
 *
 * Used by `nextMilestoneIdReserved` to fill gaps left by phantom directories
 * before resorting to max+1.
 */
function isReusableGhostMilestone(basePath, mid) {
	// Condition 1: no DB row (any status).
	if (!isDbAvailable()) return false;
	const dbRow = getMilestone(mid);
	if (dbRow != null) return false;
	// Condition 2: no worktree.
	const root = sfRoot(basePath);
	const wtPath = join(root, "worktrees", mid);
	if (existsSync(wtPath)) return false;
	// Condition 3: no content files.
	const context = resolveMilestoneFile(basePath, mid, "CONTEXT");
	const draft = resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT");
	const roadmap = resolveMilestoneFile(basePath, mid, "ROADMAP");
	const summary = resolveMilestoneFile(basePath, mid, "SUMMARY");
	return !context && !draft && !roadmap && !summary;
}
function getDatabaseMilestoneIds() {
	if (!isDbAvailable()) return [];
	return getAllMilestones().map((milestone) => milestone.id);
}
/**
 * Generate the next milestone ID, accounting for DB rows and in-process
 * reservations, and reserve it.
 */
export function nextMilestoneIdReserved(existingIds, uniqueEnabled, basePath) {
	const reservedIds = getReservedMilestoneIds();
	const allIds = [
		...new Set([...existingIds, ...reservedIds, ...getDatabaseMilestoneIds()]),
	];
	if (basePath) {
		const sorted = [...allIds].sort(milestoneIdSort);
		for (const candidate of sorted) {
			if (reservedIds.has(candidate)) continue;
			if (isReusableGhostMilestone(basePath, candidate)) {
				reserveMilestoneId(candidate);
				return candidate;
			}
		}
	}
	const id = nextMilestoneId(allIds, uniqueEnabled);
	reserveMilestoneId(id);
	return id;
}
