/**
 * Path-traversal ID validation tests for handleReassessRoadmap.
 * Part of the validation-safe-id-path-segments self-feedback fix.
 */
import assert from "node:assert/strict";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from 'vitest';

import { handleReassessRoadmap } from "../tools/reassess-roadmap.ts";

function makeTmpBase(): string {
	return mkdtempSync(join(tmpdir(), "sf-reassess-safe-id-"));
}

function cleanup(base: string): void {
	try {
		rmSync(base, { recursive: true, force: true });
	} catch {
		/* noop */
	}
}

/** Minimal valid params — unsafe_id checks fire before any DB access. */
function baseParams() {
	return {
		milestoneId: "M001",
		completedSliceId: "S01",
		verdict: "confirmed",
		assessment: "All good.",
		sliceChanges: {
			modified: [],
			added: [],
			removed: [],
		},
	};
}

// ─── milestoneId traversal ────────────────────────────────────────────────

test("handleReassessRoadmap rejects path traversal in milestoneId", async () => {
	const base = makeTmpBase();
	try {
		const result = await handleReassessRoadmap(
			{ ...baseParams(), milestoneId: "../outside" },
			base,
		);
		assert.ok("error" in result, "should return error");
		assert.equal(result.error, "unsafe_id");
		assert.ok("field" in result);
		assert.ok("reason" in result);
	} finally {
		cleanup(base);
	}
});

test("handleReassessRoadmap rejects path separator in milestoneId", async () => {
	const base = makeTmpBase();
	try {
		const result = await handleReassessRoadmap(
			{ ...baseParams(), milestoneId: "M001/../../etc/passwd" },
			base,
		);
		assert.ok("error" in result);
		assert.equal(result.error, "unsafe_id");
	} finally {
		cleanup(base);
	}
});

// ─── completedSliceId traversal ───────────────────────────────────────────

test("handleReassessRoadmap rejects path traversal in completedSliceId", async () => {
	const base = makeTmpBase();
	try {
		const result = await handleReassessRoadmap(
			{ ...baseParams(), completedSliceId: "../evil" },
			base,
		);
		assert.ok("error" in result);
		assert.equal(result.error, "unsafe_id");
		assert.ok("field" in result);
		assert.ok("reason" in result);
	} finally {
		cleanup(base);
	}
});

test("handleReassessRoadmap rejects newline in completedSliceId", async () => {
	const base = makeTmpBase();
	try {
		const result = await handleReassessRoadmap(
			{ ...baseParams(), completedSliceId: "S01\nbad: true" },
			base,
		);
		assert.ok("error" in result);
		assert.equal(result.error, "unsafe_id");
	} finally {
		cleanup(base);
	}
});

// ─── sliceChanges.modified[].sliceId traversal ───────────────────────────

test("handleReassessRoadmap rejects path traversal in sliceChanges.modified sliceId", async () => {
	const base = makeTmpBase();
	try {
		const result = await handleReassessRoadmap(
			{
				...baseParams(),
				sliceChanges: {
					modified: [{ sliceId: "../bad", title: "title" }],
					added: [],
					removed: [],
				},
			},
			base,
		);
		assert.ok("error" in result);
		assert.equal(result.error, "unsafe_id");
		assert.ok("field" in result);
		assert.ok("reason" in result);
	} finally {
		cleanup(base);
	}
});

// ─── sliceChanges.added[].sliceId traversal ──────────────────────────────

test("handleReassessRoadmap rejects path traversal in sliceChanges.added sliceId", async () => {
	const base = makeTmpBase();
	try {
		const result = await handleReassessRoadmap(
			{
				...baseParams(),
				sliceChanges: {
					modified: [],
					added: [{ sliceId: "S01/../../x", title: "Injected" }],
					removed: [],
				},
			},
			base,
		);
		assert.ok("error" in result);
		assert.equal(result.error, "unsafe_id");
		assert.ok("field" in result);
		assert.ok("reason" in result);
	} finally {
		cleanup(base);
	}
});

// ─── sliceChanges.removed[] traversal ────────────────────────────────────

test("handleReassessRoadmap rejects path traversal in sliceChanges.removed entry", async () => {
	const base = makeTmpBase();
	try {
		const result = await handleReassessRoadmap(
			{
				...baseParams(),
				sliceChanges: {
					modified: [],
					added: [],
					removed: ["../outside"],
				},
			},
			base,
		);
		assert.ok("error" in result);
		assert.equal(result.error, "unsafe_id");
		assert.ok("field" in result);
		assert.ok("reason" in result);
	} finally {
		cleanup(base);
	}
});

test("handleReassessRoadmap rejects backslash separator in sliceChanges.removed entry", async () => {
	const base = makeTmpBase();
	try {
		const result = await handleReassessRoadmap(
			{
				...baseParams(),
				sliceChanges: {
					modified: [],
					added: [],
					removed: ["S01\\..\\evil"],
				},
			},
			base,
		);
		assert.ok("error" in result);
		assert.equal(result.error, "unsafe_id");
	} finally {
		cleanup(base);
	}
});
