import path from "node:path";

import { FileSystem } from "@effect/platform";
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
import { Chunk, Effect, Fiber, Stream } from "effect";
import { describe, expect, test } from "vitest";

import { getWatcher, listenToAdd, waitForReady } from "./chokidar.js";
import { testEffect } from "./test-effect.js";

describe("chokidar wrapper", () => {
	test(
		"getWatcher creates watcher and cleans up on scope close",
		testEffect(
			Effect.gen(function* () {
				const fs = yield* FileSystem.FileSystem;
				const tmpDir = yield* fs.makeTempDirectoryScoped();
				const watcher = yield* getWatcher(tmpDir);
				expect(watcher).toBeDefined();
				return watcher;
			}).pipe(
				Effect.scoped,
				Effect.andThen((watcher) => {
					expect(watcher.closed).toBe(true);
				}),
				Effect.provide(NodeFileSystem.layer),
			),
		),
	);

	test(
		"waitForReady waits for watcher ready event",
		testEffect(
			Effect.gen(function* () {
				const fs = yield* FileSystem.FileSystem;
				const tmpDir = yield* fs.makeTempDirectoryScoped();
				const watcher = yield* getWatcher(tmpDir);
				const readyEvent = yield* waitForReady(watcher);
				expect(readyEvent._tag).toBe("ready");
			}).pipe(Effect.provide(NodeFileSystem.layer)),
		),
	);

	test(
		"listenToAdd streams file add events",
		testEffect(
			Effect.gen(function* () {
				const fs = yield* FileSystem.FileSystem;
				const tmpDir = yield* fs.makeTempDirectoryScoped();

				const watcher = yield* getWatcher(tmpDir);

				const addStream = yield* listenToAdd(watcher);

				const eventsFiber = yield* addStream.pipe(
					Stream.take(1),
					Stream.runCollect,
					Effect.andThen(Chunk.head),
					Effect.fork,
				);

				yield* waitForReady(watcher);

				const testFile = path.join(tmpDir, "test.txt");
				yield* fs.writeFileString(testFile, "content");

				const event = yield* Fiber.join(eventsFiber);

				expect(event._tag).toBe("add");
				expect(event.path).toBe(testFile);
				expect(event.stats).toBeDefined();
			}).pipe(Effect.provide(NodeFileSystem.layer)),
		),
	);
});
