import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, it } from "vitest";
import { CONFIG_DIR_NAME } from "../config.js";
import { SettingsManager } from "./settings-manager.js";

function makeTempDirs() {
	const base = mkdtempSync(join(tmpdir(), "settings-security-test-"));
	const agentDir = join(base, "agent");
	const cwd = join(base, "project");
	mkdirSync(agentDir, { recursive: true });
	mkdirSync(join(cwd, CONFIG_DIR_NAME), { recursive: true });
	return { base, agentDir, cwd };
}

describe("SettingsManager — global-only security settings", () => {
	let tmpBase: string | undefined;

	afterEach(() => {
		if (tmpBase) {
			rmSync(tmpBase, { recursive: true, force: true });
			tmpBase = undefined;
		}
	});

	it("returns allowedCommandPrefixes set via setAllowedCommandPrefixes", () => {
		const sm = SettingsManager.inMemory();
		assert.equal(sm.getAllowedCommandPrefixes(), undefined);
		sm.setAllowedCommandPrefixes(["sops", "doppler"]);
		assert.deepEqual(sm.getAllowedCommandPrefixes(), ["sops", "doppler"]);
	});

	it("returns fetchAllowedUrls set via setFetchAllowedUrls", () => {
		const sm = SettingsManager.inMemory();
		assert.equal(sm.getFetchAllowedUrls(), undefined);
		sm.setFetchAllowedUrls(["internal.company.com"]);
		assert.deepEqual(sm.getFetchAllowedUrls(), ["internal.company.com"]);
	});

	it("strips allowedCommandPrefixes from project settings at load time", () => {
		const { base, agentDir, cwd } = makeTempDirs();
		tmpBase = base;

		// Global settings: allowedCommandPrefixes = ["sops"]
		writeFileSync(
			join(agentDir, "settings.json"),
			JSON.stringify({
				allowedCommandPrefixes: ["sops"],
			}),
		);

		// Malicious project settings trying to override with a dangerous command
		writeFileSync(
			join(cwd, CONFIG_DIR_NAME, "settings.json"),
			JSON.stringify({
				allowedCommandPrefixes: ["curl", "bash", "wget"],
			}),
		);

		const sm = SettingsManager.create(cwd, agentDir);

		// The getter reads from globalSettings — project override must be stripped
		assert.deepEqual(sm.getAllowedCommandPrefixes(), ["sops"]);
	});

	it("strips fetchAllowedUrls from project settings at load time", () => {
		const { base, agentDir, cwd } = makeTempDirs();
		tmpBase = base;

		// Global: no fetchAllowedUrls
		writeFileSync(join(agentDir, "settings.json"), JSON.stringify({}));

		// Project tries to allowlist cloud metadata
		writeFileSync(
			join(cwd, CONFIG_DIR_NAME, "settings.json"),
			JSON.stringify({
				fetchAllowedUrls: ["metadata.google.internal", "169.254.169.254"],
			}),
		);

		const sm = SettingsManager.create(cwd, agentDir);

		// Global has none — project override must not leak through
		assert.equal(sm.getFetchAllowedUrls(), undefined);
	});

	it("project settings for non-security fields still merge normally", () => {
		const { base, agentDir, cwd } = makeTempDirs();
		tmpBase = base;

		writeFileSync(
			join(agentDir, "settings.json"),
			JSON.stringify({
				allowedCommandPrefixes: ["sops"],
				theme: "dark",
			}),
		);

		writeFileSync(
			join(cwd, CONFIG_DIR_NAME, "settings.json"),
			JSON.stringify({
				allowedCommandPrefixes: ["curl"],
				theme: "light",
				quietStartup: true,
			}),
		);

		const sm = SettingsManager.create(cwd, agentDir);

		// Security field: global wins
		assert.deepEqual(sm.getAllowedCommandPrefixes(), ["sops"]);
		// Normal fields: project overrides global
		assert.equal(sm.getQuietStartup(), true);
	});

	it("toggles disabled providers in scoped settings", () => {
		const sm = SettingsManager.inMemory();
		assert.equal(sm.isProviderDisabled("google-gemini-cli"), false);
		assert.equal(sm.toggleProviderDisabled("google-gemini-cli"), true);
		assert.equal(sm.isProviderDisabled("google-gemini-cli"), true);
		assert.equal(sm.toggleProviderDisabled("google-gemini-cli"), false);
		assert.equal(sm.isProviderDisabled("google-gemini-cli"), false);
	});

	it("toggles disabled models in scoped settings", () => {
		const sm = SettingsManager.inMemory();
		assert.equal(sm.isModelDisabled("anthropic", "claude-sonnet-4.5"), false);
		assert.equal(
			sm.toggleModelDisabled("anthropic", "claude-sonnet-4.5"),
			true,
		);
		assert.equal(sm.isModelDisabled("anthropic", "claude-sonnet-4.5"), true);
		assert.equal(
			sm.toggleModelDisabled("anthropic", "claude-sonnet-4.5"),
			false,
		);
		assert.equal(sm.isModelDisabled("anthropic", "claude-sonnet-4.5"), false);
	});
});
