/**
 * reopen-slice handler — the core operation behind sf_slice_reopen.
 *
 * Resets a completed slice back to "in_progress" and resets ALL of its
 * tasks back to "pending". This is intentional — if you're reopening a
 * slice, you're re-doing the work. Partial resets create ambiguous state.
 *
 * The parent milestone must still be open (not complete).
 */
// SF — reopen-slice tool handler
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
import { existsSync, unlinkSync } from "node:fs";
import { join } from "node:path";
import { clearPathCache, resolveSlicePath, resolveTasksDir } from "../paths.js";
import {
	getMilestone,
	getSlice,
	getSliceTasks,
	transaction,
	updateSliceStatus,
	updateTaskStatus,
} from "../sf-db.js";
import { invalidateStateCache } from "../state.js";
import { isClosedStatus } from "../status-guards.js";
import { isNonEmptyString } from "../validation.js";
import { logWarning } from "../workflow-logger.js";
import { runPostMutationHook } from "./tool-helpers.js";
export async function handleReopenSlice(params, basePath) {
	// ── Validate required fields ────────────────────────────────────────────
	if (!isNonEmptyString(params.sliceId)) {
		return { error: "sliceId is required and must be a non-empty string" };
	}
	if (!isNonEmptyString(params.milestoneId)) {
		return { error: "milestoneId is required and must be a non-empty string" };
	}
	// ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
	let guardError = null;
	let tasksResetCount = 0;
	transaction(() => {
		const milestone = getMilestone(params.milestoneId);
		if (!milestone) {
			guardError = `milestone not found: ${params.milestoneId}`;
			return;
		}
		if (isClosedStatus(milestone.status)) {
			guardError = `cannot reopen slice in a closed milestone: ${params.milestoneId} (status: ${milestone.status})`;
			return;
		}
		const slice = getSlice(params.milestoneId, params.sliceId);
		if (!slice) {
			guardError = `slice not found: ${params.milestoneId}/${params.sliceId}`;
			return;
		}
		if (!isClosedStatus(slice.status)) {
			guardError = `slice ${params.sliceId} is not complete (status: ${slice.status}) — nothing to reopen`;
			return;
		}
		// Fetch tasks inside txn so the list is consistent with the slice status check
		const tasks = getSliceTasks(params.milestoneId, params.sliceId);
		tasksResetCount = tasks.length;
		updateSliceStatus(params.milestoneId, params.sliceId, "in_progress");
		for (const task of tasks) {
			updateTaskStatus(params.milestoneId, params.sliceId, task.id, "pending");
		}
	});
	if (guardError) {
		return { error: guardError };
	}
	// ── Invalidate caches ────────────────────────────────────────────────────
	invalidateStateCache();
	// ── Clean up stale filesystem artifacts (M12 fix) ────────────────────────
	// Without this, the DB-filesystem reconciler sees SUMMARY.md files and
	// auto-corrects tasks back to "complete", making reopen a no-op (#3161).
	try {
		const tasksDir = resolveTasksDir(
			basePath,
			params.milestoneId,
			params.sliceId,
		);
		if (tasksDir) {
			const tasks = getSliceTasks(params.milestoneId, params.sliceId);
			for (const task of tasks) {
				const summaryPath = join(tasksDir, `${task.id}-SUMMARY.md`);
				if (existsSync(summaryPath)) unlinkSync(summaryPath);
			}
		}
		const sliceDir = resolveSlicePath(
			basePath,
			params.milestoneId,
			params.sliceId,
		);
		if (sliceDir) {
			const sliceSummary = join(sliceDir, `${params.sliceId}-SUMMARY.md`);
			if (existsSync(sliceSummary)) unlinkSync(sliceSummary);
			const sliceUat = join(sliceDir, `${params.sliceId}-UAT.md`);
			if (existsSync(sliceUat)) unlinkSync(sliceUat);
		}
	} catch (cleanupErr) {
		logWarning(
			"tool",
			`reopen-slice artifact cleanup warning: ${cleanupErr.message}`,
		);
	}
	clearPathCache();
	// ── Post-mutation hook ───────────────────────────────────────────────────
	await runPostMutationHook(
		basePath,
		params.milestoneId,
		{
			cmd: "reopen-slice",
			params: {
				milestoneId: params.milestoneId,
				sliceId: params.sliceId,
				reason: params.reason ?? null,
				tasksReset: tasksResetCount,
			},
			ts: new Date().toISOString(),
			actor: "agent",
			actor_name: params.actorName,
			trigger_reason: params.triggerReason,
		},
		{ caller: "reopen-slice" },
	);
	return {
		milestoneId: params.milestoneId,
		sliceId: params.sliceId,
		tasksReset: tasksResetCount,
	};
}
