// SPDX-License-Identifier: LGPL-3.0-or-later
import { bindContext, configure, fs, InMemory } from '@zenfs/core';
import assert from 'node:assert/strict';
import { suite, test } from 'node:test';
import { canary } from 'utilium';

fs.mkdirSync('/ctx');
const context = bindContext({ root: '/ctx' });
const ctx = context.fs;

suite('Context', () => {
	test('create a file', () => {
		ctx.writeFileSync('/example.txt', 'not in real root!');
		assert.deepEqual(fs.readdirSync('/'), ['ctx']);
		assert(fs.readdirSync('/ctx').includes('example.txt'));
	});

	test('linking', async () => {
		await ctx.promises.symlink('/example.txt', '/link');
		assert.equal(await ctx.promises.readlink('link', 'utf8'), '/example.txt');
		assert.equal(await fs.promises.readlink('/ctx/link'), '/example.txt');
		assert.deepEqual(await ctx.promises.readFile('/link', 'utf-8'), await fs.promises.readFile('/ctx/example.txt', 'utf-8'));

		// The symlink should only work inside the chroot /ctx
		assert.throws(() => fs.readFileSync('/ctx/link'));
	});

	test('path resolution', async () => {
		// Correct/normal
		assert.equal(ctx.realpathSync('/'), '/');
		assert.equal(ctx.realpathSync('example.txt'), '/example.txt');
		assert.equal(ctx.realpathSync('../link'), '/example.txt');
		assert.equal(await ctx.promises.realpath('/../link'), '/example.txt');

		assert.equal(fs.realpathSync('/ctx/link'), '/example.txt');
	});

	test('break-out fails', () => {
		assert.notDeepEqual(ctx.readdirSync('/../../../'), ['ctx']);
	});

	test('watch should consider context', async () => {
		let lastFile: string | null = null,
			events = 0;
		const watcher = ctx.promises.watch('/', { recursive: true });

		const silence = canary();
		const promise = (async () => {
			for await (const event of watcher) {
				lastFile = event.filename;
				if (++events == 2) return;
			}
		})();
		silence();
		await ctx.promises.writeFile('/xpto.txt', 'in real root');
		assert.equal(lastFile, 'xpto.txt');
		await ctx.promises.unlink('/xpto.txt');
		assert.equal(lastFile, 'xpto.txt');
		await watcher.return!();
		await promise;
	});

	test('Path resolution of / with context root and mount point being the same #226', async () => {
		await configure({
			mounts: { '/bananas': InMemory },
		});

		const bananas = bindContext({ root: '/bananas' });

		fs.writeFileSync('/bananas/yellow', 'true');

		assert.deepEqual(bananas.fs.readdirSync('/'), ['yellow']);
	});

	test('Different working directory #263', () => {
		ctx.mkdirSync('/test');
		context.pwd = '/test';

		assert.equal(ctx.realpathSync('.'), '/test');
	});
});
