/**
 * Tests for TTSR rule loader: frontmatter parsing, directory scanning,
 * and project-overrides-global merge logic.
 */

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, test } from "vitest";

import { loadRules } from "../../src/resources/extensions/guardrails/ttsr-rule-loader.js";

// ─── Helpers ─────────────────────────────────────────────────────────────────

function makeTmpProject(): {
	cwd: string;
	globalDir: string;
	projectDir: string;
	cleanup: () => void;
} {
	const cwd = mkdtempSync(join(tmpdir(), "ttsr-loader-test-"));
	const globalDir = join(cwd, ".sf-global", "agent", "rules");
	const projectDir = join(cwd, ".sf", "rules");
	return {
		cwd,
		globalDir,
		projectDir,
		cleanup: () => rmSync(cwd, { recursive: true, force: true }),
	};
}

function writeRule(
	dir: string,
	name: string,
	frontmatter: string,
	body: string,
): void {
	mkdirSync(dir, { recursive: true });
	writeFileSync(join(dir, `${name}.md`), `---\n${frontmatter}\n---\n${body}`);
}

// loadRules uses homedir() for global dir — we can't easily override that,
// so we test the project-local path and the merge logic by testing with
// a cwd that has .sf/rules/.

// ═══════════════════════════════════════════════════════════════════════════
// Project-local rule loading
// ═══════════════════════════════════════════════════════════════════════════

test("loads rule from project .sf/rules/", (_t) => {
	const { cwd, projectDir, cleanup } = makeTmpProject();
	afterEach(() => {
		cleanup();
	});

	writeRule(
		projectDir,
		"no-console",
		'condition:\n  - "console\\.log"',
		"Do not use console.log.",
	);
	const rules = loadRules(cwd);
	const projectRule = rules.find((r) => r.name === "no-console");
	assert.ok(projectRule);
	assert.deepEqual(projectRule.condition, ["console\\.log"]);
	assert.equal(projectRule.content, "Do not use console.log.");
});

test("parses scope and globs from frontmatter", (_t) => {
	const { cwd, projectDir, cleanup } = makeTmpProject();
	afterEach(() => {
		cleanup();
	});

	writeRule(
		projectDir,
		"scoped-rule",
		'condition:\n  - "TODO"\nscope:\n  - "tool:edit"\n  - "text"\nglobs:\n  - "*.ts"',
		"No TODOs allowed.",
	);
	const rules = loadRules(cwd);
	const rule = rules.find((r) => r.name === "scoped-rule");
	assert.ok(rule);
	assert.deepEqual(rule.scope, ["tool:edit", "text"]);
	assert.deepEqual(rule.globs, ["*.ts"]);
});

test("skips files without valid frontmatter", (_t) => {
	const { cwd, projectDir, cleanup } = makeTmpProject();
	afterEach(() => {
		cleanup();
	});

	mkdirSync(projectDir, { recursive: true });
	writeFileSync(join(projectDir, "broken.md"), "No frontmatter here.");
	const rules = loadRules(cwd);
	assert.equal(rules.filter((r) => r.name === "broken").length, 0);
});

test("skips rules with no condition", (_t) => {
	const { cwd, projectDir, cleanup } = makeTmpProject();
	afterEach(() => {
		cleanup();
	});

	writeRule(
		projectDir,
		"no-condition",
		'scope:\n  - "text"',
		"Missing condition field.",
	);
	const rules = loadRules(cwd);
	assert.equal(rules.filter((r) => r.name === "no-condition").length, 0);
});

test("returns empty array when .sf/rules/ does not exist", (_t) => {
	const { cwd, cleanup } = makeTmpProject();
	afterEach(() => {
		cleanup();
	});

	// cwd exists but no .sf/rules/ dir
	const rules = loadRules(cwd);
	// May include global rules from homedir — just verify no crash
	assert.ok(Array.isArray(rules));
});

test("loads multiple rules from same directory", (_t) => {
	const { cwd, projectDir, cleanup } = makeTmpProject();
	afterEach(() => {
		cleanup();
	});

	writeRule(projectDir, "rule-a", 'condition:\n  - "alpha"', "Alpha rule.");
	writeRule(projectDir, "rule-b", 'condition:\n  - "beta"', "Beta rule.");
	const rules = loadRules(cwd);
	const names = rules.map((r) => r.name);
	assert.ok(names.includes("rule-a"));
	assert.ok(names.includes("rule-b"));
});

test("handles quoted values in frontmatter", (_t) => {
	const { cwd, projectDir, cleanup } = makeTmpProject();
	afterEach(() => {
		cleanup();
	});

	writeRule(
		projectDir,
		"quoted",
		"condition:\n  - \"console\\.log\"\n  - 'debugger'",
		"Quoted values.",
	);
	const rules = loadRules(cwd);
	const rule = rules.find((r) => r.name === "quoted");
	assert.ok(rule);
	assert.deepEqual(rule.condition, ["console\\.log", "debugger"]);
});
