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 {
	handleProductAudit,
	type ProductAuditParams,
} from "../tools/product-audit-tool.ts";

const BASE_PARAMS: Omit<ProductAuditParams, "milestoneId"> = {
	verdict: "no-gaps",
	summary: "All product requirements are satisfied.",
	gaps: [],
};

function makeTmpDir(): string {
	return mkdtempSync(join(tmpdir(), "sf-product-audit-safe-id-"));
}

test("handleProductAudit rejects path traversal in milestoneId", async () => {
	const basePath = makeTmpDir();
	try {
		const result = await handleProductAudit(
			{ milestoneId: "../outside", ...BASE_PARAMS },
			basePath,
		);
		assert.ok("error" in result, "expected an error response");
		const err = result as { error: string; field?: string; reason?: string };
		assert.equal(err.error, "unsafe_id");
		assert.equal(err.field, "milestone_id");
		assert.ok(
			typeof err.reason === "string" && err.reason.length > 0,
			"reason should be a non-empty string",
		);
	} finally {
		rmSync(basePath, { recursive: true, force: true });
	}
});

test("handleProductAudit rejects slash in milestoneId", async () => {
	const basePath = makeTmpDir();
	try {
		const result = await handleProductAudit(
			{ milestoneId: "M001/../../etc/passwd", ...BASE_PARAMS },
			basePath,
		);
		assert.ok("error" in result, "expected an error response");
		const err = result as { error: string; field?: string; reason?: string };
		assert.equal(err.error, "unsafe_id");
		assert.equal(err.field, "milestone_id");
	} finally {
		rmSync(basePath, { recursive: true, force: true });
	}
});

test("handleProductAudit rejects newline in milestoneId", async () => {
	const basePath = makeTmpDir();
	try {
		const result = await handleProductAudit(
			{ milestoneId: "M001\nbad: true", ...BASE_PARAMS },
			basePath,
		);
		assert.ok("error" in result, "expected an error response");
		const err = result as { error: string; field?: string; reason?: string };
		assert.equal(err.error, "unsafe_id");
		assert.equal(err.field, "milestone_id");
	} finally {
		rmSync(basePath, { recursive: true, force: true });
	}
});

test("handleProductAudit rejects dot-dot milestoneId", async () => {
	const basePath = makeTmpDir();
	try {
		const result = await handleProductAudit(
			{ milestoneId: "..", ...BASE_PARAMS },
			basePath,
		);
		assert.ok("error" in result, "expected an error response");
		const err = result as { error: string; field?: string; reason?: string };
		assert.equal(err.error, "unsafe_id");
		assert.equal(err.field, "milestone_id");
	} finally {
		rmSync(basePath, { recursive: true, force: true });
	}
});

test("handleProductAudit accepts a safe milestoneId and writes files", async () => {
	const basePath = makeTmpDir();
	try {
		const result = await handleProductAudit(
			{ milestoneId: "M001", ...BASE_PARAMS },
			basePath,
		);
		// Should NOT be an unsafe_id error — either success or a different error
		if ("error" in result) {
			const err = result as { error: string };
			assert.notEqual(
				err.error,
				"unsafe_id",
				"a safe milestoneId should not produce unsafe_id error",
			);
		} else {
			assert.equal(result.milestoneId, "M001");
		}
	} finally {
		rmSync(basePath, { recursive: true, force: true });
	}
});
