/**
 * deriveState: when every slice on the active milestone was *skipped*
 * (not completed), route into the pre-planning ladder instead of the
 * validating-milestone phase.
 *
 * Background: the original handleAllSlicesDone branch treated
 * isStatusDone uniformly - "complete", "done", and "skipped" all
 * counted as "the milestone has finished its work". That collapsed
 * the migration-placeholder pattern (skip the stub slice -> milestone
 * advances to validation) with the genuinely-done case. The fix
 * differentiates them: zero slices in {complete, done} -> pre-planning
 * so the dispatcher's discuss -> research -> plan ladder takes over.
 *
 * This pins:
 *   - all-skipped milestone routes to pre-planning, not validating
 *   - at least one complete/done slice keeps the validating-milestone
 *     behaviour (no regression for the happy path)
 *   - the nextAction names the dispatcher path so an operator
 *     reading the snapshot knows what'll happen autonomously
 */

import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, test } from "vitest";
import {
	closeDatabase,
	insertMilestone,
	insertRequirement,
	insertSlice,
	isDbAvailable,
	openDatabase,
} from "../sf-db.js";
import { deriveState, invalidateStateCache } from "../state.js";

const tmpDirs = [];

afterEach(() => {
	closeDatabase();
	if (!isDbAvailable()) {
		openDatabase(":memory:");
		closeDatabase();
	}
	invalidateStateCache();
	while (tmpDirs.length > 0) {
		const dir = tmpDirs.pop();
		if (dir) rmSync(dir, { recursive: true, force: true });
	}
});

function makeProject(milestoneId) {
	const dir = mkdtempSync(join(tmpdir(), "sf-state-allskipped-"));
	tmpDirs.push(dir);
	mkdirSync(join(dir, ".sf", "milestones", milestoneId, "slices", "S01"), {
		recursive: true,
	});
	openDatabase(join(dir, ".sf", "sf.db"));
	insertMilestone({
		id: milestoneId,
		title: "All-skipped milestone",
		status: "active",
	});
	return dir;
}

test("all_slices_skipped_when_deriving_state_returns_pre_planning", async () => {
	const dir = makeProject("M501");
	insertSlice({
		milestoneId: "M501",
		id: "S01",
		title: "Migration placeholder",
		status: "skipped",
		sequence: 1,
	});

	const state = await deriveState(dir);

	assert.equal(state.activeMilestone?.id, "M501");
	assert.equal(state.phase, "pre-planning");
	// activeSlice is intentionally null - pre-planning needs the
	// dispatcher to pick the next unit type from artifacts.
	assert.equal(state.activeSlice, null);
	// nextAction must surface the autonomous path so operators know
	// SF will plan on its own.
	assert.match(
		state.nextAction,
		/pre-planning ladder|discuss\/research\/plan|every slice is skipped/i,
	);
});

test("complete_slice_when_deriving_state_returns_validating_milestone", async () => {
	const dir = makeProject("M502");
	insertSlice({
		milestoneId: "M502",
		id: "S01",
		title: "Real work",
		status: "complete",
		sequence: 1,
	});
	insertSlice({
		milestoneId: "M502",
		id: "S02",
		title: "Migration placeholder",
		status: "skipped",
		sequence: 2,
	});

	const state = await deriveState(dir);

	assert.equal(state.activeMilestone?.id, "M502");
	assert.equal(state.phase, "validating-milestone");
});

test("done_slice_when_deriving_state_returns_validating_milestone", async () => {
	const dir = makeProject("M503");
	insertSlice({
		milestoneId: "M503",
		id: "S01",
		title: "Legacy-style done slice",
		status: "done",
		sequence: 1,
	});

	const state = await deriveState(dir);

	assert.equal(state.activeMilestone?.id, "M503");
	assert.equal(state.phase, "validating-milestone");
});

test("multiple_skipped_slices_when_deriving_state_returns_pre_planning", async () => {
	const dir = makeProject("M504");
	insertSlice({
		milestoneId: "M504",
		id: "S01",
		title: "First placeholder",
		status: "skipped",
		sequence: 1,
	});
	insertSlice({
		milestoneId: "M504",
		id: "S02",
		title: "Second placeholder",
		status: "skipped",
		sequence: 2,
	});

	const state = await deriveState(dir);

	assert.equal(state.phase, "pre-planning");
});

test("all_slices_skipped_when_owned_requirements_complete_returns_validating_milestone", async () => {
	const dir = makeProject("M505");
	insertSlice({
		milestoneId: "M505",
		id: "S01",
		title: "Migration placeholder",
		status: "skipped",
		sequence: 1,
	});
	for (const id of ["R501", "R502"]) {
		insertRequirement({
			id,
			class: "runtime",
			status: "complete",
			description: `${id} done`,
			why: "contract satisfied",
			source: "test",
			primary_owner: "M505/S01",
			supporting_slices: "",
			validation: "unit test",
			notes: "",
			full_content: "",
			superseded_by: null,
		});
	}

	const state = await deriveState(dir);

	assert.equal(state.activeMilestone?.id, "M505");
	assert.equal(state.phase, "validating-milestone");
	assert.match(state.nextAction, /All 2 requirement\(s\) owned by M505/);
	assert.match(state.nextAction, /Run validate-milestone/);
});

test("all_slices_skipped_when_owned_requirement_incomplete_returns_pre_planning", async () => {
	const dir = makeProject("M506");
	insertSlice({
		milestoneId: "M506",
		id: "S01",
		title: "Migration placeholder",
		status: "skipped",
		sequence: 1,
	});
	insertRequirement({
		id: "R503",
		class: "runtime",
		status: "active",
		description: "still open",
		why: "contract not satisfied",
		source: "test",
		primary_owner: "M506/S01",
		supporting_slices: "",
		validation: "",
		notes: "",
		full_content: "",
		superseded_by: null,
	});

	const state = await deriveState(dir);

	assert.equal(state.activeMilestone?.id, "M506");
	assert.equal(state.phase, "pre-planning");
});
