// ---------------------------------------------------------------------------
// Ordner: lib
// Datei: storage.test.js
// Relativer Pfad: lib/storage.test.js
// ---------------------------------------------------------------------------
// tests/lib/storage.test.js
import {
	describe,
	it,
	expect,
	beforeAll,
	afterAll,
	beforeEach,
	afterEach,
	vi,
} from "vitest";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";

import {
	listBranches,
	listYears,
	listMonths,
	listDays,
	listFiles,
	__clearStorageCacheForTests,
} from "@/lib/storage";

let tmpRoot;

beforeAll(async () => {
	// Create a unique temporary directory for the tests
	tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "storage-test-"));

	// Point NAS_ROOT_PATH to our temp directory
	process.env.NAS_ROOT_PATH = tmpRoot;

	// Create a fake directory tree:
	// tmpRoot/NL01/2024/10/23/test.pdf
	await fs.mkdir(path.join(tmpRoot, "NL01", "2024", "10", "23"), {
		recursive: true,
	});

	await fs.writeFile(
		path.join(tmpRoot, "NL01", "2024", "10", "23", "test.pdf"),
		"dummy-pdf-content"
	);

	// Add snapshot folder which should be ignored
	await fs.mkdir(path.join(tmpRoot, "@Recently-Snapshot"));
});

afterAll(async () => {
	// Clean up the temporary directory after tests
	await fs.rm(tmpRoot, { recursive: true, force: true });
});

beforeEach(() => {
	// RHL-006:
	// Storage functions now use a TTL cache. We clear it between tests to ensure
	// each test sees filesystem changes deterministically.
	__clearStorageCacheForTests();
});

afterEach(() => {
	// Safety: if a test enables fake timers, always restore real timers afterwards
	// to avoid affecting unrelated tests.
	vi.useRealTimers();
});

describe("storage: listBranches", () => {
	it("returns branch names and filters snapshots", async () => {
		const branches = await listBranches();
		expect(branches).toEqual(["NL01"]);
	});
});

describe("storage: year/month/day helpers", () => {
	it("returns years for a branch", async () => {
		const years = await listYears("NL01");
		expect(years).toEqual(["2024"]);
	});

	it("returns months for a branch/year", async () => {
		const months = await listMonths("NL01", "2024");
		expect(months).toEqual(["10"]);
	});

	it("returns days for a branch/year/month", async () => {
		const days = await listDays("NL01", "2024", "10");
		expect(days).toEqual(["23"]);
	});
});

describe("storage: listFiles", () => {
	it("returns PDF files with relativePath", async () => {
		const files = await listFiles("NL01", "2024", "10", "23");
		expect(files).toEqual([
			{
				name: "test.pdf",
				relativePath: "NL01/2024/10/23/test.pdf",
			},
		]);
	});
});

describe("storage: micro-cache (RHL-006)", () => {
	it("keeps results stable within TTL and refreshes after TTL expires", async () => {
		// Use a dedicated folder so this test doesn't interfere with other fixtures.
		const dayDir = path.join(tmpRoot, "NL01", "2024", "10", "24");
		await fs.mkdir(dayDir, { recursive: true });

		// Initial file
		await fs.writeFile(path.join(dayDir, "a.pdf"), "a");

		// Fake timers allow us to advance time without real waiting.
		vi.useFakeTimers();
		const t0 = new Date("2025-01-01T00:00:00.000Z");
		vi.setSystemTime(t0);

		// First call populates the cache (TTL for files: 15s)
		const first = await listFiles("NL01", "2024", "10", "24");
		expect(first.map((f) => f.name)).toEqual(["a.pdf"]);

		// Add a new file while the cache is still valid.
		await fs.writeFile(path.join(dayDir, "b.pdf"), "b");

		// Second call should still return the cached result (b.pdf not visible yet).
		const second = await listFiles("NL01", "2024", "10", "24");
		expect(second.map((f) => f.name)).toEqual(["a.pdf"]);

		// Advance time beyond the TTL (15s) so the next call re-reads the directory.
		vi.setSystemTime(t0.getTime() + 16_000);

		const third = await listFiles("NL01", "2024", "10", "24");
		expect(third.map((f) => f.name)).toEqual(["a.pdf", "b.pdf"]);
	});
});
